diff options
Diffstat (limited to 'src/mod_http_upload.erl')
-rw-r--r-- | src/mod_http_upload.erl | 166 |
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], <<$/>>), |