mod_http_upload: Make file/dir modes configurable
The new "file_mode" and "dir_mode" options allow admins to specify the permission bits of files and directories created by mod_http_upload.
This commit is contained in:
parent
12274a2adc
commit
d45ec798ee
|
@ -77,6 +77,19 @@ The configurable mod_http_upload options are:
|
|||
mod_http_upload. Otherwise, a SHA-1 hash of the user's bare JID is
|
||||
included instead.
|
||||
|
||||
- file_mode (default: 'undefined')
|
||||
|
||||
This option defines the permission bits of uploaded files. The bits are
|
||||
specified as an octal number (see the chmod(1) manual page) within double
|
||||
quotes. For example: "0644".
|
||||
|
||||
- dir_mode (default: 'undefined')
|
||||
|
||||
This option defines the permission bits of the 'docroot' directory and any
|
||||
directories created during file uploads. The bits are specified as an
|
||||
octal number (see the chmod(1) manual page) within double quotes. For
|
||||
example: "0755".
|
||||
|
||||
- docroot (default: "@HOME@/upload")
|
||||
|
||||
Uploaded files are stored below the directory specified (as an absolute
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
-define(PROCNAME, ?MODULE).
|
||||
-define(URL_ENC(URL), binary_to_list(ejabberd_http:url_encode(URL))).
|
||||
-define(ADDR_TO_STR(IP), ejabberd_config:may_hide_data(jlib:ip_to_list(IP))).
|
||||
-define(STR_TO_INT(Str, B), jlib:binary_to_integer(iolist_to_binary(Str), B)).
|
||||
-define(DEFAULT_CONTENT_TYPE, <<"application/octet-stream">>).
|
||||
-define(CONTENT_TYPES,
|
||||
[{<<".avi">>, <<"video/avi">>},
|
||||
|
@ -78,6 +79,8 @@
|
|||
max_size :: pos_integer() | infinity,
|
||||
secret_length :: pos_integer(),
|
||||
jid_in_url :: sha1 | node,
|
||||
file_mode :: integer() | undefined,
|
||||
dir_mode :: integer() | undefined,
|
||||
docroot :: binary(),
|
||||
put_url :: binary(),
|
||||
get_url :: binary(),
|
||||
|
@ -164,6 +167,10 @@ mod_opt_type(jid_in_url) ->
|
|||
fun(sha1) -> sha1;
|
||||
(node) -> node
|
||||
end;
|
||||
mod_opt_type(file_mode) ->
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end;
|
||||
mod_opt_type(dir_mode) ->
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end;
|
||||
mod_opt_type(docroot) ->
|
||||
fun iolist_to_binary/1;
|
||||
mod_opt_type(put_url) ->
|
||||
|
@ -181,8 +188,8 @@ mod_opt_type(service_url) ->
|
|||
mod_opt_type(rm_on_unregister) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) ->
|
||||
[host, name, access, max_size, secret_length, jid_in_url, docroot,
|
||||
put_url, get_url, service_url, rm_on_unregister].
|
||||
[host, name, access, max_size, secret_length, jid_in_url, file_mode,
|
||||
dir_mode, docroot, put_url, get_url, service_url, rm_on_unregister].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks.
|
||||
|
@ -215,6 +222,10 @@ init({ServerHost, Opts}) ->
|
|||
DocRoot = gen_mod:get_opt(docroot, Opts,
|
||||
fun iolist_to_binary/1,
|
||||
<<"@HOME@/upload">>),
|
||||
FileMode = gen_mod:get_opt(file_mode, Opts,
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end),
|
||||
DirMode = gen_mod:get_opt(dir_mode, Opts,
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end),
|
||||
PutURL = gen_mod:get_opt(put_url, Opts,
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
|
@ -240,10 +251,17 @@ init({ServerHost, Opts}) ->
|
|||
application:start(public_key),
|
||||
application:start(ssl)
|
||||
end,
|
||||
case DirMode of
|
||||
undefined ->
|
||||
ok;
|
||||
Mode ->
|
||||
file:change_mode(DocRoot, Mode)
|
||||
end,
|
||||
ejabberd_router:register_route(Host),
|
||||
{ok, #state{server_host = ServerHost, host = Host, name = Name,
|
||||
access = Access, max_size = MaxSize,
|
||||
secret_length = SecretLength, jid_in_url = JIDinURL,
|
||||
file_mode = FileMode, dir_mode = DirMode,
|
||||
docroot = expand_home(str:strip(DocRoot, right, $/)),
|
||||
put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
|
||||
get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
|
||||
|
@ -251,13 +269,15 @@ init({ServerHost, Opts}) ->
|
|||
|
||||
-spec handle_call(_, {pid(), _}, state()) -> {noreply, state()}.
|
||||
|
||||
handle_call({use_slot, Slot}, _From, #state{docroot = DocRoot} = State) ->
|
||||
handle_call({use_slot, Slot}, _From, #state{file_mode = FileMode,
|
||||
dir_mode = DirMode,
|
||||
docroot = DocRoot} = State) ->
|
||||
case get_slot(Slot, State) of
|
||||
{ok, {Size, Timer}} ->
|
||||
timer:cancel(Timer),
|
||||
NewState = del_slot(Slot, State),
|
||||
Path = str:join([DocRoot | Slot], <<$/>>),
|
||||
{reply, {ok, Size, Path}, NewState};
|
||||
{reply, {ok, Size, Path, FileMode, DirMode}, NewState};
|
||||
error ->
|
||||
{reply, {error, <<"Invalid slot">>}, State}
|
||||
end;
|
||||
|
@ -322,10 +342,10 @@ process(LocalPath, #request{method = 'PUT', host = Host, ip = IP,
|
|||
data = Data}) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
case catch gen_server:call(Proc, {use_slot, LocalPath}) of
|
||||
{ok, Size, Path} when byte_size(Data) == Size ->
|
||||
{ok, Size, Path, FileMode, DirMode} when byte_size(Data) == Size ->
|
||||
?DEBUG("Storing file from ~s for ~s: ~s",
|
||||
[?ADDR_TO_STR(IP), Host, Path]),
|
||||
case store_file(Path, Data) of
|
||||
case store_file(Path, Data, FileMode, DirMode) of
|
||||
ok ->
|
||||
http_response(201);
|
||||
{error, Error} ->
|
||||
|
@ -635,15 +655,26 @@ iq_disco_info(Lang, Name) ->
|
|||
|
||||
%% HTTP request handling.
|
||||
|
||||
-spec store_file(file:filename_all(), binary()) -> ok | {error, term()}.
|
||||
-spec store_file(file:filename_all(), binary(), integer(), integer())
|
||||
-> ok | {error, term()}.
|
||||
|
||||
store_file(Path, Data) ->
|
||||
store_file(Path, Data, FileMode, DirMode) ->
|
||||
try
|
||||
ok = filelib:ensure_dir(Path),
|
||||
{ok, Io} = file:open(Path, [write, exclusive, raw]),
|
||||
Ok = file:write(Io, Data),
|
||||
ok = file:close(Io), % Close file even if file:write/2 failed.
|
||||
ok = Ok % But raise an exception in that case.
|
||||
ok = file:close(Io),
|
||||
ok = if is_integer(FileMode) ->
|
||||
file:change_mode(Path, FileMode);
|
||||
FileMode == undefined ->
|
||||
ok
|
||||
end,
|
||||
ok = if is_integer(DirMode) ->
|
||||
file:change_mode(filename:dirname(Path), DirMode);
|
||||
DirMode == undefined ->
|
||||
ok
|
||||
end,
|
||||
ok = Ok % Raise an exception if file:write/2 failed.
|
||||
catch
|
||||
_:{badmatch, {error, Error}} ->
|
||||
{error, Error};
|
||||
|
|
Loading…
Reference in New Issue