aboutsummaryrefslogtreecommitdiff
path: root/src/mod_http_upload.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_http_upload.erl')
-rw-r--r--src/mod_http_upload.erl166
1 files changed, 72 insertions, 94 deletions
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index 364b3a019..9cd828ebf 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -25,7 +25,8 @@
-module(mod_http_upload).
-author('holger@zedat.fu-berlin.de').
-
+-behaviour(gen_server).
+-behaviour(gen_mod).
-protocol({xep, 363, '0.1'}).
-define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds.
@@ -57,9 +58,6 @@
{<<".xz">>, <<"application/x-xz">>},
{<<".zip">>, <<"application/zip">>}]).
--behaviour(gen_server).
--behaviour(gen_mod).
-
%% gen_mod/supervisor callbacks.
-export([start/2,
stop/1,
@@ -107,7 +105,7 @@
service_url :: binary() | undefined,
thumbnail :: boolean(),
custom_headers :: [{binary(), binary()}],
- slots = #{} :: map(),
+ slots = #{} :: slots(),
external_secret :: binary()}).
-record(media_info,
@@ -118,6 +116,7 @@
-type state() :: #state{}.
-type slot() :: [binary(), ...].
+-type slots() :: #{slot() => {pos_integer(), reference()}}.
-type media_info() :: #media_info{}.
%%--------------------------------------------------------------------
@@ -125,7 +124,7 @@
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, already_started}.
start(ServerHost, Opts) ->
- case gen_mod:get_opt(rm_on_unregister, Opts) of
+ case mod_http_upload_opt:rm_on_unregister(Opts) of
true ->
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
remove_user, 50);
@@ -144,7 +143,7 @@ start(ServerHost, Opts) ->
-spec stop(binary()) -> ok | {error, any()}.
stop(ServerHost) ->
- case gen_mod:get_module_opt(ServerHost, ?MODULE, rm_on_unregister) of
+ case mod_http_upload_opt:rm_on_unregister(ServerHost) of
true ->
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
remove_user, 50);
@@ -154,76 +153,55 @@ stop(ServerHost) ->
Proc = get_proc_name(ServerHost, ?MODULE),
gen_mod:stop_child(Proc).
--spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
-mod_opt_type(host) ->
- fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) ->
- fun ejabberd_config:v_hosts/1;
+-spec mod_opt_type(atom()) -> econf:validator().
mod_opt_type(name) ->
- fun iolist_to_binary/1;
+ econf:binary();
mod_opt_type(access) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(max_size) ->
- fun(I) when is_integer(I), I > 0 -> I;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
mod_opt_type(secret_length) ->
- fun(I) when is_integer(I), I >= 8 -> I end;
+ econf:int(8, 1000);
mod_opt_type(jid_in_url) ->
- fun(sha1) -> sha1;
- (node) -> node
- end;
+ econf:enum([sha1, node]);
mod_opt_type(file_mode) ->
- fun(undefined) -> undefined;
- (Mode) -> binary_to_integer(iolist_to_binary(Mode), 8)
- end;
+ econf:octal();
mod_opt_type(dir_mode) ->
- fun(undefined) -> undefined;
- (Mode) -> binary_to_integer(iolist_to_binary(Mode), 8)
- end;
+ econf:octal();
mod_opt_type(docroot) ->
- fun iolist_to_binary/1;
+ econf:binary();
mod_opt_type(put_url) ->
- fun misc:try_url/1;
+ econf:url();
mod_opt_type(get_url) ->
- fun(undefined) -> undefined;
- (URL) -> misc:try_url(URL)
- end;
+ econf:url();
mod_opt_type(service_url) ->
- fun(undefined) -> undefined;
- (URL) ->
- ?WARNING_MSG("option 'service_url' is deprecated, consider unsing "
- "the 'external_secret' interface instead", []),
- misc:try_url(URL)
- end;
+ econf:url();
mod_opt_type(custom_headers) ->
- fun(Headers) ->
- lists:map(fun({K, V}) ->
- {iolist_to_binary(K), iolist_to_binary(V)}
- end, Headers)
- end;
+ econf:map(econf:binary(), econf:binary());
mod_opt_type(rm_on_unregister) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(thumbnail) ->
- fun(true) ->
- case eimp:supported_formats() of
- [] ->
- ?WARNING_MSG("ejabberd is built without image converter "
- "support, option '~s' is ignored",
- [thumbnail]),
- erlang:error(badarg);
- _ ->
- true
- end;
- (false) ->
- false
- end;
+ econf:and_then(
+ econf:bool(),
+ fun(true) ->
+ case eimp:supported_formats() of
+ [] -> econf:fail(eimp_error);
+ [_|_] -> true
+ end;
+ (false) ->
+ false
+ end);
mod_opt_type(external_secret) ->
- fun iolist_to_binary/1.
+ econf:binary();
+mod_opt_type(host) ->
+ econf:host();
+mod_opt_type(hosts) ->
+ econf:hosts().
--spec mod_options(binary()) -> [{atom(), any()}].
-mod_options(_Host) ->
- [{host, <<"upload.@HOST@">>},
+-spec mod_options(binary()) -> [{thumbnail, boolean()} |
+ {atom(), any()}].
+mod_options(Host) ->
+ [{host, <<"upload.", Host/binary>>},
{hosts, []},
{name, ?T("HTTP File Upload")},
{access, local},
@@ -233,7 +211,7 @@ mod_options(_Host) ->
{file_mode, undefined},
{dir_mode, undefined},
{docroot, <<"@HOME@/upload">>},
- {put_url, <<"https://@HOST@:5443/upload">>},
+ {put_url, <<"https://", Host/binary, ":5443/upload">>},
{get_url, undefined},
{service_url, undefined},
{external_secret, <<"">>},
@@ -251,24 +229,24 @@ depends(_Host, _Opts) ->
-spec init(list()) -> {ok, state()}.
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
- Hosts = gen_mod:get_opt_hosts(ServerHost, Opts),
- Name = gen_mod:get_opt(name, Opts),
- Access = gen_mod:get_opt(access, Opts),
- MaxSize = gen_mod:get_opt(max_size, Opts),
- SecretLength = gen_mod:get_opt(secret_length, Opts),
- JIDinURL = gen_mod:get_opt(jid_in_url, Opts),
- DocRoot = gen_mod:get_opt(docroot, Opts),
- FileMode = gen_mod:get_opt(file_mode, Opts),
- DirMode = gen_mod:get_opt(dir_mode, Opts),
- PutURL = gen_mod:get_opt(put_url, Opts),
- GetURL = case gen_mod:get_opt(get_url, Opts) of
+ 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 = gen_mod:get_opt(service_url, Opts),
- Thumbnail = gen_mod:get_opt(thumbnail, Opts),
- ExternalSecret = gen_mod:get_opt(external_secret, Opts),
- CustomHeaders = gen_mod:get_opt(custom_headers, Opts),
+ 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
@@ -323,12 +301,12 @@ handle_call(get_conf, _From,
custom_headers = CustomHeaders} = State) ->
{reply, {ok, DocRoot, CustomHeaders}, State};
handle_call(Request, From, State) ->
- ?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]),
+ ?ERROR_MSG("Unexpected request from ~p: ~p", [From, Request]),
{noreply, State}.
-spec handle_cast(_, state()) -> {noreply, state()}.
handle_cast(Request, State) ->
- ?ERROR_MSG("Got unexpected request: ~p", [Request]),
+ ?ERROR_MSG("Unexpected request: ~p", [Request]),
{noreply, State}.
-spec handle_info(timeout | _, state()) -> {noreply, state()}.
@@ -359,7 +337,7 @@ handle_info({timeout, _TRef, Slot}, State) ->
NewState = del_slot(Slot, State),
{noreply, NewState};
handle_info(Info, State) ->
- ?ERROR_MSG("Got unexpected info: ~p", [Info]),
+ ?ERROR_MSG("Unexpected info: ~p", [Info]),
{noreply, State}.
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
@@ -507,7 +485,7 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
%%--------------------------------------------------------------------
-spec get_proc_name(binary(), atom()) -> atom().
get_proc_name(ServerHost, ModuleName) ->
- PutURL = gen_mod:get_module_opt(ServerHost, ?MODULE, put_url),
+ PutURL = mod_http_upload_opt:put_url(ServerHost),
%% 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))),
@@ -552,7 +530,7 @@ process_iq(#iq{type = get, sub_els = [#upload_request_0{filename = File,
State) ->
process_slot_request(IQ, File, Size, CType, XMLNS, State);
process_iq(#iq{type = T, lang = Lang} = IQ, _State) when T == get; T == set ->
- Txt = <<"No module is handling this query">>,
+ Txt = ?T("No module is handling this query"),
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang));
process_iq(#iq{}, _State) ->
not_request.
@@ -582,18 +560,18 @@ process_slot_request(#iq{lang = Lang, from = From} = IQ,
deny ->
?DEBUG("Denying HTTP upload slot request from ~s",
[jid:encode(From)]),
- Txt = <<"Access denied by service policy">>,
+ Txt = ?T("Access denied by service policy"),
xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang))
end.
-spec create_slot(state(), jid(), binary(), pos_integer(), binary(), binary(),
binary())
- -> {ok, slot()} | {ok, binary(), binary()} | {error, xmlel()}.
+ -> {ok, slot()} | {ok, binary(), binary()} | {error, xmpp_element()}.
create_slot(#state{service_url = undefined, max_size = MaxSize},
JID, File, Size, _ContentType, XMLNS, Lang)
when MaxSize /= infinity,
Size > MaxSize ->
- Text = {<<"File larger than ~w bytes">>, [MaxSize]},
+ Text = {?T("File larger than ~w bytes"), [MaxSize]},
?WARNING_MSG("Rejecting file ~s from ~s (too large: ~B bytes)",
[File, jid:encode(JID), Size]),
Error = xmpp:err_not_acceptable(Text, Lang),
@@ -647,7 +625,7 @@ create_slot(#state{service_url = ServiceURL},
Lines ->
?ERROR_MSG("Can't parse data received for ~s from <~s>: ~p",
[jid:encode(JID), ServiceURL, Lines]),
- Txt = <<"Failed to parse HTTP response">>,
+ Txt = ?T("Failed to parse HTTP response"),
{error, xmpp:err_service_unavailable(Txt, Lang)}
end;
{ok, {402, _Body}} ->
@@ -663,7 +641,7 @@ create_slot(#state{service_url = ServiceURL},
[jid:encode(JID), ServiceURL]),
{error, xmpp:err_not_acceptable()};
{ok, {Code, _Body}} ->
- ?ERROR_MSG("Got unexpected status code for ~s from <~s>: ~B",
+ ?ERROR_MSG("Unexpected status code for ~s from <~s>: ~B",
[jid:encode(JID), ServiceURL, Code]),
{error, xmpp:err_service_unavailable()};
{error, Reason} ->
@@ -741,7 +719,7 @@ encode_addr(IP) ->
-spec iq_disco_info(binary(), binary(), binary(), [xdata()]) -> disco_info().
iq_disco_info(Host, Lang, Name, AddInfo) ->
- Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size) of
+ Form = case mod_http_upload_opt:max_size(Host) of
infinity ->
AddInfo;
MaxSize ->
@@ -853,9 +831,9 @@ http_response(Code, ExtraHeaders) ->
Message = <<(code_to_message(Code))/binary, $\n>>,
http_response(Code, ExtraHeaders, Message).
--type http_body() :: binary() | {file, file:filename()}.
+-type http_body() :: binary() | {file, file:filename_all()}.
-spec http_response(100..599, [{binary(), binary()}], http_body())
- -> {pos_integer(), [{binary(), binary()}], binary()}.
+ -> {pos_integer(), [{binary(), binary()}], http_body()}.
http_response(Code, ExtraHeaders, Body) ->
Headers = case proplists:is_defined(<<"Content-Type">>, ExtraHeaders) of
true ->
@@ -914,13 +892,13 @@ read_image(Path) ->
pass
end.
--spec convert(binary(), media_info()) -> {ok, binary(), media_info()} | pass.
+-spec convert(binary(), media_info()) -> {ok, media_info()} | pass.
convert(InData, #media_info{path = Path, type = T, width = W, height = H} = Info) ->
if W * H >= 25000000 ->
?DEBUG("The image ~s is more than 25 Mpix", [Path]),
pass;
W =< 300, H =< 300 ->
- {ok, Path, Info};
+ {ok, Info};
true ->
Dir = filename:dirname(Path),
Ext = atom_to_binary(T, latin1),
@@ -961,8 +939,8 @@ thumb_el(#media_info{type = T, height = H, width = W}, URI) ->
-spec remove_user(binary(), binary()) -> ok.
remove_user(User, Server) ->
ServerHost = jid:nameprep(Server),
- DocRoot = gen_mod:get_module_opt(ServerHost, ?MODULE, docroot),
- JIDinURL = gen_mod:get_module_opt(ServerHost, ?MODULE, jid_in_url),
+ DocRoot = mod_http_upload_opt:docroot(ServerHost),
+ JIDinURL = mod_http_upload_opt:jid_in_url(ServerHost),
DocRoot1 = expand_host(expand_home(DocRoot), ServerHost),
UserStr = make_user_string(jid:make(User, Server), JIDinURL),
UserDir = str:join([DocRoot1, UserStr], <<$/>>),