Mercurial > prosody-modules
comparison mod_http_upload_external/mod_http_upload_external.lua @ 2332:c2cf5b40b66d
mod_http_upload_external: Variant of mod_http_upload that delegates HTTP handling to other server using signed URLs
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 13 Oct 2016 18:57:15 +0200 |
| parents | |
| children | d6badf56ab5f |
comparison
equal
deleted
inserted
replaced
| 2331:611a787e6d08 | 2332:c2cf5b40b66d |
|---|---|
| 1 -- mod_http_upload_external | |
| 2 -- | |
| 3 -- Copyright (C) 2015-2016 Kim Alvefur | |
| 4 -- | |
| 5 -- This file is MIT/X11 licensed. | |
| 6 -- | |
| 7 | |
| 8 -- imports | |
| 9 local st = require"util.stanza"; | |
| 10 local uuid = require"util.uuid".generate; | |
| 11 local http = require "util.http"; | |
| 12 local dataform = require "util.dataforms".new; | |
| 13 local HMAC = require "util.hashes".hmac_sha256; | |
| 14 | |
| 15 -- config | |
| 16 local file_size_limit = module:get_option_number(module.name .. "_file_size_limit", 100 * 1024 * 1024); -- 100 MB | |
| 17 local base_url = assert(module:get_option_string(module.name .. "_base_url"), module.name .. "_base_url is a required option"); | |
| 18 local secret = assert(module:get_option_string(module.name .. "_secret"), module.name .. "_secret is a required option"); | |
| 19 | |
| 20 -- depends | |
| 21 module:depends("disco"); | |
| 22 | |
| 23 -- namespace | |
| 24 local xmlns_http_upload = "urn:xmpp:http:upload"; | |
| 25 | |
| 26 -- identity and feature advertising | |
| 27 module:add_identity("store", "file", module:get_option_string("name", "HTTP File Upload")) | |
| 28 module:add_feature(xmlns_http_upload); | |
| 29 | |
| 30 module:add_extension(dataform { | |
| 31 { name = "FORM_TYPE", type = "hidden", value = xmlns_http_upload }, | |
| 32 { name = "max-file-size", type = "text-single" }, | |
| 33 }:form({ ["max-file-size"] = tostring(file_size_limit) }, "result")); | |
| 34 | |
| 35 local function magic_crypto_dust(random, filename, filesize) | |
| 36 local message = string.format("%s/%s %d", random, filename, filesize); | |
| 37 local digest = HMAC(secret, message, true); | |
| 38 random, filename = http.urlencode(random), http.urlencode(filename); | |
| 39 return base_url .. random .. "/" .. filename, "?v=" .. digest; | |
| 40 end | |
| 41 | |
| 42 -- hooks | |
| 43 module:hook("iq/host/"..xmlns_http_upload..":request", function (event) | |
| 44 local stanza, origin = event.stanza, event.origin; | |
| 45 local request = stanza.tags[1]; | |
| 46 -- local clients only | |
| 47 if origin.type ~= "c2s" then | |
| 48 module:log("debug", "Request for upload slot from a %s", origin.type); | |
| 49 origin.send(st.error_reply(stanza, "cancel", "not-authorized")); | |
| 50 return true; | |
| 51 end | |
| 52 -- validate | |
| 53 local filename = request:get_child_text("filename"); | |
| 54 if not filename or filename:find("/") then | |
| 55 module:log("debug", "Filename %q not allowed", filename or ""); | |
| 56 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid filename")); | |
| 57 return true; | |
| 58 end | |
| 59 local filesize = tonumber(request:get_child_text("size")); | |
| 60 if not filesize then | |
| 61 module:log("debug", "Missing file size"); | |
| 62 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing or invalid file size")); | |
| 63 return true; | |
| 64 elseif filesize > file_size_limit then | |
| 65 module:log("debug", "File too large (%d > %d)", filesize, file_size_limit); | |
| 66 origin.send(st.error_reply(stanza, "modify", "not-acceptable", "File too large", | |
| 67 st.stanza("file-too-large", {xmlns=xmlns_http_upload}) | |
| 68 :tag("max-size"):text(tostring(file_size_limit)))); | |
| 69 return true; | |
| 70 end | |
| 71 local reply = st.reply(stanza); | |
| 72 reply:tag("slot", { xmlns = xmlns_http_upload }); | |
| 73 local random = uuid(); | |
| 74 local get_url, verify = magic_crypto_dust(random, filename, filesize); | |
| 75 reply:tag("get"):text(get_url):up(); | |
| 76 reply:tag("put"):text(get_url .. verify):up(); | |
| 77 module:log("info", "Handed out upload slot %s to %s@%s", get_url, origin.username, origin.host); | |
| 78 origin.send(reply); | |
| 79 return true; | |
| 80 end); |
