]> granicus.if.org Git - ejabberd/commitdiff
Implement reload/3 for mod_http_upload
authorEvgeny Khramtsov <ekhramtsov@process-one.net>
Tue, 6 Aug 2019 09:31:33 +0000 (12:31 +0300)
committerEvgeny Khramtsov <ekhramtsov@process-one.net>
Tue, 6 Aug 2019 09:31:33 +0000 (12:31 +0300)
src/gen_mod.erl
src/mod_http_upload.erl

index 1d46908c0f0ffc31f5664371b9890796c96cb550..eab90a7900e66925c9684fac87080e4cf57580c6 100644 (file)
@@ -56,7 +56,7 @@
 
 -callback start(binary(), opts()) -> ok | {ok, pid()} | {error, term()}.
 -callback stop(binary()) -> any().
--callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}.
+-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()} | {error, term()}.
 -callback mod_opt_type(atom()) -> econf:validator().
 -callback mod_options(binary()) -> [{atom(), term()} | atom()].
 -callback depends(binary(), opts()) -> [{module(), hard | soft}].
index 5aca8583a19c9386592efdd133821289d271c035..649588a22f569e29bb17a1d102dd24d3a28a56d6 100644 (file)
@@ -61,6 +61,7 @@
 %% gen_mod/supervisor callbacks.
 -export([start/2,
         stop/1,
+        reload/3,
         depends/2,
         mod_opt_type/1,
         mod_options/1]).
 -include("translate.hrl").
 
 -record(state,
-       {server_host            :: binary(),
-        hosts                  :: [binary()],
-        name                   :: binary(),
-        access                 :: atom(),
-        max_size               :: pos_integer() | infinity,
-        secret_length          :: pos_integer(),
-        jid_in_url             :: sha1 | node,
+       {server_host = <<>>     :: binary(),
+        hosts = []             :: [binary()],
+        name = <<>>            :: binary(),
+        access = none          :: atom(),
+        max_size = infinity    :: pos_integer() | infinity,
+        secret_length = 40     :: pos_integer(),
+        jid_in_url = sha1      :: sha1 | node,
         file_mode              :: integer() | undefined,
         dir_mode               :: integer() | undefined,
-        docroot                :: binary(),
-        put_url                :: binary(),
-        get_url                :: binary(),
+        docroot = <<>>         :: binary(),
+        put_url = <<>>         :: binary(),
+        get_url = <<>>         :: binary(),
         service_url            :: binary() | undefined,
-        thumbnail              :: boolean(),
-        custom_headers         :: [{binary(), binary()}],
+        thumbnail = false      :: boolean(),
+        custom_headers = []    :: [{binary(), binary()}],
         slots = #{}            :: slots(),
-        external_secret        :: binary()}).
+        external_secret = <<>> :: binary()}).
 
 -record(media_info,
        {path   :: binary(),
 %%--------------------------------------------------------------------
 %% gen_mod/supervisor callbacks.
 %%--------------------------------------------------------------------
--spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, already_started}.
+-spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, term()}.
 start(ServerHost, Opts) ->
-    case mod_http_upload_opt:rm_on_unregister(Opts) of
-       true ->
-           ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
-                              remove_user, 50);
-       false ->
-           ok
-    end,
     Proc = get_proc_name(ServerHost, ?MODULE),
-    case whereis(Proc) of
-       undefined ->
-           gen_mod:start_child(?MODULE, ServerHost, Opts, Proc);
-       _Pid ->
+    case gen_mod:start_child(?MODULE, ServerHost, Opts, Proc) of
+       {ok, _} = Ret -> Ret;
+       {error, {already_started, _}} = Err ->
            ?ERROR_MSG("Multiple virtual hosts can't use a single 'put_url' "
                       "without the @HOST@ keyword", []),
-           {error, already_started}
+           Err;
+       Err ->
+           Err
     end.
 
 -spec stop(binary()) -> ok | {error, any()}.
 stop(ServerHost) ->
-    case mod_http_upload_opt:rm_on_unregister(ServerHost) of
-       true ->
-           ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
-                                 remove_user, 50);
-       false ->
-           ok
-    end,
     Proc = get_proc_name(ServerHost, ?MODULE),
     gen_mod:stop_child(Proc).
 
+-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok | {ok, pid()} | {error, term()}.
+reload(ServerHost, NewOpts, OldOpts) ->
+    NewURL = mod_http_upload_opt:put_url(NewOpts),
+    OldURL = mod_http_upload_opt:put_url(OldOpts),
+    OldProc = get_proc_name(ServerHost, ?MODULE, OldURL),
+    NewProc = get_proc_name(ServerHost, ?MODULE, NewURL),
+    if OldProc /= NewProc ->
+           gen_mod:stop_child(OldProc),
+           start(ServerHost, NewOpts);
+       true ->
+           gen_server:cast(NewProc, {reload, NewOpts, OldOpts})
+    end.
+
 -spec mod_opt_type(atom()) -> econf:validator().
 mod_opt_type(name) ->
     econf:binary();
@@ -234,46 +235,15 @@ init([ServerHost|_]) ->
     process_flag(trap_exit, true),
     Opts = gen_mod:get_module_opts(ServerHost, ?MODULE),
     Hosts = gen_mod:get_opt_hosts(Opts),
-    Name = mod_http_upload_opt:name(Opts),
-    Access = mod_http_upload_opt:access(Opts),
-    MaxSize = mod_http_upload_opt:max_size(Opts),
-    SecretLength = mod_http_upload_opt:secret_length(Opts),
-    JIDinURL = mod_http_upload_opt:jid_in_url(Opts),
-    DocRoot = mod_http_upload_opt:docroot(Opts),
-    FileMode = mod_http_upload_opt:file_mode(Opts),
-    DirMode = mod_http_upload_opt:dir_mode(Opts),
-    PutURL = mod_http_upload_opt:put_url(Opts),
-    GetURL = case mod_http_upload_opt:get_url(Opts) of
-                undefined -> PutURL;
-                URL -> URL
-            end,
-    ServiceURL = mod_http_upload_opt:service_url(Opts),
-    Thumbnail = mod_http_upload_opt:thumbnail(Opts),
-    ExternalSecret = mod_http_upload_opt:external_secret(Opts),
-    CustomHeaders = mod_http_upload_opt:custom_headers(Opts),
-    DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
-    DocRoot2 = expand_host(DocRoot1, ServerHost),
-    case DirMode of
-       undefined ->
-           ok;
-       Mode ->
-           file:change_mode(DocRoot2, Mode)
+    case mod_http_upload_opt:rm_on_unregister(Opts) of
+       true ->
+           ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
+                              remove_user, 50);
+       false ->
+           ok
     end,
-    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,
-               thumbnail = Thumbnail,
-               docroot = DocRoot2,
-               put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
-               get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
-               service_url = ServiceURL,
-               external_secret = ExternalSecret,
-               custom_headers = CustomHeaders}}.
+    State = init_state(ServerHost, Hosts, Opts),
+    {ok, State}.
 
 -spec handle_call(_, {pid(), _}, state())
       -> {reply, {ok, pos_integer(), binary(),
@@ -309,6 +279,24 @@ handle_call(Request, From, State) ->
     {noreply, State}.
 
 -spec handle_cast(_, state()) -> {noreply, state()}.
+handle_cast({reload, NewOpts, OldOpts},
+           #state{server_host = ServerHost} = State) ->
+    case {mod_http_upload_opt:rm_on_unregister(NewOpts),
+         mod_http_upload_opt:rm_on_unregister(OldOpts)} of
+       {true, false} ->
+           ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
+                              remove_user, 50);
+       {false, true} ->
+           ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
+                                 remove_user, 50);
+       _ ->
+           ok
+    end,
+    NewHosts = gen_mod:get_opt_hosts(NewOpts),
+    OldHosts = gen_mod:get_opt_hosts(OldOpts),
+    lists:foreach(fun ejabberd_router:unregister_route/1, OldHosts -- NewHosts),
+    NewState = init_state(State#state{hosts = NewHosts -- OldHosts}, NewOpts),
+    {noreply, NewState};
 handle_cast(Request, State) ->
     ?WARNING_MSG("Unexpected cast: ~p", [Request]),
     {noreply, State}.
@@ -347,6 +335,7 @@ handle_info(Info, State) ->
 -spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
 terminate(Reason, #state{server_host = ServerHost, hosts = Hosts}) ->
     ?DEBUG("Stopping HTTP upload process for ~s: ~p", [ServerHost, Reason]),
+    ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
     lists:foreach(fun ejabberd_router:unregister_route/1, Hosts).
 
 -spec code_change({down, _} | _, state(), _) -> {ok, state()}.
@@ -484,12 +473,66 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
           [Method, encode_addr(IP), Host]),
     http_response(405, [{<<"Allow">>, <<"OPTIONS, HEAD, GET, PUT">>}]).
 
+%%--------------------------------------------------------------------
+%% State initialization
+%%--------------------------------------------------------------------
+-spec init_state(binary(), [binary()], gen_mod:opts()) -> state().
+init_state(ServerHost, Hosts, Opts) ->
+    init_state(#state{server_host = ServerHost, hosts = Hosts}, Opts).
+
+-spec init_state(state(), gen_mod:opts()) -> state().
+init_state(#state{server_host = ServerHost, hosts = Hosts} = State, Opts) ->
+    Name = mod_http_upload_opt:name(Opts),
+    Access = mod_http_upload_opt:access(Opts),
+    MaxSize = mod_http_upload_opt:max_size(Opts),
+    SecretLength = mod_http_upload_opt:secret_length(Opts),
+    JIDinURL = mod_http_upload_opt:jid_in_url(Opts),
+    DocRoot = mod_http_upload_opt:docroot(Opts),
+    FileMode = mod_http_upload_opt:file_mode(Opts),
+    DirMode = mod_http_upload_opt:dir_mode(Opts),
+    PutURL = mod_http_upload_opt:put_url(Opts),
+    GetURL = case mod_http_upload_opt:get_url(Opts) of
+                undefined -> PutURL;
+                URL -> URL
+            end,
+    ServiceURL = mod_http_upload_opt:service_url(Opts),
+    Thumbnail = mod_http_upload_opt:thumbnail(Opts),
+    ExternalSecret = mod_http_upload_opt:external_secret(Opts),
+    CustomHeaders = mod_http_upload_opt:custom_headers(Opts),
+    DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
+    DocRoot2 = expand_host(DocRoot1, ServerHost),
+    case DirMode of
+       undefined ->
+           ok;
+       Mode ->
+           file:change_mode(DocRoot2, Mode)
+    end,
+    lists:foreach(
+      fun(Host) ->
+             ejabberd_router:register_route(Host, ServerHost)
+      end, Hosts),
+    State#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,
+               thumbnail = Thumbnail,
+               docroot = DocRoot2,
+               put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
+               get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
+               service_url = ServiceURL,
+               external_secret = ExternalSecret,
+               custom_headers = CustomHeaders}.
+
 %%--------------------------------------------------------------------
 %% Exported utility functions.
 %%--------------------------------------------------------------------
 -spec get_proc_name(binary(), atom()) -> atom().
 get_proc_name(ServerHost, ModuleName) ->
     PutURL = mod_http_upload_opt:put_url(ServerHost),
+    get_proc_name(ServerHost, ModuleName, PutURL).
+
+-spec get_proc_name(binary(), atom(), binary()) -> atom().
+get_proc_name(ServerHost, ModuleName, PutURL) ->
     %% Once we depend on OTP >= 20.0, we can use binaries with http_uri.
     {ok, {_Scheme, _UserInfo, Host0, _Port, Path0, _Query}} =
        http_uri:parse(binary_to_list(expand_host(PutURL, ServerHost))),