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.erl185
1 files changed, 181 insertions, 4 deletions
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index 035aa3982..51d7761ab 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Aug 2015 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2015-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2015-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -63,6 +63,7 @@
stop/1,
reload/3,
depends/2,
+ mod_doc/0,
mod_opt_type/1,
mod_options/1]).
@@ -223,6 +224,182 @@ mod_options(Host) ->
{rm_on_unregister, true},
{thumbnail, false}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module allows for requesting permissions to "
+ "upload a file via HTTP as described in "
+ "https://xmpp.org/extensions/xep-0363.html"
+ "[XEP-0363: HTTP File Upload]. If the request is accepted, "
+ "the client receives a URL for uploading the file and "
+ "another URL from which that file can later be downloaded."), "",
+ ?T("In order to use this module, it must be configured as "
+ "a 'request_handler' for 'ejabberd_http' listener.")],
+ opts =>
+ [{host,
+ #{desc => ?T("Deprecated. Use 'hosts' instead.")}},
+ {hosts,
+ #{value => ?T("[Host, ...]"),
+ desc =>
+ ?T("This option defines the Jabber IDs of the service. "
+ "If the 'hosts' option is not specified, the only Jabber ID will "
+ "be the hostname of the virtual host with the prefix \"upload.\". "
+ "The keyword '@HOST@' is replaced with the real virtual host name.")}},
+ {name,
+ #{value => ?T("Name"),
+ desc =>
+ ?T("A name of the service in the Service Discovery. "
+ "This will only be displayed by special XMPP clients. "
+ "The default value is \"HTTP File Upload\".")}},
+ {access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option defines the access rule to limit who is "
+ "permitted to use the HTTP upload service. "
+ "The default value is 'local'. If no access rule of "
+ "that name exists, no user will be allowed to use the service.")}},
+ {max_size,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("This option limits the acceptable file size. "
+ "Either a number of bytes (larger than zero) or "
+ "'infinity' must be specified. "
+ "The default value is '104857600'.")}},
+ {secret_length,
+ #{value => ?T("Length"),
+ desc =>
+ ?T("This option defines the length of the random "
+ "string included in the GET and PUT URLs generated "
+ "by 'mod_http_upload'. The minimum length is 8 characters, "
+ "but it is recommended to choose a larger value. "
+ "The default value is '40'.")}},
+ {jid_in_url,
+ #{value => "node | sha1",
+ desc =>
+ ?T("When this option is set to 'node', the node identifier "
+ "of the user's JID (i.e., the user name) is included in "
+ "the GET and PUT URLs generated by 'mod_http_upload'. "
+ "Otherwise, a SHA-1 hash of the user's bare JID is "
+ "included instead. The default value is 'sha1'.")}},
+ {thumbnail,
+ #{value => "true | false",
+ desc =>
+ ?T("This option specifies whether ejabberd should create "
+ "thumbnails of uploaded images. If a thumbnail is created, "
+ "a <thumbnail/> element that contains the download <uri/> "
+ "and some metadata is returned with the PUT response. "
+ "The default value is 'false'.")}},
+ {file_mode,
+ #{value => ?T("Permission"),
+ desc =>
+ ?T("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\". "
+ "The default is undefined, which means no explicit permissions "
+ "will be set.")}},
+ {dir_mode,
+ #{value => ?T("Permission"),
+ desc =>
+ ?T("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\". "
+ "The default is undefined, which means no explicit permissions "
+ "will be set.")}},
+ {docroot,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("Uploaded files are stored below the directory specified "
+ "(as an absolute path) with this option. The keyword "
+ "@HOME@ is replaced with the home directory of the user "
+ "running ejabberd, and the keyword @HOST@ with the virtual "
+ "host name. The default value is \"@HOME@/upload\".")}},
+ {put_url,
+ #{value => ?T("URL"),
+ desc =>
+ ?T("This option specifies the initial part of the PUT URLs "
+ "used for file uploads. The keyword @HOST@ is replaced "
+ "with the virtual host name. NOTE: different virtual "
+ "hosts cannot use the same PUT URL. "
+ "The default value is \"https://@HOST@:5443\".")}},
+ {get_url,
+ #{value => ?T("URL"),
+ desc =>
+ ?T("This option specifies the initial part of the GET URLs "
+ "used for downloading the files. By default, it is set "
+ "to the same value as 'put_url'. The keyword @HOST@ is "
+ "replaced with the virtual host name. NOTE: if GET requests "
+ "are handled by 'mod_http_upload', the 'get_url' must match the "
+ "'put_url'. Setting it to a different value only makes "
+ "sense if an external web server or 'mod_http_fileserver' "
+ "is used to serve the uploaded files.")}},
+ {service_url,
+ #{desc => ?T("Deprecated.")}},
+ {custom_headers,
+ #{value => "{Name: Value}",
+ desc =>
+ ?T("This option specifies additional header fields to be "
+ "included in all HTTP responses. By default no custom "
+ "headers are included.")}},
+ {external_secret,
+ #{value => ?T("Text"),
+ desc =>
+ ?T("This option makes it possible to offload all HTTP "
+ "Upload processing to a separate HTTP server. "
+ "Both ejabberd and the HTTP server should share this "
+ "secret and behave exactly as described at "
+ "https://modules.prosody.im/mod_http_upload_external.html"
+ "[Prosody's mod_http_upload_external] in the "
+ "'Implementation' section. There is no default value.")}},
+ {rm_on_unregister,
+ #{value => "true | false",
+ desc =>
+ ?T("This option specifies whether files uploaded by a user "
+ "should be removed when that user is unregistered. "
+ "The default value is 'true'.")}},
+ {vcard,
+ #{value => ?T("vCard"),
+ desc =>
+ ?T("A custom vCard of the service that will be displayed "
+ "by some XMPP clients in Service Discovery. The value of "
+ "'vCard' is a YAML map constructed from an XML representation "
+ "of vCard. Since the representation has no attributes, "
+ "the mapping is straightforward."),
+ example =>
+ [{?T("For example, the following XML representation of vCard:"),
+ ["<vCard xmlns='vcard-temp'>",
+ " <FN>Conferences</FN>",
+ " <ADR>",
+ " <WORK/>",
+ " <STREET>Elm Street</STREET>",
+ " </ADR>",
+ "</vCard>"]},
+ {?T("will be translated to:"),
+ ["vcard:",
+ " fn: Conferences",
+ " adr:",
+ " -",
+ " work: true",
+ " street: Elm Street"]}]}}],
+ example =>
+ ["listen:",
+ " ...",
+ " -",
+ " port: 5443",
+ " module: ejabberd_http",
+ " tls: true",
+ " request_handlers:",
+ " ...",
+ " /upload: mod_http_upload",
+ " ...",
+ " ...",
+ "",
+ "modules:",
+ " ...",
+ " mod_http_upload:",
+ " docroot: /ejabberd/upload",
+ " put_url: \"https://@HOST@:5443/upload\"",
+ " ..."]}.
+
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[].
@@ -368,7 +545,7 @@ process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP,
ok ->
http_response(201, CustomHeaders);
{ok, Headers, OutData} ->
- http_response(201, Headers ++ CustomHeaders, OutData);
+ http_response(201, ejabberd_http:apply_custom_headers(Headers, CustomHeaders), OutData);
{error, closed} ->
?DEBUG("Cannot store file ~ts from ~ts for ~ts: connection closed",
[Path, encode_addr(IP), Host]),
@@ -418,7 +595,7 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request)
$", FileName/binary, $">>}]
end,
Headers2 = [{<<"Content-Type">>, ContentType} | Headers1],
- Headers3 = Headers2 ++ CustomHeaders,
+ Headers3 = ejabberd_http:apply_custom_headers(Headers2, CustomHeaders),
http_response(200, Headers3, {file, Path});
{error, eacces} ->
?WARNING_MSG("Cannot serve ~ts to ~ts: Permission denied",
@@ -456,7 +633,7 @@ process(_LocalPath, #request{method = 'OPTIONS', host = Host,
try gen_server:call(Proc, get_conf, ?CALL_TIMEOUT) of
{ok, _DocRoot, CustomHeaders} ->
AllowHeader = {<<"Allow">>, <<"OPTIONS, HEAD, GET, PUT">>},
- http_response(200, [AllowHeader | CustomHeaders])
+ http_response(200, ejabberd_http:apply_custom_headers([AllowHeader], CustomHeaders))
catch
exit:{noproc, _} ->
?WARNING_MSG("Cannot handle OPTIONS request from ~ts for ~ts: "