stop_child/1, stop_child/2, config_reloaded/0]).
-export([start_module/2, start_module/3,
stop_module/2, stop_module_keep_config/2,
- get_opt/2, get_opt/3, get_opt_host/3, opt_type/1, is_equal_opt/4,
+ get_opt/2, get_opt/3, get_opt_host/3,
+ get_opt_hosts/3, opt_type/1, is_equal_opt/4,
get_module_opt/3, get_module_opt/4, get_module_opt_host/3,
loaded_modules/1, loaded_modules_with_opts/1,
get_hosts/2, get_module_proc/2, is_loaded/2, is_loaded_elsewhere/2,
Val = get_opt(host, Opts, Default),
ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
+-spec get_opt_hosts(binary(), opts(), binary()) -> [binary()].
+
+get_opt_hosts(Host, Opts, Default) ->
+ Vals = case get_opt(host, Opts, undefined) of
+ undefined ->
+ case get_opt(hosts, Opts, []) of
+ [] -> [Default];
+ L -> L
+ end;
+ Val ->
+ [Val]
+ end,
+ [ejabberd_regexp:greplace(V, <<"@HOST@">>, Host) || V <- Vals].
+
-spec get_validators(binary(), module(), opts()) -> dict:dict() | undef.
get_validators(Host, Module, Opts) ->
try Module:mod_opt_type('') of
-include("xmpp.hrl").
--record(state, {host = <<"">> :: binary()}).
+-record(state, {hosts = [] :: [binary()]}).
%%====================================================================
%% gen_mod API
[].
mod_opt_type(host) -> fun iolist_to_binary/1;
-mod_opt_type(_) -> [host].
+mod_opt_type(hosts) ->
+ fun(L) -> lists:map(fun iolist_to_binary/1, L) end;
+mod_opt_type(_) -> [host, hosts].
%%====================================================================
%% gen_server callbacks
%%--------------------------------------------------------------------
init([Host, Opts]) ->
process_flag(trap_exit, true),
- MyHost = gen_mod:get_opt_host(Host, Opts,
+ Hosts = gen_mod:get_opt_hosts(Host, Opts,
<<"echo.@HOST@">>),
- ejabberd_router:register_route(MyHost, Host),
- {ok, #state{host = MyHost}}.
+ lists:foreach(
+ fun(H) ->
+ ejabberd_router:register_route(H, Host)
+ end, Hosts),
+ {ok, #state{hosts = Hosts}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({reload, Host, NewOpts, OldOpts}, State) ->
- NewMyHost = gen_mod:get_opt_host(Host, NewOpts,
- <<"echo.@HOST@">>),
- OldMyHost = gen_mod:get_opt_host(Host, OldOpts,
- <<"echo.@HOST@">>),
- if NewMyHost /= OldMyHost ->
- ejabberd_router:register_route(NewMyHost, Host),
- ejabberd_router:unregister_route(OldMyHost);
- true ->
- ok
- end,
- {noreply, State#state{host = NewMyHost}};
+ NewMyHosts = gen_mod:get_opt_hosts(Host, NewOpts,
+ <<"echo.@HOST@">>),
+ OldMyHosts = gen_mod:get_opt_hosts(Host, OldOpts,
+ <<"echo.@HOST@">>),
+ lists:foreach(
+ fun(H) ->
+ ejabberd_router:unregister_route(H)
+ end, OldMyHosts -- NewMyHosts),
+ lists:foreach(
+ fun(H) ->
+ ejabberd_router:register_route(H, Host)
+ end, NewMyHosts -- OldMyHosts),
+ {noreply, State#state{hosts = NewMyHosts}};
handle_cast(Msg, State) ->
?WARNING_MSG("unexpected cast: ~p", [Msg]),
{noreply, State}.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, State) ->
- ejabberd_router:unregister_route(State#state.host), ok.
+ lists:foreach(fun ejabberd_router:unregister_route/1, State#state.hosts).
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-record(state,
{server_host :: binary(),
- host :: binary(),
+ hosts :: [binary()],
name :: binary(),
access :: atom(),
max_size :: pos_integer() | infinity,
mod_opt_type(host) ->
fun iolist_to_binary/1;
+mod_opt_type(hosts) ->
+ fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(name) ->
fun iolist_to_binary/1;
mod_opt_type(access) ->
mod_opt_type(thumbnail) ->
fun(B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
- [host, name, access, max_size, secret_length, jid_in_url, file_mode,
+ [host, hosts, name, access, max_size, secret_length, jid_in_url, file_mode,
dir_mode, docroot, put_url, get_url, service_url, custom_headers,
rm_on_unregister, thumbnail].
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
- Host = gen_mod:get_opt_host(ServerHost, Opts, <<"upload.@HOST@">>),
+ Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"upload.@HOST@">>),
Name = gen_mod:get_opt(name, Opts, <<"HTTP File Upload">>),
Access = gen_mod:get_opt(access, Opts, local),
MaxSize = gen_mod:get_opt(max_size, Opts, 104857600),
false ->
ok
end,
- ejabberd_router:register_route(Host, ServerHost),
- {ok, #state{server_host = ServerHost, host = Host, name = Name,
+ lists:foreach(
+ fun(Host) ->
+ ejabberd_router:register_route(Host, ServerHost)
+ end, Hosts),
+ {ok, #state{server_host = ServerHost, hosts = Hosts, name = Name,
access = Access, max_size = MaxSize,
secret_length = SecretLength, jid_in_url = JIDinURL,
file_mode = FileMode, dir_mode = DirMode,
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
-terminate(Reason, #state{server_host = ServerHost, host = Host}) ->
+terminate(Reason, #state{server_host = ServerHost, hosts = Hosts}) ->
?DEBUG("Stopping HTTP upload process for ~s: ~p", [ServerHost, Reason]),
- ejabberd_router:unregister_route(Host),
- ok.
+ lists:foreach(fun ejabberd_router:unregister_route/1, Hosts).
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
[<<"koi8-r">>, <<"iso8859-15">>, <<"iso8859-1">>, <<"iso8859-2">>,
<<"utf-8">>, <<"utf-8+latin-1">>]).
--record(state, {host = <<"">> :: binary(),
+-record(state, {hosts = [] :: [binary()],
server_host = <<"">> :: binary(),
access = all :: atom()}).
init([Host, Opts]) ->
process_flag(trap_exit, true),
ejabberd:start_app(iconv),
- MyHost = gen_mod:get_opt_host(Host, Opts,
- <<"irc.@HOST@">>),
+ MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"irc.@HOST@">>),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
Access = gen_mod:get_opt(access, Opts, all),
[named_table, public,
{keypos, #irc_connection.jid_server_host}]),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
- register_hooks(MyHost, IQDisc),
- ejabberd_router:register_route(MyHost, Host),
+ lists:foreach(
+ fun(MyHost) ->
+ register_hooks(MyHost, IQDisc),
+ ejabberd_router:register_route(MyHost, Host)
+ end, MyHosts),
{ok,
- #state{host = MyHost, server_host = Host,
+ #state{hosts = MyHosts, server_host = Host,
access = Access}}.
%%--------------------------------------------------------------------
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({reload, ServerHost, NewOpts, OldOpts}, State) ->
- NewHost = gen_mod:get_opt_host(ServerHost, NewOpts, <<"irc.@HOST@">>),
- OldHost = gen_mod:get_opt_host(ServerHost, OldOpts, <<"irc.@HOST@">>),
+ NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts, <<"irc.@HOST@">>),
+ OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts, <<"irc.@HOST@">>),
NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)),
OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)),
NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE),
true ->
ok
end,
- if (NewIQDisc /= OldIQDisc) or (NewHost /= OldHost) ->
- register_hooks(NewHost, NewIQDisc);
- true ->
- ok
- end,
- if NewHost /= OldHost ->
- ejabberd_router:register_route(NewHost, ServerHost),
- ejabberd_router:unregister_route(OldHost),
- unregister_hooks(OldHost);
+ if (NewIQDisc /= OldIQDisc) ->
+ lists:foreach(
+ fun(NewHost) ->
+ register_hooks(NewHost, NewIQDisc)
+ end, NewHosts -- (NewHosts -- OldHosts));
true ->
ok
end,
+ lists:foreach(
+ fun(NewHost) ->
+ ejabberd_router:register_route(NewHost, ServerHost),
+ register_hooks(NewHost, NewIQDisc)
+ end, NewHosts -- OldHosts),
+ lists:foreach(
+ fun(OldHost) ->
+ ejabberd_router:unregister_route(OldHost),
+ unregister_hooks(OldHost)
+ end, OldHosts -- NewHosts),
Access = gen_mod:get_opt(access, NewOpts, all),
- {noreply, State#state{host = NewHost, access = Access}};
+ {noreply, State#state{hosts = NewHosts, access = Access}};
handle_cast(Msg, State) ->
?WARNING_MSG("unexpected cast: ~p", [Msg]),
{noreply, State}.
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, Packet},
- #state{host = Host, server_host = ServerHost,
- access = Access} =
+ #state{server_host = ServerHost, access = Access} =
State) ->
+ To = xmpp:get_to(Packet),
+ Host = To#jid.lserver,
case catch do_route(Host, ServerHost, Access, Packet) of
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
_ -> ok
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
-terminate(_Reason, #state{host = MyHost}) ->
- ejabberd_router:unregister_route(MyHost),
- unregister_hooks(MyHost).
+terminate(_Reason, #state{hosts = MyHosts}) ->
+ lists:foreach(
+ fun(MyHost) ->
+ ejabberd_router:unregister_route(MyHost),
+ unregister_hooks(MyHost)
+ end, MyHosts).
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
mod_opt_type(default_encoding) ->
fun iolist_to_binary/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
+mod_opt_type(hosts) ->
+ fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(_) ->
- [access, db_type, default_encoding, host].
+ [access, db_type, default_encoding, host, hosts].
-spec extract_ident(stanza()) -> binary().
extract_ident(Packet) ->
?NS_MIX_NODES_CONFIG]).
-record(state, {server_host :: binary(),
- host :: binary()}).
+ hosts :: [binary()]}).
%%%===================================================================
%%% API
%%%===================================================================
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
- Host = gen_mod:get_opt_host(ServerHost, Opts, <<"mix.@HOST@">>),
- IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
- ConfigTab = gen_mod:get_module_proc(Host, config),
- ets:new(ConfigTab, [named_table]),
- ets:insert(ConfigTab, {plugins, [<<"mix">>]}),
- ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 100),
- ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 100),
- ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 100),
- ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, disco_items, 100),
- ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_features, 100),
- ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
- ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 100),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host,
- ?NS_DISCO_ITEMS, mod_disco,
- process_local_iq_items, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host,
- ?NS_DISCO_INFO, mod_disco,
- process_local_iq_info, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_DISCO_ITEMS, mod_disco,
- process_local_iq_items, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_DISCO_INFO, mod_disco,
- process_local_iq_info, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_PUBSUB, mod_pubsub, iq_sm, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_MIX_0, ?MODULE, process_iq, IQDisc),
- ejabberd_router:register_route(Host, ServerHost),
- {ok, #state{server_host = ServerHost, host = Host}}.
+ Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"mix.@HOST@">>),
+ IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(ServerHost)),
+ lists:foreach(
+ fun(Host) ->
+ ConfigTab = gen_mod:get_module_proc(Host, config),
+ ets:new(ConfigTab, [named_table]),
+ ets:insert(ConfigTab, {plugins, [<<"mix">>]}),
+ ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 100),
+ ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 100),
+ ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 100),
+ ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, disco_items, 100),
+ ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_features, 100),
+ ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
+ ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 100),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host,
+ ?NS_DISCO_ITEMS, mod_disco,
+ process_local_iq_items, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host,
+ ?NS_DISCO_INFO, mod_disco,
+ process_local_iq_info, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
+ ?NS_DISCO_ITEMS, mod_disco,
+ process_local_iq_items, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
+ ?NS_DISCO_INFO, mod_disco,
+ process_local_iq_info, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
+ ?NS_PUBSUB, mod_pubsub, iq_sm, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
+ ?NS_MIX_0, ?MODULE, process_iq, IQDisc),
+ ejabberd_router:register_route(Host, ServerHost)
+ end, Hosts),
+ {ok, #state{server_host = ServerHost, hosts = Hosts}}.
handle_call(_Request, _From, State) ->
Reply = ok,
handle_info(_Info, State) ->
{noreply, State}.
-terminate(_Reason, #state{host = Host}) ->
- ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 100),
- ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 100),
- ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 100),
- ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, disco_items, 100),
- ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_features, 100),
- ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
- ejabberd_hooks:delete(disco_info, Host, ?MODULE, disco_info, 100),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PUBSUB),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_0),
- ejabberd_router:unregister_route(Host),
- ok.
+terminate(_Reason, #state{hosts = Hosts}) ->
+ lists:foreach(
+ fun(Host) ->
+ ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 100),
+ ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 100),
+ ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 100),
+ ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, disco_items, 100),
+ ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_features, 100),
+ ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
+ ejabberd_hooks:delete(disco_info, Host, ?MODULE, disco_info, 100),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PUBSUB),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_0),
+ ejabberd_router:unregister_route(Host)
+ end, Hosts).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
-mod_opt_type(_) -> [host, iqdisc].
+mod_opt_type(hosts) ->
+ fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
+mod_opt_type(_) -> [host, hosts, iqdisc].
-include("mod_muc.hrl").
-record(state,
- {host = <<"">> :: binary(),
+ {hosts = [] :: [binary()],
server_host = <<"">> :: binary(),
access = {none, none, none, none} :: {atom(), atom(), atom(), atom()},
history_size = 20 :: non_neg_integer(),
%% 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, ?MODULE),
- gen_server:call(Proc, {create, Name, From, Nick, Opts}).
+ ServerHost = ejabberd_router:host_of_route(Host),
+ Proc = gen_mod:get_module_proc(ServerHost, ?MODULE),
+ gen_server:call(Proc, {create, Name, Host, From, Nick, Opts}).
store_room(ServerHost, Host, Name, Opts) ->
LServer = jid:nameprep(ServerHost),
init([Host, Opts]) ->
process_flag(trap_exit, true),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
- #state{access = Access, host = MyHost,
+ #state{access = Access, hosts = MyHosts,
history_size = HistorySize, queue_type = QueueType,
room_shaper = RoomShaper} = State = init_state(Host, Opts),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
RMod = gen_mod:ram_db_mod(Host, Opts, ?MODULE),
- Mod:init(Host, [{host, MyHost}|Opts]),
- RMod:init(Host, [{host, MyHost}|Opts]),
- register_iq_handlers(MyHost, IQDisc),
- ejabberd_router:register_route(MyHost, Host),
- load_permanent_rooms(MyHost, Host, Access, HistorySize, RoomShaper, QueueType),
+ Mod:init(Host, [{hosts, MyHosts}|Opts]),
+ RMod:init(Host, [{hosts, MyHosts}|Opts]),
+ lists:foreach(
+ fun(MyHost) ->
+ register_iq_handlers(MyHost, IQDisc),
+ ejabberd_router:register_route(MyHost, Host),
+ load_permanent_rooms(MyHost, Host, Access, HistorySize,
+ RoomShaper, QueueType)
+ end, MyHosts),
{ok, State}.
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
-handle_call({create, Room, From, Nick, Opts}, _From,
- #state{host = Host, server_host = ServerHost,
+handle_call({create, Room, Host, From, Nick, Opts}, _From,
+ #state{server_host = ServerHost,
access = Access, default_room_opts = DefOpts,
history_size = HistorySize, queue_type = QueueType,
room_shaper = RoomShaper} = State) ->
ejabberd_hooks:run(create_room, ServerHost, [ServerHost, Room, Host]),
{reply, ok, State}.
-handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{host = OldHost}) ->
+handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{hosts = OldHosts}) ->
NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)),
OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)),
NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE),
NewRMod = gen_mod:ram_db_mod(ServerHost, NewOpts, ?MODULE),
OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE),
OldRMod = gen_mod:ram_db_mod(ServerHost, OldOpts, ?MODULE),
- #state{host = NewHost} = NewState = init_state(ServerHost, NewOpts),
+ #state{hosts = NewHosts} = NewState = init_state(ServerHost, NewOpts),
if NewMod /= OldMod ->
- NewMod:init(ServerHost, [{host, NewHost}|NewOpts]);
+ NewMod:init(ServerHost, [{hosts, NewHosts}|NewOpts]);
true ->
ok
end,
if NewRMod /= OldRMod ->
- NewRMod:init(ServerHost, [{host, NewHost}|NewOpts]);
- true ->
- ok
- end,
- if (NewIQDisc /= OldIQDisc) or (NewHost /= OldHost) ->
- register_iq_handlers(NewHost, NewIQDisc);
+ NewRMod:init(ServerHost, [{hosts, NewHosts}|NewOpts]);
true ->
ok
end,
- if NewHost /= OldHost ->
- ejabberd_router:register_route(NewHost, ServerHost),
- ejabberd_router:unregister_route(OldHost),
- unregister_iq_handlers(OldHost);
+ if (NewIQDisc /= OldIQDisc) ->
+ lists:foreach(
+ fun(NewHost) ->
+ register_iq_handlers(NewHost, NewIQDisc)
+ end, NewHosts -- (NewHosts -- OldHosts));
true ->
ok
end,
+ lists:foreach(
+ fun(NewHost) ->
+ ejabberd_router:register_route(NewHost, ServerHost),
+ register_iq_handlers(NewHost, NewIQDisc)
+ end, NewHosts -- OldHosts),
+ lists:foreach(
+ fun(OldHost) ->
+ ejabberd_router:unregister_route(OldHost),
+ unregister_iq_handlers(OldHost)
+ end, OldHosts -- NewHosts),
{noreply, NewState};
handle_cast(Msg, State) ->
?WARNING_MSG("unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({route, Packet},
- #state{host = Host, server_host = ServerHost,
+ #state{server_host = ServerHost,
access = Access, default_room_opts = DefRoomOpts,
history_size = HistorySize, queue_type = QueueType,
max_rooms_discoitems = MaxRoomsDiscoItems,
room_shaper = RoomShaper} = State) ->
From = xmpp:get_from(Packet),
To = xmpp:get_to(Packet),
+ Host = To#jid.lserver,
case catch do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
From, To, Packet, DefRoomOpts, MaxRoomsDiscoItems,
QueueType) of
?ERROR_MSG("unexpected info: ~p", [Info]),
{noreply, State}.
-terminate(_Reason, #state{host = MyHost}) ->
- ejabberd_router:unregister_route(MyHost),
- unregister_iq_handlers(MyHost).
+terminate(_Reason, #state{hosts = MyHosts}) ->
+ lists:foreach(
+ fun(MyHost) ->
+ ejabberd_router:unregister_route(MyHost),
+ unregister_iq_handlers(MyHost)
+ end, MyHosts).
code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%% Internal functions
%%--------------------------------------------------------------------
init_state(Host, Opts) ->
- MyHost = gen_mod:get_opt_host(Host, Opts,
- <<"conference.@HOST@">>),
+ MyHosts = gen_mod:get_opt_hosts(Host, Opts,
+ <<"conference.@HOST@">>),
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),
QueueType = gen_mod:get_opt(queue_type, Opts,
ejabberd_config:default_queue_type(Host)),
RoomShaper = gen_mod:get_opt(room_shaper, Opts, none),
- #state{host = MyHost,
+ #state{hosts = MyHosts,
server_host = Host,
access = {Access, AccessCreate, AccessAdmin, AccessPersistent},
default_room_opts = DefRoomOpts,
mod_opt_type(history_size) ->
fun (I) when is_integer(I), I >= 0 -> I end;
mod_opt_type(host) -> fun iolist_to_binary/1;
+mod_opt_type(hosts) ->
+ fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(max_room_desc) ->
fun (infinity) -> infinity;
(I) when is_integer(I), I > 0 -> I
end;
mod_opt_type(_) ->
[access, access_admin, access_create, access_persistent,
- db_type, ram_db_type, history_size, host,
+ db_type, ram_db_type, history_size, host, hosts,
max_room_desc, max_room_id, max_room_name,
max_rooms_discoitems, max_user_conferences, max_users,
max_users_admin_threshold, max_users_presence,
%%% gen_server callbacks
%%%===================================================================
init([Host, Opts]) ->
- MyHost = proplists:get_value(host, Opts),
+ MyHosts = proplists:get_value(hosts, Opts),
case gen_mod:db_mod(Host, Opts, mod_muc) of
?MODULE ->
ejabberd_mnesia:create(?MODULE, muc_room,
{type, ordered_set},
{attributes, record_info(fields, muc_online_room)}]),
catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
- clean_table_from_bad_node(node(), MyHost),
+ lists:foreach(
+ fun(MyHost) ->
+ clean_table_from_bad_node(node(), MyHost)
+ end, MyHosts),
mnesia:subscribe(system);
_ ->
ok
mod_opt_type(access) -> fun acl:access_rules_validator/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
+mod_opt_type(hosts) ->
+ fun(L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(hostname) -> fun iolist_to_binary/1;
mod_opt_type(ip) ->
fun (S) ->
mod_opt_type(Opt) ->
case mod_proxy65_stream:listen_opt_type(Opt) of
Opts when is_list(Opts) ->
- [access, host, hostname, ip, name, port,
+ [access, host, hosts, hostname, ip, name, port,
max_connections, ram_db_type] ++ Opts;
Fun ->
Fun
-define(PROCNAME, ejabberd_mod_proxy65_service).
--record(state, {myhost = <<"">> :: binary()}).
+-record(state, {myhosts = [] :: [binary()]}).
%%%------------------------
%%% gen_server callbacks
init([Host, Opts]) ->
process_flag(trap_exit, true),
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
- MyHost = gen_mod:get_opt_host(Host, Opts, <<"proxy.@HOST@">>),
- gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO,
- ?MODULE, process_disco_info, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS,
- ?MODULE, process_disco_items, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_VCARD,
- ?MODULE, process_vcard, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_BYTESTREAMS,
- ?MODULE, process_bytestreams, IQDisc),
- ejabberd_router:register_route(MyHost, Host),
- {ok, #state{myhost = MyHost}}.
+ MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"proxy.@HOST@">>),
+ lists:foreach(
+ fun(MyHost) ->
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO,
+ ?MODULE, process_disco_info, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS,
+ ?MODULE, process_disco_items, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_VCARD,
+ ?MODULE, process_vcard, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_BYTESTREAMS,
+ ?MODULE, process_bytestreams, IQDisc),
+ ejabberd_router:register_route(MyHost, Host)
+ end, MyHosts),
+ {ok, #state{myhosts = MyHosts}}.
-terminate(_Reason, #state{myhost = MyHost}) ->
- ejabberd_router:unregister_route(MyHost),
- gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO),
- gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS),
- gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD),
- gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_BYTESTREAMS).
+terminate(_Reason, #state{myhosts = MyHosts}) ->
+ lists:foreach(
+ fun(MyHost) ->
+ ejabberd_router:unregister_route(MyHost),
+ unregister_handlers(MyHost)
+ end, MyHosts).
handle_info({route, #iq{} = Packet}, State) ->
ejabberd_router:process_iq(Packet),
{reply, ok, State}.
handle_cast({reload, ServerHost, NewOpts, OldOpts}, State) ->
- NewHost = gen_mod:get_opt_host(ServerHost, NewOpts, <<"proxy.@HOST@">>),
- OldHost = gen_mod:get_opt_host(ServerHost, OldOpts, <<"proxy.@HOST@">>),
+ NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts, <<"proxy.@HOST@">>),
+ OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts, <<"proxy.@HOST@">>),
NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)),
OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)),
- if (NewIQDisc /= OldIQDisc) or (NewHost /= OldHost) ->
- gen_iq_handler:add_iq_handler(ejabberd_local, NewHost, ?NS_DISCO_INFO,
- ?MODULE, process_disco_info, NewIQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, NewHost, ?NS_DISCO_ITEMS,
- ?MODULE, process_disco_items, NewIQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, NewHost, ?NS_VCARD,
- ?MODULE, process_vcard, NewIQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, NewHost, ?NS_BYTESTREAMS,
- ?MODULE, process_bytestreams, NewIQDisc);
- true ->
- ok
- end,
- if NewHost /= OldHost ->
- ejabberd_router:register_route(NewHost, ServerHost),
- ejabberd_router:unregister_route(OldHost),
- gen_iq_handler:remove_iq_handler(ejabberd_local, OldHost, ?NS_DISCO_INFO),
- gen_iq_handler:remove_iq_handler(ejabberd_local, OldHost, ?NS_DISCO_ITEMS),
- gen_iq_handler:remove_iq_handler(ejabberd_local, OldHost, ?NS_VCARD),
- gen_iq_handler:remove_iq_handler(ejabberd_local, OldHost, ?NS_BYTESTREAMS);
+ if (NewIQDisc /= OldIQDisc) ->
+ lists:foreach(
+ fun(NewHost) ->
+ register_handlers(NewHost, NewIQDisc)
+ end, NewHosts -- (NewHosts -- OldHosts));
true ->
ok
end,
- {noreply, State#state{myhost = NewHost}};
+ lists:foreach(
+ fun(NewHost) ->
+ ejabberd_router:register_route(NewHost, ServerHost),
+ register_handlers(NewHost, NewIQDisc)
+ end, NewHosts -- OldHosts),
+ lists:foreach(
+ fun(OldHost) ->
+ ejabberd_router:unregister_route(OldHost),
+ unregister_handlers(OldHost)
+ end, OldHosts -- NewHosts),
+ {noreply, State#state{myhosts = NewHosts}};
handle_cast(Msg, State) ->
?WARNING_MSG("unexpected cast: ~p", [Msg]),
{noreply, State}.
max_connections(ServerHost) ->
gen_mod:get_module_opt(ServerHost, mod_proxy65, max_connections, infinity).
+
+register_handlers(Host, IQDisc) ->
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO,
+ ?MODULE, process_disco_info, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS,
+ ?MODULE, process_disco_items, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
+ ?MODULE, process_vcard, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_BYTESTREAMS,
+ ?MODULE, process_bytestreams, IQDisc).
+
+unregister_handlers(Host) ->
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_BYTESTREAMS).
-record(state,
{
server_host,
- host,
+ hosts,
access,
pep_mapping = [],
ignore_pep_from_offline = true,
-type(state() ::
#state{
server_host :: binary(),
- host :: mod_pubsub:hostPubsub(),
+ hosts :: [mod_pubsub:hostPubsub()],
access :: atom(),
pep_mapping :: [{binary(), binary()}],
ignore_pep_from_offline :: boolean(),
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
?DEBUG("pubsub init ~p ~p", [ServerHost, Opts]),
- Host = gen_mod:get_opt_host(ServerHost, Opts, <<"pubsub.@HOST@">>),
- ejabberd_router:register_route(Host, ServerHost),
+ Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"pubsub.@HOST@">>),
Access = gen_mod:get_opt(access_createnode, Opts, all),
PepOffline = gen_mod:get_opt(ignore_pep_from_offline, Opts, true),
- IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
+ IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(ServerHost)),
LastItemCache = gen_mod:get_opt(last_item_cache, Opts, false),
MaxItemsNode = gen_mod:get_opt(max_items_node, Opts, ?MAXITEMS),
MaxSubsNode = gen_mod:get_opt(max_subscriptions_node, Opts),
- case gen_mod:db_type(ServerHost, ?MODULE) of
- mnesia -> pubsub_index:init(Host, ServerHost, Opts);
- _ -> ok
- end,
- {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
- DefaultModule = plugin(Host, hd(Plugins)),
- BaseOptions = DefaultModule:options(),
- DefaultNodeCfg = filter_node_options(
- gen_mod:get_opt(default_node_config, Opts, []),
- BaseOptions),
ejabberd_mnesia:create(?MODULE, pubsub_last_item,
- [{ram_copies, [node()]},
- {attributes, record_info(fields, pubsub_last_item)}]),
- lists:foreach(
- fun(H) ->
- T = gen_mod:get_module_proc(H, config),
- ets:new(T, [set, named_table]),
- ets:insert(T, {nodetree, NodeTree}),
- ets:insert(T, {plugins, Plugins}),
- ets:insert(T, {last_item_cache, LastItemCache}),
- ets:insert(T, {max_items_node, MaxItemsNode}),
- ets:insert(T, {max_subscriptions_node, MaxSubsNode}),
- ets:insert(T, {default_node_config, DefaultNodeCfg}),
- ets:insert(T, {pep_mapping, PepMapping}),
- ets:insert(T, {ignore_pep_from_offline, PepOffline}),
- ets:insert(T, {host, Host}),
- ets:insert(T, {access, Access})
- end, [Host, ServerHost]),
+ [{ram_copies, [node()]},
+ {attributes, record_info(fields, pubsub_last_item)}]),
+ AllPlugins =
+ lists:flatmap(
+ fun(Host) ->
+ ejabberd_router:register_route(Host, ServerHost),
+ case gen_mod:db_type(ServerHost, ?MODULE) of
+ mnesia -> pubsub_index:init(Host, ServerHost, Opts);
+ _ -> ok
+ end,
+ {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
+ DefaultModule = plugin(Host, hd(Plugins)),
+ BaseOptions = DefaultModule:options(),
+ DefaultNodeCfg = filter_node_options(
+ gen_mod:get_opt(default_node_config, Opts, []),
+ BaseOptions),
+ lists:foreach(
+ fun(H) ->
+ T = gen_mod:get_module_proc(H, config),
+ try
+ ets:new(T, [set, named_table]),
+ ets:insert(T, {nodetree, NodeTree}),
+ ets:insert(T, {plugins, Plugins}),
+ ets:insert(T, {last_item_cache, LastItemCache}),
+ ets:insert(T, {max_items_node, MaxItemsNode}),
+ ets:insert(T, {max_subscriptions_node, MaxSubsNode}),
+ ets:insert(T, {default_node_config, DefaultNodeCfg}),
+ ets:insert(T, {pep_mapping, PepMapping}),
+ ets:insert(T, {ignore_pep_from_offline, PepOffline}),
+ ets:insert(T, {host, Host}),
+ ets:insert(T, {access, Access})
+ catch error:badarg when H == ServerHost ->
+ ok
+ end
+ end, [Host, ServerHost]),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO,
+ ?MODULE, process_disco_info, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS,
+ ?MODULE, process_disco_items, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PUBSUB,
+ ?MODULE, process_pubsub, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PUBSUB_OWNER,
+ ?MODULE, process_pubsub_owner, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
+ ?MODULE, process_vcard, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
+ ?MODULE, process_commands, IQDisc),
+ Plugins
+ end, Hosts),
ejabberd_hooks:add(sm_remove_connection_hook, ServerHost,
?MODULE, on_user_offline, 75),
ejabberd_hooks:add(disco_local_identity, ServerHost,
?MODULE, remove_user, 50),
ejabberd_hooks:add(c2s_handle_info, ServerHost,
?MODULE, c2s_handle_info, 50),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO,
- ?MODULE, process_disco_info, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS,
- ?MODULE, process_disco_items, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PUBSUB,
- ?MODULE, process_pubsub, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PUBSUB_OWNER,
- ?MODULE, process_pubsub_owner, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
- ?MODULE, process_vcard, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
- ?MODULE, process_commands, IQDisc),
- case lists:member(?PEPNODE, Plugins) of
+ case lists:member(?PEPNODE, AllPlugins) of
true ->
ejabberd_hooks:add(caps_add, ServerHost,
?MODULE, caps_add, 80),
false ->
ok
end,
- {_, State} = init_send_loop(ServerHost),
+ {_, State} = init_send_loop(ServerHost, Hosts),
{ok, State}.
-init_send_loop(ServerHost) ->
+init_send_loop(ServerHost, Hosts) ->
NodeTree = config(ServerHost, nodetree),
Plugins = config(ServerHost, plugins),
LastItemCache = config(ServerHost, last_item_cache),
MaxItemsNode = config(ServerHost, max_items_node),
PepMapping = config(ServerHost, pep_mapping),
PepOffline = config(ServerHost, ignore_pep_from_offline),
- Host = config(ServerHost, host),
Access = config(ServerHost, access),
DBType = gen_mod:db_type(ServerHost, ?MODULE),
- State = #state{host = Host, server_host = ServerHost,
+ State = #state{hosts = Hosts, server_host = ServerHost,
access = Access, pep_mapping = PepMapping,
ignore_pep_from_offline = PepOffline,
last_item_cache = LastItemCache,
send_loop(State) ->
receive
{presence, JID, _Pid} ->
- Host = State#state.host,
+ Host = State#state.server_host,
ServerHost = State#state.server_host,
DBType = State#state.db_type,
LJID = jid:tolower(JID),
send_loop(State);
{presence, User, Server, Resources, JID} ->
spawn(fun() ->
- Host = State#state.host,
+ Host = State#state.server_host,
Owner = jid:remove_resource(jid:tolower(JID)),
lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, type = Type, id = Nidx, options = Options}) ->
case match_option(Options, send_last_published_item, on_sub_and_presence) of
ok.
presence(ServerHost, Presence) ->
- {SendLoop, _} = case whereis(gen_mod:get_module_proc(ServerHost, ?LOOPNAME)) of
- undefined -> init_send_loop(ServerHost);
- Pid -> {Pid, undefined}
- end,
- SendLoop ! Presence,
+ gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! Presence,
ok.
%% -------
%%--------------------------------------------------------------------
%% @private
terminate(_Reason,
- #state{host = Host, server_host = ServerHost, nodetree = TreePlugin, plugins = Plugins}) ->
+ #state{hosts = Hosts, server_host = ServerHost, nodetree = TreePlugin, plugins = Plugins}) ->
case lists:member(?PEPNODE, Plugins) of
true ->
ejabberd_hooks:delete(caps_add, ServerHost,
?MODULE, remove_user, 50),
ejabberd_hooks:delete(c2s_handle_info, ServerHost,
?MODULE, c2s_handle_info, 50),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PUBSUB),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PUBSUB_OWNER),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS),
case whereis(gen_mod:get_module_proc(ServerHost, ?LOOPNAME)) of
undefined ->
?ERROR_MSG("~s process is dead, pubsub was broken", [?LOOPNAME]);
Pid ->
Pid ! stop
end,
- terminate_plugins(Host, ServerHost, Plugins, TreePlugin),
- ejabberd_router:unregister_route(Host).
+ lists:foreach(
+ fun(Host) ->
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PUBSUB),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PUBSUB_OWNER),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS),
+ terminate_plugins(Host, ServerHost, Plugins, TreePlugin),
+ ejabberd_router:unregister_route(Host)
+ end, Hosts).
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
mod_opt_type(access_createnode) -> fun acl:access_rules_validator/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(host) -> fun iolist_to_binary/1;
+mod_opt_type(hosts) ->
+ fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(ignore_pep_from_offline) ->
fun (A) when is_boolean(A) -> A end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(plugins) ->
fun (A) when is_list(A) -> A end;
mod_opt_type(_) ->
- [access_createnode, db_type, host,
+ [access_createnode, db_type, host, hosts,
ignore_pep_from_offline, iqdisc, last_item_cache,
max_items_node, nodetree, pep_mapping, plugins,
max_subscriptions_node, default_node_config].
-optional_callbacks([use_cache/1, cache_nodes/1]).
--record(state, {host :: binary(), server_host :: binary()}).
+-record(state, {hosts :: [binary()], server_host :: binary()}).
%%====================================================================
%% gen_mod callbacks
?NS_VCARD, ?MODULE, process_sm_iq, IQDisc),
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE,
get_sm_features, 50),
- MyHost = gen_mod:get_opt_host(Host, Opts, <<"vjud.@HOST@">>),
+ MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"vjud.@HOST@">>),
Search = gen_mod:get_opt(search, Opts, false),
if Search ->
- ejabberd_hooks:add(
- disco_local_items, MyHost, ?MODULE, disco_items, 100),
- ejabberd_hooks:add(
- disco_local_features, MyHost, ?MODULE, disco_features, 100),
- ejabberd_hooks:add(
- disco_local_identity, MyHost, ?MODULE, disco_identity, 100),
- gen_iq_handler:add_iq_handler(
- ejabberd_local, MyHost, ?NS_SEARCH, ?MODULE, process_search, IQDisc),
- gen_iq_handler:add_iq_handler(
- ejabberd_local, MyHost, ?NS_VCARD, ?MODULE, process_vcard, IQDisc),
- gen_iq_handler:add_iq_handler(
- ejabberd_local, MyHost, ?NS_DISCO_ITEMS, mod_disco,
- process_local_iq_items, IQDisc),
- gen_iq_handler:add_iq_handler(
- ejabberd_local, MyHost, ?NS_DISCO_INFO, mod_disco,
- process_local_iq_info, IQDisc),
- case Mod:is_search_supported(Host) of
- false ->
- ?WARNING_MSG("vcard search functionality is "
- "not implemented for ~s backend",
- [gen_mod:db_type(Host, Opts, ?MODULE)]);
- true ->
- ejabberd_router:register_route(MyHost, Host)
- end;
+ lists:foreach(
+ fun(MyHost) ->
+ ejabberd_hooks:add(
+ disco_local_items, MyHost, ?MODULE, disco_items, 100),
+ ejabberd_hooks:add(
+ disco_local_features, MyHost, ?MODULE, disco_features, 100),
+ ejabberd_hooks:add(
+ disco_local_identity, MyHost, ?MODULE, disco_identity, 100),
+ gen_iq_handler:add_iq_handler(
+ ejabberd_local, MyHost, ?NS_SEARCH, ?MODULE, process_search, IQDisc),
+ gen_iq_handler:add_iq_handler(
+ ejabberd_local, MyHost, ?NS_VCARD, ?MODULE, process_vcard, IQDisc),
+ gen_iq_handler:add_iq_handler(
+ ejabberd_local, MyHost, ?NS_DISCO_ITEMS, mod_disco,
+ process_local_iq_items, IQDisc),
+ gen_iq_handler:add_iq_handler(
+ ejabberd_local, MyHost, ?NS_DISCO_INFO, mod_disco,
+ process_local_iq_info, IQDisc),
+ case Mod:is_search_supported(Host) of
+ false ->
+ ?WARNING_MSG("vcard search functionality is "
+ "not implemented for ~s backend",
+ [gen_mod:db_type(Host, Opts, ?MODULE)]);
+ true ->
+ ejabberd_router:register_route(MyHost, Host)
+ end
+ end, MyHosts);
true ->
ok
end,
- {ok, #state{host = MyHost, server_host = Host}}.
+ {ok, #state{hosts = MyHosts, server_host = Host}}.
handle_call(_Call, _From, State) ->
{noreply, State}.
?WARNING_MSG("unexpected info: ~p", [Info]),
{noreply, State}.
-terminate(_Reason, #state{host = MyHost, server_host = Host}) ->
+terminate(_Reason, #state{hosts = MyHosts, server_host = Host}) ->
ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
Mod = gen_mod:db_mod(Host, ?MODULE),
Mod:stop(Host),
- ejabberd_router:unregister_route(MyHost),
- ejabberd_hooks:delete(disco_local_items, MyHost, ?MODULE, disco_items, 100),
- ejabberd_hooks:delete(disco_local_features, MyHost, ?MODULE, disco_features, 100),
- ejabberd_hooks:delete(disco_local_identity, MyHost, ?MODULE, disco_identity, 100),
- gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_SEARCH),
- gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD),
- gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS),
- gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO).
+ lists:foreach(
+ fun(MyHost) ->
+ ejabberd_router:unregister_route(MyHost),
+ ejabberd_hooks:delete(disco_local_items, MyHost, ?MODULE, disco_items, 100),
+ ejabberd_hooks:delete(disco_local_features, MyHost, ?MODULE, disco_features, 100),
+ ejabberd_hooks:delete(disco_local_identity, MyHost, ?MODULE, disco_identity, 100),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_SEARCH),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO)
+ end, MyHosts).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
fun (B) when is_boolean(B) -> B end;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(host) -> fun iolist_to_binary/1;
+mod_opt_type(hosts) ->
+ fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(matches) ->
fun (infinity) -> infinity;
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
- [allow_return_all, db_type, host, iqdisc, matches,
+ [allow_return_all, db_type, host, hosts, iqdisc, matches,
search, search_all_hosts, cache_life_time, cache_size,
use_cache, cache_missed].
-record(state,
{serverhost = <<"">> :: binary(),
- myhost = <<"">> :: binary(),
+ myhosts = [] :: [binary()],
eldap_id = <<"">> :: binary(),
search = false :: boolean(),
servers = [] :: [binary()],
{<<"Organization Unit">>, <<"ORGUNIT">>}].
parse_options(Host, Opts) ->
- MyHost = gen_mod:get_opt_host(Host, Opts,
- <<"vjud.@HOST@">>),
+ MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"vjud.@HOST@">>),
Search = gen_mod:get_opt(search, Opts, false),
Matches = gen_mod:get_opt(matches, Opts, 30),
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?PROCNAME)),
end,
SearchReported)
++ UIDAttrs),
- #state{serverhost = Host, myhost = MyHost,
+ #state{serverhost = Host, myhosts = MyHosts,
eldap_id = Eldap_ID, search = Search,
servers = Cfg#eldap_config.servers,
backups = Cfg#eldap_config.backups,