diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 89cd67cad..ff0f7b3e8 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -138,7 +138,7 @@ get_ejabberd_config_path() -> -spec get_env_config() -> {ok, string()} | undefined. get_env_config() -> %% First case: the filename can be specified with: erl -config "/path/to/ejabberd.yml". - case application:get_env(config) of + case application:get_env(ejabberd, config) of R = {ok, _Path} -> R; undefined -> %% Second case for embbeding ejabberd in another app, for example for Elixir: @@ -194,7 +194,8 @@ load_file(File) -> reload_file() -> Config = get_ejabberd_config_path(), - load_file(Config). + load_file(Config), + ejabberd_hooks:run(config_reloaded, []). -spec convert_to_yaml(file:filename()) -> ok | {error, any()}. diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl index 62874c4f4..ebf056ebb 100644 --- a/src/gen_iq_handler.erl +++ b/src/gen_iq_handler.erl @@ -50,7 +50,7 @@ -type component() :: ejabberd_sm | ejabberd_local. -type type() :: no_queue | one_queue | pos_integer() | parallel. -type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel. --export_type([opts/0]). +-export_type([opts/0, type/0]). %%==================================================================== %% API diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 68a5aeb5b..b74d1555d 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -31,10 +31,10 @@ -author('alexey@process-one.net'). -export([init/1, start_link/0, start_child/3, start_child/4, - stop_child/1, stop_child/2]). + stop_child/1, stop_child/2, config_reloaded/0]). -export([start/0, start_module/2, start_module/3, stop_module/2, stop_module_keep_config/2, get_opt/3, - get_opt/4, get_opt_host/3, opt_type/1, + get_opt/4, get_opt_host/3, opt_type/1, is_equal_opt/5, get_module_opt/4, get_module_opt/5, get_module_opt_host/3, loaded_modules/1, loaded_modules_with_opts/1, get_hosts/2, get_module_proc/2, is_loaded/2, @@ -46,6 +46,7 @@ -include("ejabberd.hrl"). -include("logger.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). -record(ejabberd_module, {module_host = {undefined, <<"">>} :: {atom(), binary()}, @@ -56,9 +57,12 @@ -callback start(binary(), opts()) -> ok | {ok, pid()}. -callback stop(binary()) -> any(). +-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}. -callback mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()]. -callback depends(binary(), opts()) -> [{module(), hard | soft}]. +-optional_callbacks([reload/3]). + -export_type([opts/0]). -export_type([db_type/0]). @@ -75,6 +79,7 @@ start_link() -> supervisor:start_link({local, ejabberd_gen_mod_sup}, ?MODULE, []). init([]) -> + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), ets:new(ejabberd_modules, [named_table, public, {keypos, #ejabberd_module.module_host}]), @@ -182,9 +187,7 @@ start_module(Host, Module) -> start_module(Host, Module, Opts0) -> ?DEBUG("loading ~s at ~s", [Module, Host]), Opts = validate_opts(Module, Opts0), - ets:insert(ejabberd_modules, - #ejabberd_module{module_host = {Module, Host}, - opts = Opts}), + store_options(Host, Module, Opts), try case Module:start(Host, Opts) of ok -> ok; {ok, Pid} when is_pid(Pid) -> {ok, Pid}; @@ -202,6 +205,77 @@ start_module(Host, Module, Opts0) -> erlang:raise(Class, Reason, erlang:get_stacktrace()) end. +-spec reload_modules(binary()) -> ok. +reload_modules(Host) -> + NewMods = ejabberd_config:get_option( + {modules, Host}, opt_type(modules), []), + OldMods = ets:select( + ejabberd_modules, + ets:fun2ms( + fun(#ejabberd_module{module_host = {M, H}, opts = O}) + when H == Host -> {M, O} + end)), + lists:foreach( + fun({Mod, _Opts}) -> + case lists:keymember(Mod, 1, NewMods) of + false -> + stop_module(Host, Mod); + true -> + ok + end + end, OldMods), + lists:foreach( + fun({Mod, Opts}) -> + case lists:keymember(Mod, 1, OldMods) of + false -> + start_module(Host, Mod, Opts); + true -> + ok + end + end, NewMods), + lists:foreach( + fun({Mod, OldOpts}) -> + case lists:keyfind(Mod, 1, NewMods) of + {_, NewOpts} when NewOpts /= OldOpts -> + reload_module(Host, Mod, NewOpts, OldOpts); + _ -> + ok + end + end, OldMods). + +-spec reload_module(binary(), module(), opts(), opts()) -> ok | {ok, pid()}. +reload_module(Host, Module, NewOpts0, OldOpts) -> + case erlang:function_exported(Module, reload, 3) of + true -> + ?DEBUG("reloading ~s at ~s", [Module, Host]), + NewOpts = validate_opts(Module, NewOpts0), + store_options(Host, Module, NewOpts), + try case Module:reload(Host, NewOpts, OldOpts) of + ok -> ok; + {ok, Pid} when is_pid(Pid) -> {ok, Pid}; + Err -> erlang:error(Err) + end + catch Class:Reason -> + StackTrace = erlang:get_stacktrace(), + ?CRITICAL_MSG("failed to reload module ~s at ~s:~n" + "** Reason = ~p", + [Module, Host, + {Class, {Reason, StackTrace}}]), + erlang:raise(Class, Reason, StackTrace) + end; + false -> + ?WARNING_MSG("module ~s doesn't support reloading " + "and will be restarted", [Module]), + stop_module(Host, Module), + start_module(Host, Module, NewOpts0) + end. + +-spec store_options(binary(), module(), opts()) -> true. +store_options(Host, Module, Opts) -> + ets:insert(ejabberd_modules, + #ejabberd_module{module_host = {Module, Host}, + opts = Opts}). + maybe_halt_ejabberd(ErrorText) -> case is_app_running(ejabberd) of false -> @@ -239,6 +313,7 @@ stop_modules(Host) -> -spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}. stop_module(Host, Module) -> + ?DEBUG("stopping ~s at ~s", [Module, Host]), case stop_module_keep_config(Host, Module) of error -> error; ok -> ok @@ -544,5 +619,29 @@ get_module_proc(Host, Base) -> is_loaded(Host, Module) -> ets:member(ejabberd_modules, {Module, Host}). -opt_type(modules) -> fun (L) when is_list(L) -> L end; +-spec config_reloaded() -> ok. +config_reloaded() -> + lists:foreach( + fun(Host) -> + reload_modules(Host) + end, ?MYHOSTS). + +-spec is_equal_opt(atom(), opts(), opts(), check_fun(), any()) -> + true | {false, any(), any()}. +is_equal_opt(Opt, NewOpts, OldOpts, VFun, Default) -> + NewVal = get_opt(Opt, NewOpts, VFun, Default), + OldVal = get_opt(Opt, OldOpts, VFun, Default), + if NewVal /= OldVal -> + {false, NewVal, OldVal}; + true -> + true + end. + +opt_type(modules) -> + fun(Mods) -> + lists:map( + fun({M, A}) when is_atom(M), is_list(A) -> + {M, A} + end, Mods) + end; opt_type(_) -> [modules]. diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index fc0c62831..a0090aba9 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_local_iq/1, +-export([start/2, stop/1, reload/3, process_local_iq/1, process_sm_iq/1, get_local_commands/5, get_local_identity/5, get_local_features/5, get_sm_commands/5, get_sm_identity/5, get_sm_features/5, @@ -88,6 +88,19 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS). +reload(Host, NewOpts, OldOpts) -> + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS, + ?MODULE, process_local_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS, + ?MODULE, process_sm_iq, IQDisc); + true -> + ok + end. + %------------------------------------------------------------------------- get_local_commands(Acc, _From, diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index fc07faacb..317d0406f 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -30,7 +30,7 @@ -include("logger.hrl"). --export([start/2, stop/1, mod_opt_type/1, +-export([start/2, stop/1, reload/3, mod_opt_type/1, get_commands_spec/0, depends/2]). % Commands API @@ -96,6 +96,9 @@ start(_Host, _Opts) -> stop(_Host) -> ejabberd_commands:unregister_commands(get_commands_spec()). +reload(_Host, _NewOpts, _OldOpts) -> + ok. + depends(_Host, _Opts) -> []. diff --git a/src/mod_announce.erl b/src/mod_announce.erl index 29def8378..f1da3802b 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -32,7 +32,7 @@ -behaviour(gen_server). -behaviour(gen_mod). --export([start/2, stop/1, export/1, import_info/0, +-export([start/2, stop/1, reload/3, export/1, import_info/0, import_start/2, import/5, announce/1, send_motd/1, disco_identity/5, disco_features/5, disco_items/5, depends/2, send_announcement_to_all/3, announce_commands/4, @@ -80,6 +80,16 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + ok. + depends(_Host, _Opts) -> [{mod_adhoc, hard}]. diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index 56fddd093..dfc9f9b74 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -29,7 +29,7 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1, +-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). -export([filter_packet/1]). @@ -50,6 +50,9 @@ stop(Host) -> ?MODULE, filter_packet, 25), ok. +reload(_Host, _NewOpts, _OldOpts) -> + ok. + filter_packet({#message{} = Msg, State} = Acc) -> From = xmpp:get_from(Msg), LFrom = jid:tolower(From), diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index dd1f8f741..97c2e8b16 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -29,7 +29,7 @@ -protocol({xep, 191, '1.2'}). --export([start/2, stop/1, process_iq/1, mod_opt_type/1, depends/2, +-export([start/2, stop/1, reload/3, process_iq/1, mod_opt_type/1, depends/2, disco_features/5]). -include("ejabberd.hrl"). @@ -56,6 +56,17 @@ stop(Host) -> ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING). +reload(Host, NewOpts, OldOpts) -> + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING, + ?MODULE, process_iq, IQDisc); + true -> + ok + end. + depends(_Host, _Opts) -> [{mod_privacy, hard}]. diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl index 04fb7f557..049504ee4 100644 --- a/src/mod_bosh.erl +++ b/src/mod_bosh.erl @@ -33,7 +33,7 @@ -behaviour(gen_mod). -export([start_link/0]). --export([start/2, stop/1, process/2, open_session/2, +-export([start/2, stop/1, reload/3, process/2, open_session/2, close_session/1, find_session/1]). -export([depends/2, mod_opt_type/1]). @@ -103,6 +103,12 @@ stop(Host) -> supervisor:terminate_child(ejabberd_sup, TmpSup), supervisor:delete_child(ejabberd_sup, TmpSup). +reload(_Host, NewOpts, _OldOpts) -> + start_jiffy(NewOpts), + Mod = gen_mod:ram_db_mod(global, ?MODULE), + Mod:init(), + ok. + %%%=================================================================== %%% Internal functions %%%=================================================================== diff --git a/src/mod_caps.erl b/src/mod_caps.erl index e23b66895..42a5d94df 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -41,7 +41,7 @@ get_user_caps/2, import_start/2, import_stop/2]). %% gen_mod callbacks --export([start/2, stop/1, depends/2]). +-export([start/2, stop/1, reload/3, depends/2]). %% gen_server callbacks -export([init/1, handle_info/2, handle_call/3, @@ -238,6 +238,31 @@ c2s_presence_in(C2SState, depends(_Host, _Opts) -> []. +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if OldMod /= NewMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + case gen_mod:is_equal_opt(cache_size, NewOpts, OldOpts, + fun(I) when is_integer(I), I>0 -> I end, + 1000) of + {false, MaxSize, _} -> + cache_tab:setopts(caps_features, [{max_size, MaxSize}]); + true -> + ok + end, + case gen_mod:is_equal_opt(cache_life_time, NewOpts, OldOpts, + fun(I) when is_integer(I), I>0 -> I end, + timer:hours(24) div 1000) of + {false, LifeTime, _} -> + cache_tab:setopts(caps_features, [{life_time, LifeTime}]); + true -> + ok + end. + init([Host, Opts]) -> process_flag(trap_exit, true), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 0071b68b5..03fe475f2 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -32,8 +32,7 @@ -behavior(gen_mod). %% API: --export([start/2, - stop/1]). +-export([start/2, stop/1, reload/3]). -export([user_send_packet/1, user_receive_packet/1, iq_handler/1, remove_connection/4, disco_features/5, @@ -75,6 +74,24 @@ stop(Host) -> ejabberd_hooks:delete(user_receive_packet,Host, ?MODULE, user_receive_packet, 89), ejabberd_hooks:delete(unset_presence_hook,Host, ?MODULE, remove_connection, 10). +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, + ?MODULE, iq_handler, IQDisc); + true -> + ok + end. + -spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl index d912bef50..f6d7292b5 100644 --- a/src/mod_client_state.erl +++ b/src/mod_client_state.erl @@ -31,7 +31,7 @@ -behavior(gen_mod). %% gen_mod callbacks. --export([start/2, stop/1, mod_opt_type/1, depends/2]). +-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]). %% ejabberd_hooks callbacks. -export([filter_presence/1, filter_chat_states/1, @@ -72,16 +72,7 @@ start(Host, Opts) -> fun(B) when is_boolean(B) -> B end, true), if QueuePresence; QueueChatStates; QueuePEP -> - ejabberd_hooks:add(c2s_stream_started, Host, ?MODULE, - c2s_stream_started, 50), - ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE, - add_stream_feature, 50), - ejabberd_hooks:add(c2s_authenticated_packet, Host, ?MODULE, - c2s_authenticated_packet, 50), - ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, - c2s_copy_session, 50), - ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, - c2s_session_resumed, 50), + register_hooks(Host), if QueuePresence -> ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_presence, 50); @@ -96,9 +87,7 @@ start(Host, Opts) -> ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, filter_pep, 50); true -> ok - end, - ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, - filter_other, 75); + end; true -> ok end. @@ -118,16 +107,7 @@ stop(Host) -> fun(B) when is_boolean(B) -> B end, true), if QueuePresence; QueueChatStates; QueuePEP -> - ejabberd_hooks:delete(c2s_stream_started, Host, ?MODULE, - c2s_stream_started, 50), - ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE, - add_stream_feature, 50), - ejabberd_hooks:delete(c2s_authenticated_packet, Host, ?MODULE, - c2s_authenticated_packet, 50), - ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, - c2s_copy_session, 50), - ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, - c2s_session_resumed, 50), + unregister_hooks(Host), if QueuePresence -> ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_presence, 50); @@ -142,12 +122,48 @@ stop(Host) -> ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, filter_pep, 50); true -> ok - end, - ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, - filter_other, 75); + end; true -> ok end. +-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. +reload(Host, NewOpts, _OldOpts) -> + QueuePresence = gen_mod:get_opt(queue_presence, NewOpts, + fun(B) when is_boolean(B) -> B end, + true), + QueueChatStates = gen_mod:get_opt(queue_chat_states, NewOpts, + fun(B) when is_boolean(B) -> B end, + true), + QueuePEP = gen_mod:get_opt(queue_pep, NewOpts, + fun(B) when is_boolean(B) -> B end, + true), + if QueuePresence; QueueChatStates; QueuePEP -> + register_hooks(Host); + true -> + unregister_hooks(Host) + end, + if QueuePresence -> + ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, + filter_presence, 50); + true -> + ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, + filter_presence, 50) + end, + if QueueChatStates -> + ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, + filter_chat_states, 50); + true -> + ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, + filter_chat_states, 50) + end, + if QueuePEP -> + ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, + filter_pep, 50); + true -> + ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, + filter_pep, 50) + end. + -spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()]. mod_opt_type(queue_presence) -> @@ -163,6 +179,36 @@ mod_opt_type(_) -> [queue_presence, queue_chat_states, queue_pep]. depends(_Host, _Opts) -> []. +-spec register_hooks(binary()) -> ok. +register_hooks(Host) -> + ejabberd_hooks:add(c2s_stream_started, Host, ?MODULE, + c2s_stream_started, 50), + ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE, + add_stream_feature, 50), + ejabberd_hooks:add(c2s_authenticated_packet, Host, ?MODULE, + c2s_authenticated_packet, 50), + ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, + c2s_copy_session, 50), + ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, + c2s_session_resumed, 50), + ejabberd_hooks:add(c2s_filter_send, Host, ?MODULE, + filter_other, 75). + +-spec unregister_hooks(binary()) -> ok. +unregister_hooks(Host) -> + ejabberd_hooks:delete(c2s_stream_started, Host, ?MODULE, + c2s_stream_started, 50), + ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE, + add_stream_feature, 50), + ejabberd_hooks:delete(c2s_authenticated_packet, Host, ?MODULE, + c2s_authenticated_packet, 50), + ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, + c2s_copy_session, 50), + ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, + c2s_session_resumed, 50), + ejabberd_hooks:delete(c2s_filter_send, Host, ?MODULE, + filter_other, 75). + %%-------------------------------------------------------------------- %% ejabberd_hooks callbacks. %%-------------------------------------------------------------------- diff --git a/src/mod_configure.erl b/src/mod_configure.erl index b1827be05..1af8bf6ff 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, get_local_identity/5, +-export([start/2, stop/1, reload/3, get_local_identity/5, get_local_features/5, get_local_items/5, adhoc_local_items/4, adhoc_local_commands/4, get_sm_identity/5, get_sm_features/5, get_sm_items/5, @@ -89,11 +89,10 @@ stop(Host) -> ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 50), ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, - get_local_items, 50), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_COMMANDS), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_COMMANDS). + get_local_items, 50). + +reload(_Host, _NewOpts, _OldOpts) -> + ok. depends(_Host, _Opts) -> [{mod_adhoc, hard}, {mod_last, soft}]. diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl index b3377fe75..baad79af3 100644 --- a/src/mod_delegation.erl +++ b/src/mod_delegation.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1, mod_opt_type/1, depends/2]). +-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -57,6 +57,9 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(_Host, _NewOpts, _OldOpts) -> + ok. + mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; mod_opt_type(namespaces) -> validate_fun(); mod_opt_type(_) -> diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 2de8679ee..88c06c543 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -32,7 +32,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_local_iq_items/1, +-export([start/2, stop/1, reload/3, process_local_iq_items/1, process_local_iq_info/1, get_local_identity/5, get_local_features/5, get_local_services/5, process_sm_iq_items/1, process_sm_iq_info/1, @@ -118,10 +118,51 @@ stop(Host) -> {{'_', Host}}), ok. +reload(Host, NewOpts, OldOpts) -> + case gen_mod:is_equal_opt(extra_domains, NewOpts, OldOpts, + fun(Hs) -> + [iolist_to_binary(H) || H <- Hs] + end, []) of + {false, NewDomains, OldDomains} -> + lists:foreach( + fun(Domain) -> + register_extra_domain(Host, Domain) + end, NewDomains -- OldDomains), + lists:foreach( + fun(Domain) -> + unregister_extra_domain(Host, Domain) + end, OldDomains -- NewDomains); + true -> + ok + end, + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, + ?NS_DISCO_ITEMS, ?MODULE, + process_local_iq_items, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, Host, + ?NS_DISCO_INFO, ?MODULE, + process_local_iq_info, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, + ?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items, + IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, + ?NS_DISCO_INFO, ?MODULE, process_sm_iq_info, + IQDisc); + true -> + ok + end. + -spec register_extra_domain(binary(), binary()) -> true. register_extra_domain(Host, Domain) -> ets:insert(disco_extra_domains, {{Domain, Host}}). +-spec unregister_extra_domain(binary(), binary()) -> true. +unregister_extra_domain(Host, Domain) -> + ets:delete_object(disco_extra_domains, {{Domain, Host}}). + -spec process_local_iq_items(iq()) -> iq(). process_local_iq_items(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, diff --git a/src/mod_echo.erl b/src/mod_echo.erl index 5db5d49ce..7eeb8d4e9 100644 --- a/src/mod_echo.erl +++ b/src/mod_echo.erl @@ -32,7 +32,7 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1, do_client_version/3]). +-export([start/2, stop/1, reload/3, do_client_version/3]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, @@ -54,6 +54,10 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(Host, NewOpts, OldOpts) -> + Proc = gen_mod:get_module_proc(Host, ?MODULE), + gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). + depends(_Host, _Opts) -> []. @@ -96,7 +100,21 @@ handle_call(stop, _From, State) -> %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> {noreply, State}. +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}}; +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), + {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl index 7f404d846..b6f889fc9 100644 --- a/src/mod_fail2ban.erl +++ b/src/mod_fail2ban.erl @@ -28,7 +28,7 @@ -behaviour(gen_server). %% API --export([start/2, stop/1, c2s_auth_result/3, +-export([start/2, stop/1, reload/3, c2s_auth_result/3, c2s_stream_started/2]). -export([init/1, handle_call/3, handle_cast/2, @@ -111,6 +111,9 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(_Host, _NewOpts, _OldOpts) -> + ok. + depends(_Host, _Opts) -> []. diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index d962aaefe..4956f2f62 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -74,7 +74,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process/2, mod_opt_type/1, depends/2]). +-export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("xmpp.hrl"). @@ -125,6 +125,10 @@ stop(_Host) -> ejabberd_access_permissions:unregister_permission_addon(?MODULE), ok. +reload(Host, NewOpts, _OldOpts) -> + stop(Host), + start(Host, NewOpts). + depends(_Host, _Opts) -> []. diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl index 143ab03f5..79eeed5ac 100644 --- a/src/mod_http_fileserver.erl +++ b/src/mod_http_fileserver.erl @@ -31,7 +31,7 @@ -behaviour(gen_server). %% gen_mod callbacks --export([start/2, stop/1]). +-export([start/2, stop/1, reload/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -90,6 +90,10 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(Host, NewOpts, OldOpts) -> + Proc = get_proc_name(Host), + gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). + depends(_Host, _Opts) -> []. @@ -105,19 +109,9 @@ depends(_Host, _Opts) -> %%-------------------------------------------------------------------- init([Host, Opts]) -> try initialize(Host, Opts) of - {DocRoot, AccessLog, AccessLogFD, DirectoryIndices, - CustomHeaders, DefaultContentType, ContentTypes, - UserAccess} -> + State -> process_flag(trap_exit, true), - {ok, #state{host = Host, - accesslog = AccessLog, - accesslogfd = AccessLogFD, - docroot = DocRoot, - directory_indices = DirectoryIndices, - custom_headers = CustomHeaders, - default_content_type = DefaultContentType, - content_types = ContentTypes, - user_access = UserAccess}} + {ok, State} catch throw:Reason -> {stop, Reason} @@ -163,9 +157,15 @@ initialize(Host, Opts) -> ?INFO_MSG("known content types: ~s", [str:join([[$*, K, " -> ", V] || {K, V} <- ContentTypes], <<", ">>)]), - {DocRoot, AccessLog, AccessLogFD, DirectoryIndices, - CustomHeaders, DefaultContentType, ContentTypes, UserAccess}. - + #state{host = Host, + accesslog = AccessLog, + accesslogfd = AccessLogFD, + docroot = DocRoot, + directory_indices = DirectoryIndices, + custom_headers = CustomHeaders, + default_content_type = DefaultContentType, + content_types = ContentTypes, + user_access = UserAccess}. %% @spec (AdminCTs::[CT], Default::[CT]) -> [CT] %% where CT = {Extension::string(), Value} @@ -251,7 +251,16 @@ handle_cast({add_to_log, FileSize, Code, Request}, State) -> handle_cast(reopen_log, State) -> FD2 = reopen_log(State#state.accesslog, State#state.accesslogfd), {noreply, State#state{accesslogfd = FD2}}; -handle_cast(_Msg, State) -> +handle_cast({reload, Host, NewOpts, _OldOpts}, OldState) -> + try initialize(Host, NewOpts) of + NewState -> + FD = reopen_log(NewState#state.accesslog, OldState#state.accesslogfd), + {noreply, NewState#state{accesslogfd = FD}} + catch throw:_ -> + {noreply, OldState} + end; +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), {noreply, State}. %%-------------------------------------------------------------------- diff --git a/src/mod_irc.erl b/src/mod_irc.erl index 68f31dfde..37d54954b 100644 --- a/src/mod_irc.erl +++ b/src/mod_irc.erl @@ -32,7 +32,7 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1, export/1, import/1, +-export([start/2, stop/1, reload/3, export/1, import/1, import/3, closed_connection/3, get_connection_params/3, data_to_binary/2, process_disco_info/1, process_disco_items/1, process_register/1, process_vcard/1, process_command/1]). @@ -78,6 +78,10 @@ stop(Host) -> stop_supervisor(Host), gen_mod:stop_child(?MODULE, Host). +reload(Host, NewOpts, OldOpts) -> + Proc = gen_mod:get_module_proc(Host, ?MODULE), + gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). + depends(_Host, _Opts) -> []. @@ -107,16 +111,7 @@ init([Host, Opts]) -> {keypos, #irc_connection.jid_server_host}]), IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, one_queue), - 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_REGISTER, - ?MODULE, process_register, 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_COMMANDS, - ?MODULE, process_command, IQDisc), + register_hooks(MyHost, IQDisc), ejabberd_router:register_route(MyHost, Host), {ok, #state{host = MyHost, server_host = Host, @@ -140,7 +135,44 @@ handle_call(stop, _From, State) -> %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> {noreply, State}. +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@">>), + NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, + fun gen_iq_handler:check_type/1, + one_queue), + OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue), + NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE), + Access = gen_mod:get_opt(access, NewOpts, + fun acl:access_rules_validator/1, + all), + if NewMod /= OldMod -> + NewMod:init(ServerHost, NewOpts); + 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); + true -> + ok + end, + Access = gen_mod:get_opt(access, NewOpts, + fun acl:access_rules_validator/1, + all), + {noreply, State#state{host = NewHost, access = Access}}; +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), + {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | @@ -168,11 +200,7 @@ handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- terminate(_Reason, #state{host = 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_REGISTER), - gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD), - gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_COMMANDS). + unregister_hooks(MyHost). %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} @@ -183,6 +211,25 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +register_hooks(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_REGISTER, + ?MODULE, process_register, 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_command, IQDisc). + +unregister_hooks(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_REGISTER), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS). + start_supervisor(Host) -> Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup), diff --git a/src/mod_last.erl b/src/mod_last.erl index f60005163..e20c1524d 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -33,7 +33,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_local_iq/1, export/1, +-export([start/2, stop/1, reload/3, process_local_iq/1, export/1, process_sm_iq/1, on_presence_update/4, import_info/0, import/5, import_start/2, store_last_info/4, get_last_info/2, remove_user/2, transform_options/1, mod_opt_type/1, @@ -86,6 +86,26 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST). +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST, + ?MODULE, process_local_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST, + ?MODULE, process_sm_iq, IQDisc); + true -> + ok + end. + %%% %%% Uptime of ejabberd node %%% diff --git a/src/mod_legacy_auth.erl b/src/mod_legacy_auth.erl index 723c47f85..49e210ec6 100644 --- a/src/mod_legacy_auth.erl +++ b/src/mod_legacy_auth.erl @@ -25,7 +25,7 @@ -protocol({xep, 78, '2.5'}). %% gen_mod API --export([start/2, stop/1, depends/2, mod_opt_type/1]). +-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). %% hooks -export([c2s_unauthenticated_packet/2, c2s_stream_features/2]). @@ -48,6 +48,9 @@ stop(Host) -> ejabberd_hooks:delete(c2s_pre_auth_features, Host, ?MODULE, c2s_stream_features, 50). +reload(_Host, _NewOpts, _OldOpts) -> + ok. + depends(_Host, _Opts) -> []. diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 553c90f94..a10c3a382 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -30,7 +30,7 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1, depends/2]). +-export([start/2, stop/1, reload/3, depends/2]). -export([user_send_packet/1, user_send_packet_strip_tag/1, user_receive_packet/1, process_iq_v0_2/1, process_iq_v0_3/1, disco_sm_features/5, @@ -74,18 +74,7 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Host, Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Opts), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_MAM_TMP, ?MODULE, process_iq_v0_2, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_MAM_TMP, ?MODULE, process_iq_v0_2, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_MAM_0, ?MODULE, process_iq_v0_3, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_MAM_0, ?MODULE, process_iq_v0_3, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_MAM_1, ?MODULE, process_iq_v0_3, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_MAM_1, ?MODULE, process_iq_v0_3, IQDisc), + register_iq_handlers(Host, IQDisc), ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, user_receive_packet, 88), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, @@ -130,24 +119,19 @@ init_cache(Opts) -> {life_time, LifeTime}]). stop(Host) -> + unregister_iq_handlers(Host), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet, 88), ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, user_receive_packet, 88), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, - user_send_packet_strip_tag, 500), + user_send_packet_strip_tag, 500), ejabberd_hooks:delete(offline_message_hook, Host, ?MODULE, offline_message, 50), ejabberd_hooks:delete(muc_filter_message, Host, ?MODULE, muc_filter_message, 50), ejabberd_hooks:delete(muc_process_iq, Host, ?MODULE, muc_process_iq, 50), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_TMP), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_TMP), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_0), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_0), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_1), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_1), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_sm_features, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, @@ -169,9 +153,77 @@ stop(Host) -> ejabberd_commands:unregister_commands(get_commands_spec()), ok. +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + case gen_mod:is_equal_opt(cache_size, NewOpts, OldOpts, + fun(I) when is_integer(I), I>0 -> I end, + 1000) of + {false, MaxSize, _} -> + cache_tab:setopts(archive_prefs, [{max_size, MaxSize}]); + true -> + ok + end, + case gen_mod:is_equal_opt(cache_life_time, NewOpts, OldOpts, + fun(I) when is_integer(I), I>0 -> I end, + timer:hours(1) div 1000) of + {false, LifeTime, _} -> + cache_tab:setopts(archive_prefs, [{life_time, LifeTime}]); + true -> + ok + end, + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + register_iq_handlers(Host, IQDisc); + true -> + ok + end, + case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts, + fun(B) when is_boolean(B) -> B end, false) of + {false, true, _} -> + ejabberd_hooks:add(message_is_archived, Host, ?MODULE, + message_is_archived, 50); + {false, false, _} -> + ejabberd_hooks:delete(message_is_archived, Host, ?MODULE, + message_is_archived, 50); + true -> + ok + end. + depends(_Host, _Opts) -> []. +-spec register_iq_handlers(binary(), gen_iq_handler:type()) -> ok. +register_iq_handlers(Host, IQDisc) -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_TMP, + ?MODULE, process_iq_v0_2, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_TMP, + ?MODULE, process_iq_v0_2, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_0, + ?MODULE, process_iq_v0_3, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_0, ?MODULE, + process_iq_v0_3, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_1, + ?MODULE, process_iq_v0_3, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_1, + ?MODULE, process_iq_v0_3, IQDisc). + +-spec unregister_iq_handlers(binary()) -> ok. +unregister_iq_handlers(Host) -> + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_TMP), + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_TMP), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_0), + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_0), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MAM_1), + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_1). + -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> LUser = jid:nodeprep(User), diff --git a/src/mod_metrics.erl b/src/mod_metrics.erl index 849f8ee0c..d9fc586f9 100644 --- a/src/mod_metrics.erl +++ b/src/mod_metrics.erl @@ -34,7 +34,7 @@ -include("xmpp.hrl"). -export([start/2, stop/1, send_metrics/4, opt_type/1, mod_opt_type/1, - depends/2]). + depends/2, reload/3]). -export([offline_message_hook/2, sm_register_connection_hook/3, sm_remove_connection_hook/3, @@ -68,6 +68,9 @@ stop(Host) -> ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 20), ejabberd_hooks:delete(register_user, Host, ?MODULE, register_user, 20). +reload(_Host, _NewOpts, _OldOpts) -> + ok. + depends(_Host, _Opts) -> []. diff --git a/src/mod_muc.erl b/src/mod_muc.erl index a15dd3789..563f4c68a 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -36,6 +36,7 @@ %% API -export([start/2, stop/1, + reload/3, room_destroyed/4, store_room/4, restore_room/3, @@ -114,6 +115,10 @@ stop(Host) -> gen_mod:stop_child(?MODULE, Host), {wait, Rooms}. +reload(Host, NewOpts, OldOpts) -> + Proc = gen_mod:get_module_proc(Host, ?MODULE), + gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). + depends(_Host, _Opts) -> [{mod_mam, soft}]. @@ -220,12 +225,115 @@ init([Host, Opts]) -> process_flag(trap_exit, true), IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, one_queue), - MyHost = gen_mod:get_opt_host(Host, Opts, - <<"conference.@HOST@">>), + #state{access = Access, host = MyHost, + history_size = HistorySize, + 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), + {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, + access = Access, default_room_opts = DefOpts, + history_size = HistorySize, + room_shaper = RoomShaper} = State) -> + ?DEBUG("MUC: create new room '~s'~n", [Room]), + NewOpts = case Opts of + default -> DefOpts; + _ -> Opts + end, + {ok, Pid} = mod_muc_room:start( + Host, ServerHost, Access, + Room, HistorySize, + RoomShaper, From, + Nick, NewOpts), + RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), + RMod:register_online_room(Room, Host, Pid), + {reply, ok, State}. + +handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{host = OldHost}) -> + NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, + fun gen_iq_handler:check_type/1, + one_queue), + OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue), + 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), + if NewMod /= OldMod -> + NewMod:init(ServerHost, [{host, NewHost}|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); + true -> + ok + end, + if NewHost /= OldHost -> + ejabberd_router:register_route(NewHost, ServerHost), + ejabberd_router:unregister_route(OldHost), + unregister_iq_handlers(OldHost); + true -> + ok + end, + {noreply, NewState}; +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info({route, Packet}, + #state{host = Host, server_host = ServerHost, + access = Access, default_room_opts = DefRoomOpts, + history_size = HistorySize, + max_rooms_discoitems = MaxRoomsDiscoItems, + room_shaper = RoomShaper} = State) -> + From = xmpp:get_from(Packet), + To = xmpp:get_to(Packet), + case catch do_route(Host, ServerHost, Access, HistorySize, RoomShaper, + From, To, Packet, DefRoomOpts, MaxRoomsDiscoItems) of + {'EXIT', Reason} -> + ?ERROR_MSG("~p", [Reason]); + _ -> + ok + end, + {noreply, State}; +handle_info({room_destroyed, {Room, Host}, Pid}, State) -> + ServerHost = State#state.server_host, + RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), + RMod:unregister_online_room(Room, Host, Pid), + {noreply, State}; +handle_info(Info, State) -> + ?ERROR_MSG("unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, #state{host = MyHost}) -> + ejabberd_router:unregister_route(MyHost), + unregister_iq_handlers(MyHost). + +code_change(_OldVsn, State, _Extra) -> {ok, State}. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +init_state(Host, Opts) -> + MyHost = gen_mod:get_opt_host(Host, Opts, + <<"conference.@HOST@">>), Access = gen_mod:get_opt(access, Opts, fun acl:access_rules_validator/1, all), AccessCreate = gen_mod:get_opt(access_create, Opts, @@ -298,93 +406,35 @@ init([Host, Opts]) -> RoomShaper = gen_mod:get_opt(room_shaper, Opts, fun(A) when is_atom(A) -> A end, none), - gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER, + #state{host = MyHost, + server_host = Host, + access = {Access, AccessCreate, AccessAdmin, AccessPersistent}, + default_room_opts = DefRoomOpts, + history_size = HistorySize, + max_rooms_discoitems = MaxRoomsDiscoItems, + room_shaper = RoomShaper}. + +register_iq_handlers(Host, IQDisc) -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER, ?MODULE, process_register, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_VCARD, + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD, ?MODULE, process_vcard, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_MUCSUB, + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MUCSUB, ?MODULE, process_mucsub, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_MUC_UNIQUE, + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MUC_UNIQUE, ?MODULE, process_muc_unique, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO, + 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, MyHost, ?NS_DISCO_ITEMS, - ?MODULE, process_disco_items, IQDisc), - ejabberd_router:register_route(MyHost, Host), - load_permanent_rooms(MyHost, Host, - {Access, AccessCreate, AccessAdmin, AccessPersistent}, - HistorySize, RoomShaper), - {ok, #state{host = MyHost, - server_host = Host, - access = {Access, AccessCreate, AccessAdmin, AccessPersistent}, - default_room_opts = DefRoomOpts, - history_size = HistorySize, - max_rooms_discoitems = MaxRoomsDiscoItems, - room_shaper = RoomShaper}}. + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, + ?MODULE, process_disco_items, IQDisc). -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, - history_size = HistorySize, - room_shaper = RoomShaper} = State) -> - ?DEBUG("MUC: create new room '~s'~n", [Room]), - NewOpts = case Opts of - default -> DefOpts; - _ -> Opts - end, - {ok, Pid} = mod_muc_room:start( - Host, ServerHost, Access, - Room, HistorySize, - RoomShaper, From, - Nick, NewOpts), - RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), - RMod:register_online_room(Room, Host, Pid), - {reply, ok, State}. - -handle_cast(_Msg, State) -> {noreply, State}. - -handle_info({route, Packet}, - #state{host = Host, server_host = ServerHost, - access = Access, default_room_opts = DefRoomOpts, - history_size = HistorySize, - max_rooms_discoitems = MaxRoomsDiscoItems, - room_shaper = RoomShaper} = State) -> - From = xmpp:get_from(Packet), - To = xmpp:get_to(Packet), - case catch do_route(Host, ServerHost, Access, HistorySize, RoomShaper, - From, To, Packet, DefRoomOpts, MaxRoomsDiscoItems) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]); - _ -> - ok - end, - {noreply, State}; -handle_info({room_destroyed, {Room, Host}, Pid}, State) -> - ServerHost = State#state.server_host, - RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), - RMod:unregister_online_room(Room, Host, Pid), - {noreply, State}; -handle_info(Info, State) -> - ?ERROR_MSG("unexpected info: ~p", [Info]), - {noreply, State}. - -terminate(_Reason, #state{host = MyHost}) -> - ejabberd_router:unregister_route(MyHost), - gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER), - gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD), - gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_MUCSUB), - gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_MUC_UNIQUE), - gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO), - gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS), - ok. - -code_change(_OldVsn, State, _Extra) -> {ok, State}. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- +unregister_iq_handlers(Host) -> + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_REGISTER), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MUCSUB), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_MUC_UNIQUE), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS). do_route(Host, ServerHost, Access, HistorySize, RoomShaper, From, To, Packet, DefRoomOpts, _MaxRoomsDiscoItems) -> diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 2a6a0bb81..21a1d90c6 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -28,7 +28,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, depends/2, muc_online_rooms/1, +-export([start/2, stop/1, reload/3, depends/2, muc_online_rooms/1, muc_unregister_nick/1, create_room/3, destroy_room/2, create_room_with_opts/4, create_rooms_file/1, destroy_rooms_file/1, @@ -67,6 +67,9 @@ stop(Host) -> ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50), ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, web_page_host, 50). +reload(_Host, _NewOpts, _OldOpts) -> + ok. + depends(_Host, _Opts) -> [{mod_muc, hard}]. diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index 916deef24..13f0efdfe 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -36,7 +36,7 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1, transform_module_options/1, +-export([start/2, stop/1, reload/3, transform_module_options/1, check_access_log/2, add_to_log/5]). -export([init/1, handle_call/3, handle_cast/2, @@ -78,6 +78,10 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(Host, NewOpts, _OldOpts) -> + Proc = get_proc_name(Host), + gen_server:cast(Proc, {reload, NewOpts}). + add_to_log(Host, Type, Data, Room, Opts) -> gen_server:cast(get_proc_name(Host), {add_to_log, Type, Data, Room, Opts}). @@ -106,6 +110,36 @@ depends(_Host, _Opts) -> %%==================================================================== init([Host, Opts]) -> process_flag(trap_exit, true), + {ok, init_state(Host, Opts)}. + +handle_call({check_access_log, ServerHost, FromJID}, _From, State) -> + Reply = acl:match_rule(ServerHost, State#logstate.access, FromJID), + {reply, Reply, State}; +handle_call(stop, _From, State) -> + {stop, normal, ok, State}. + +handle_cast({reload, Opts}, #logstate{host = Host}) -> + {noreply, init_state(Host, Opts)}; +handle_cast({add_to_log, Type, Data, Room, Opts}, State) -> + case catch add_to_log2(Type, Data, Room, Opts, State) of + {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); + _ -> ok + end, + {noreply, State}; +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info(_Info, State) -> {noreply, State}. + +terminate(_Reason, _State) -> ok. + +code_change(_OldVsn, State, _Extra) -> {ok, State}. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +init_state(Host, Opts) -> OutDir = gen_mod:get_opt(outdir, Opts, fun iolist_to_binary/1, <<"www/muc">>), @@ -152,37 +186,13 @@ init([Host, Opts]) -> {language, Host}, fun iolist_to_binary/1, ?MYLANG), - {ok, - #logstate{host = Host, out_dir = OutDir, - dir_type = DirType, dir_name = DirName, - file_format = FileFormat, css_file = CSSFile, - file_permissions = FilePermissions, - access = AccessLog, lang = Lang, timezone = Timezone, - spam_prevention = NoFollow, top_link = Top_link}}. + #logstate{host = Host, out_dir = OutDir, + dir_type = DirType, dir_name = DirName, + file_format = FileFormat, css_file = CSSFile, + file_permissions = FilePermissions, + access = AccessLog, lang = Lang, timezone = Timezone, + spam_prevention = NoFollow, top_link = Top_link}. -handle_call({check_access_log, ServerHost, FromJID}, _From, State) -> - Reply = acl:match_rule(ServerHost, State#logstate.access, FromJID), - {reply, Reply, State}; -handle_call(stop, _From, State) -> - {stop, normal, ok, State}. - -handle_cast({add_to_log, Type, Data, Room, Opts}, State) -> - case catch add_to_log2(Type, Data, Room, Opts, State) of - {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); - _ -> ok - end, - {noreply, State}; -handle_cast(_Msg, State) -> {noreply, State}. - -handle_info(_Info, State) -> {noreply, State}. - -terminate(_Reason, _State) -> ok. - -code_change(_OldVsn, State, _Extra) -> {ok, State}. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- add_to_log2(text, {Nick, Packet}, Room, Opts, State) -> case has_no_permanent_store_hint(Packet) of false -> diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl index 81fe50c9d..48a2b5962 100644 --- a/src/mod_multicast.erl +++ b/src/mod_multicast.erl @@ -34,7 +34,7 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1]). +-export([start/2, stop/1, reload/3]). %% gen_server callbacks -export([init/1, handle_info/2, handle_call/3, @@ -122,6 +122,10 @@ start(LServerS, Opts) -> stop(LServerS) -> gen_mod:stop_child(?MODULE, LServerS). +reload(LServerS, NewOpts, OldOpts) -> + Proc = gen_mod:get_module_proc(LServerS, ?MODULE), + gen_server:cast(Proc, {reload, NewOpts, OldOpts}). + %%==================================================================== %% gen_server callbacks %%==================================================================== @@ -150,7 +154,29 @@ init([LServerS, Opts]) -> handle_call(stop, _From, State) -> try_stop_loop(), {stop, normal, ok, State}. -handle_cast(_Msg, State) -> {noreply, State}. +handle_cast({reload, NewOpts, NewOpts}, + #state{lserver = LServerS, lservice = OldLServiceS} = State) -> + Access = gen_mod:get_opt(access, NewOpts, + fun acl:access_rules_validator/1, all), + SLimits = + build_service_limit_record(gen_mod:get_opt(limits, NewOpts, + fun (A) when is_list(A) -> + A + end, + [])), + NewLServiceS = gen_mod:get_opt_host(LServerS, NewOpts, + <<"multicast.@HOST@">>), + if NewLServiceS /= OldLServiceS -> + ejabberd_router:register_route(NewLServiceS, LServerS), + ejabberd_router:unregister_route(OldLServiceS); + true -> + ok + end, + {noreply, State#state{lservice = NewLServiceS, + access = Access, service_limits = SLimits}}; +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), + {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 846d7c783..552b49cd9 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -38,6 +38,7 @@ -export([start/2, stop/1, + reload/3, store_packet/2, store_offline_msg/5, c2s_self_presence/1, @@ -113,6 +114,10 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(Host, NewOpts, OldOpts) -> + Proc = gen_mod:get_module_proc(Host, ?MODULE), + gen_server:cast(Proc, {reload, NewOpts, OldOpts}). + depends(_Host, _Opts) -> []. @@ -162,8 +167,35 @@ init([Host, Opts]) -> handle_call(stop, _From, State) -> {stop, normal, ok, State}. - -handle_cast(_Msg, State) -> {noreply, State}. +handle_cast({reload, NewOpts, OldOpts}, #state{host = Host} = State) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE, + ?MODULE, handle_offline_query, IQDisc); + true -> + ok + end, + case gen_mod:is_equal_opt(access_max_user_messages, NewOpts, OldOpts, + fun acl:shaper_rules_validator/1, + max_user_offline_messages) of + {false, AccessMaxOfflineMsgs, _} -> + {noreply, + State#state{access_max_offline_messages = AccessMaxOfflineMsgs}}; + true -> + {noreply, State} + end; +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), + {noreply, State}. handle_info(#offline_msg{us = UserServer} = Msg, State) -> diff --git a/src/mod_ping.erl b/src/mod_ping.erl index aa048e36e..0e2ccb9fa 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -48,7 +48,7 @@ -export([start_ping/2, stop_ping/2]). %% gen_mod callbacks --export([start/2, stop/1]). +-export([start/2, stop/1, reload/3]). %% gen_server callbacks -export([init/1, terminate/2, handle_call/3, @@ -87,64 +87,49 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(Host, NewOpts, OldOpts) -> + Proc = gen_mod:get_module_proc(Host, ?MODULE), + gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). + %%==================================================================== %% gen_server callbacks %%==================================================================== init([Host, Opts]) -> process_flag(trap_exit, true), - SendPings = gen_mod:get_opt(send_pings, Opts, - fun(B) when is_boolean(B) -> B end, - ?DEFAULT_SEND_PINGS), - PingInterval = gen_mod:get_opt(ping_interval, Opts, - fun(I) when is_integer(I), I>0 -> I end, - ?DEFAULT_PING_INTERVAL), - PingAckTimeout = gen_mod:get_opt(ping_ack_timeout, Opts, - fun(I) when is_integer(I), I>0 -> I * 1000 end, - undefined), - TimeoutAction = gen_mod:get_opt(timeout_action, Opts, - fun(none) -> none; - (kill) -> kill - end, none), + State = init_state(Host, Opts), IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, no_queue), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_PING, ?MODULE, iq_ping, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_PING, ?MODULE, iq_ping, IQDisc), - case SendPings of - true -> - ejabberd_hooks:add(sm_register_connection_hook, Host, - ?MODULE, user_online, 100), - ejabberd_hooks:add(sm_remove_connection_hook, Host, - ?MODULE, user_offline, 100), - ejabberd_hooks:add(user_send_packet, Host, ?MODULE, - user_send, 100); - _ -> ok + register_iq_handlers(Host, IQDisc), + case State#state.send_pings of + true -> register_hooks(Host); + false -> ok end, - {ok, - #state{host = Host, send_pings = SendPings, - ping_interval = PingInterval, - timeout_action = TimeoutAction, - ping_ack_timeout = PingAckTimeout, - timers = maps:new()}}. + {ok, State}. terminate(_Reason, #state{host = Host}) -> - ejabberd_hooks:delete(sm_remove_connection_hook, Host, - ?MODULE, user_offline, 100), - ejabberd_hooks:delete(sm_register_connection_hook, Host, - ?MODULE, user_online, 100), - ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, - user_send, 100), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_PING), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_PING). + unregister_hooks(Host), + unregister_iq_handlers(Host). handle_call(stop, _From, State) -> {stop, normal, ok, State}; handle_call(_Req, _From, State) -> {reply, {error, badarg}, State}. +handle_cast({reload, Host, NewOpts, OldOpts}, + #state{timers = Timers} = OldState) -> + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> register_iq_handlers(Host, IQDisc); + true -> ok + end, + NewState = init_state(Host, NewOpts), + case {NewState#state.send_pings, OldState#state.send_pings} of + {true, false} -> register_hooks(Host); + {false, true} -> unregister_hooks(Host); + _ -> ok + end, + {noreply, NewState#state{timers = Timers}}; handle_cast({start_ping, JID}, State) -> Timers = add_timer(JID, State#state.ping_interval, State#state.timers), @@ -169,7 +154,9 @@ handle_cast({iq_pong, JID, timeout}, State) -> _ -> ok end, {noreply, State#state{timers = Timers}}; -handle_cast(_Msg, State) -> {noreply, State}. +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), + {noreply, State}. handle_info({timeout, _TRef, {ping, JID}}, State) -> From = jid:make(State#state.host), @@ -212,6 +199,53 @@ user_send({Packet, #{jid := JID} = C2SState}) -> %%==================================================================== %% Internal functions %%==================================================================== +init_state(Host, Opts) -> + SendPings = gen_mod:get_opt(send_pings, Opts, + fun(B) when is_boolean(B) -> B end, + ?DEFAULT_SEND_PINGS), + PingInterval = gen_mod:get_opt(ping_interval, Opts, + fun(I) when is_integer(I), I>0 -> I end, + ?DEFAULT_PING_INTERVAL), + PingAckTimeout = gen_mod:get_opt(ping_ack_timeout, Opts, + fun(I) when is_integer(I), I>0 -> I * 1000 end, + undefined), + TimeoutAction = gen_mod:get_opt(timeout_action, Opts, + fun(none) -> none; + (kill) -> kill + end, none), + #state{host = Host, + send_pings = SendPings, + ping_interval = PingInterval, + timeout_action = TimeoutAction, + ping_ack_timeout = PingAckTimeout, + timers = maps:new()}. + +register_hooks(Host) -> + ejabberd_hooks:add(sm_register_connection_hook, Host, + ?MODULE, user_online, 100), + ejabberd_hooks:add(sm_remove_connection_hook, Host, + ?MODULE, user_offline, 100), + ejabberd_hooks:add(user_send_packet, Host, ?MODULE, + user_send, 100). + +unregister_hooks(Host) -> + ejabberd_hooks:delete(sm_remove_connection_hook, Host, + ?MODULE, user_offline, 100), + ejabberd_hooks:delete(sm_register_connection_hook, Host, + ?MODULE, user_online, 100), + ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, + user_send, 100). + +register_iq_handlers(Host, IQDisc) -> + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING, + ?MODULE, iq_ping, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PING, + ?MODULE, iq_ping, IQDisc). + +unregister_iq_handlers(Host) -> + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PING), + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PING). + -spec add_timer(jid(), non_neg_integer(), map()) -> map(). add_timer(JID, Interval, Timers) -> LJID = jid:tolower(JID), diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl index c22562a11..f70e2d9f0 100644 --- a/src/mod_pres_counter.erl +++ b/src/mod_pres_counter.erl @@ -27,7 +27,7 @@ -behavior(gen_mod). --export([start/2, stop/1, check_packet/4, +-export([start/2, stop/1, reload/3, check_packet/4, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). @@ -48,6 +48,9 @@ stop(Host) -> ?MODULE, check_packet, 25), ok. +reload(_Host, _NewOpts, _OldOpts) -> + ok. + depends(_Host, _Opts) -> []. diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index 50008053e..5aa816975 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_iq/1, export/1, import_info/0, +-export([start/2, stop/1, reload/3, process_iq/1, export/1, import_info/0, c2s_session_opened/1, c2s_copy_session/2, push_list_update/3, user_send_packet/1, user_receive_packet/1, disco_features/5, check_packet/4, remove_user/2, encode_list_item/1, @@ -99,6 +99,24 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY). +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY, + ?MODULE, process_iq, IQDisc); + true -> + ok + end. + -spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, jid(), jid(), binary(), binary()) -> {error, stanza_error()} | {result, [binary()]}. diff --git a/src/mod_private.erl b/src/mod_private.erl index 87172b744..dc33af7f8 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_sm_iq/1, import_info/0, +-export([start/2, stop/1, reload/3, process_sm_iq/1, import_info/0, remove_user/2, get_data/2, get_data/3, export/1, import/5, import_start/2, mod_opt_type/1, set_data/3, depends/2]). @@ -64,6 +64,24 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE). +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, + ?MODULE, process_sm_iq, IQDisc); + true -> + ok + end. + -spec process_sm_iq(iq()) -> iq(). process_sm_iq(#iq{type = Type, lang = Lang, from = #jid{luser = LUser, lserver = LServer}, diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index c320afd57..1547d53ad 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1, mod_opt_type/1, depends/2]). +-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -55,6 +55,9 @@ start(Host, Opts) -> stop(Host) -> gen_mod:stop_child(?MODULE, Host). +reload(_Host, _NewOpts, _OldOpts) -> + ok. + mod_opt_type(roster) -> v_roster(); mod_opt_type(message) -> v_message(); mod_opt_type(presence) -> v_presence(); diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl index 4bb754d84..e8183b21f 100644 --- a/src/mod_proxy65.erl +++ b/src/mod_proxy65.erl @@ -34,7 +34,7 @@ -behaviour(supervisor). %% gen_mod callbacks. --export([start/2, stop/1, transform_module_options/1]). +-export([start/2, stop/1, reload/3, transform_module_options/1]). %% supervisor callbacks. -export([init/1]). @@ -68,6 +68,11 @@ stop(Host) -> supervisor:terminate_child(ejabberd_sup, Proc), supervisor:delete_child(ejabberd_sup, Proc). +reload(Host, NewOpts, OldOpts) -> + Mod = gen_mod:ram_db_mod(global, ?MODULE), + Mod:init(), + mod_proxy65_service:reload(Host, NewOpts, OldOpts). + start_link(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), supervisor:start_link({local, Proc}, ?MODULE, diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl index bdcd677fb..b9a96ca71 100644 --- a/src/mod_proxy65_service.erl +++ b/src/mod_proxy65_service.erl @@ -33,7 +33,7 @@ -export([init/1, handle_info/2, handle_call/3, handle_cast/2, terminate/2, code_change/3]). --export([start_link/2, add_listener/2, process_disco_info/1, +-export([start_link/2, reload/3, add_listener/2, process_disco_info/1, process_disco_items/1, process_vcard/1, process_bytestreams/1, transform_module_options/1, delete_listener/1]). @@ -54,6 +54,10 @@ start_link(Host, Opts) -> gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). +reload(Host, NewOpts, OldOpts) -> + Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). + init([Host, Opts]) -> process_flag(trap_exit, true), IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, @@ -85,7 +89,41 @@ handle_info(_Info, State) -> {noreply, State}. handle_call(_Request, _From, State) -> {reply, ok, State}. -handle_cast(_Request, State) -> {noreply, 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@">>), + NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, + fun gen_iq_handler:check_type/1, + one_queue), + OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue), + 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); + true -> + ok + end, + {noreply, State#state{myhost = NewHost}}; +handle_cast(Msg, State) -> + ?WARNING_MSG("unexpected cast: ~p", [Msg]), + {noreply, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. diff --git a/src/mod_register.erl b/src/mod_register.erl index 55ce3ff00..7269d21cb 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -33,7 +33,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, stream_feature_register/2, +-export([start/2, stop/1, reload/3, stream_feature_register/2, c2s_unauthenticated_packet/2, try_register/5, process_iq/1, send_registration_notifications/3, transform_options/1, transform_module_options/1, @@ -71,6 +71,19 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_REGISTER). +reload(Host, NewOpts, OldOpts) -> + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER, + ?MODULE, process_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_REGISTER, + ?MODULE, process_iq, IQDisc); + true -> + ok + end. + depends(_Host, _Opts) -> []. diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 21db76b46..237a905b7 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -55,7 +55,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process/2, mod_opt_type/1, depends/2]). +-export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -76,6 +76,9 @@ start(_Host, _Opts) -> stop(_Host) -> ok. +reload(_Host, _NewOpts, _OldOpts) -> + ok. + depends(_Host, _Opts) -> [{mod_register, hard}]. diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 86a9ab8bc..30b4269f1 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -41,7 +41,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_iq/1, export/1, +-export([start/2, stop/1, reload/3, process_iq/1, export/1, import_info/0, process_local_iq/1, get_user_roster/2, import/5, c2s_session_opened/1, get_roster/2, import_start/2, import_stop/2, user_receive_packet/1, @@ -139,6 +139,24 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER). +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER, + ?MODULE, process_iq, IQDisc); + true -> + ok + end. + depends(_Host, _Opts) -> []. diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl index 7c7a0e8d5..d48bda2d9 100644 --- a/src/mod_s2s_dialback.erl +++ b/src/mod_s2s_dialback.erl @@ -26,7 +26,7 @@ -protocol({xep, 185, '1.0'}). %% gen_mod API --export([start/2, stop/1, depends/2, mod_opt_type/1]). +-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). %% Hooks -export([s2s_out_auth_result/2, s2s_out_downgraded/2, s2s_in_packet/2, s2s_out_packet/2, s2s_in_recv/3, @@ -86,6 +86,14 @@ stop(Host) -> ejabberd_hooks:delete(s2s_out_auth_result, Host, ?MODULE, s2s_out_auth_result, 50). +reload(Host, NewOpts, _OldOpts) -> + case ejabberd_s2s:tls_verify(Host) of + false -> + start(Host, NewOpts); + true -> + stop(Host) + end. + depends(_Host, _Opts) -> []. diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 214035147..7929b21ab 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -29,7 +29,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, export/1, +-export([start/2, stop/1, reload/3, export/1, import_info/0, webadmin_menu/3, webadmin_page/3, get_user_roster/2, c2s_session_opened/1, get_jid_info/4, import/5, process_item/2, import_start/2, @@ -127,8 +127,16 @@ stop(Host) -> ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50). - %%ejabberd_hooks:delete(remove_user, Host, - %% ?MODULE, remove_user, 50), + +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + ok. depends(_Host, _Opts) -> []. diff --git a/src/mod_sic.erl b/src/mod_sic.erl index fe8d8aa0f..bca662fcd 100644 --- a/src/mod_sic.erl +++ b/src/mod_sic.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_local_iq/1, +-export([start/2, stop/1, reload/3, process_local_iq/1, process_sm_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). @@ -56,6 +56,23 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SIC_1), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_SIC_1). +reload(Host, NewOpts, OldOpts) -> + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_0, + ?MODULE, process_local_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_0, + ?MODULE, process_sm_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_1, + ?MODULE, process_local_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_1, + ?MODULE, process_sm_iq, IQDisc); + true -> + ok + end. + depends(_Host, _Opts) -> []. diff --git a/src/mod_sip.erl b/src/mod_sip.erl index 7d62bdfc3..c34a90795 100644 --- a/src/mod_sip.erl +++ b/src/mod_sip.erl @@ -29,7 +29,8 @@ -behaviour(esip). %% API --export([start/2, stop/1, make_response/2, is_my_host/1, at_my_host/1]). +-export([start/2, stop/1, reload/3, + make_response/2, is_my_host/1, at_my_host/1]). -export([data_in/2, data_out/2, message_in/2, message_out/2, request/2, request/3, response/2, @@ -61,6 +62,9 @@ start(_Host, _Opts) -> stop(_Host) -> ok. +reload(_Host, _NewOpts, _OldOpts) -> + ok. + depends(_Host, _Opts) -> []. diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 6cafdf732..124b21d50 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_iq/1, mod_opt_type/1, depends/2]). +-export([start/2, stop/1, reload/3, process_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -46,6 +46,9 @@ start(Host, Opts) -> stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_STATS). +reload(Host, NewOpts, _OldOpts) -> + start(Host, NewOpts). + depends(_Host, _Opts) -> []. diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 981b357b7..6420c257b 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -26,7 +26,7 @@ -protocol({xep, 198, '1.5.2'}). %% gen_mod API --export([start/2, stop/1, depends/2, mod_opt_type/1]). +-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]). %% hooks -export([c2s_stream_init/2, c2s_stream_started/2, c2s_stream_features/2, c2s_authenticated_packet/2, c2s_unauthenticated_packet/2, @@ -87,6 +87,10 @@ stop(Host) -> ejabberd_hooks:delete(c2s_closed, Host, ?MODULE, c2s_closed, 50), ejabberd_hooks:delete(c2s_terminated, Host, ?MODULE, c2s_terminated, 50). +reload(_Host, _NewOpts, _OldOpts) -> + ?WARNING_MSG("module ~s is reloaded, but new configuration will take " + "effect for newly created client connections only", []). + depends(_Host, _Opts) -> []. diff --git a/src/mod_time.erl b/src/mod_time.erl index 883fd4b1a..0b5264889 100644 --- a/src/mod_time.erl +++ b/src/mod_time.erl @@ -32,7 +32,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_local_iq/1, +-export([start/2, stop/1, reload/3, process_local_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). @@ -50,6 +50,17 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_TIME). +reload(Host, NewOpts, OldOpts) -> + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_TIME, + ?MODULE, process_local_iq, IQDisc); + true -> + ok + end. + process_local_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index 12e31b8dc..2a754d408 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -28,7 +28,7 @@ -behaviour(gen_mod). %% gen_mod callbacks --export([start/2, stop/1]). +-export([start/2, stop/1, reload/3]). -export([update_presence/1, vcard_set/3, export/1, import_info/0, import/5, import_start/2, @@ -64,6 +64,16 @@ stop(Host) -> vcard_set, 100), ok. +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + ok. + depends(_Host, _Opts) -> []. diff --git a/src/mod_version.erl b/src/mod_version.erl index ee35a3c41..2ec713c17 100644 --- a/src/mod_version.erl +++ b/src/mod_version.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_local_iq/1, +-export([start/2, stop/1, reload/3, process_local_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). @@ -50,6 +50,17 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VERSION). +reload(Host, NewOpts, OldOpts) -> + case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, + fun gen_iq_handler:check_type/1, + one_queue) of + {false, IQDisc, _} -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VERSION, + ?MODULE, process_local_iq, IQDisc); + true -> + ok + end. + process_local_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));