Software / code / prosody-modules
Comparison
mod_http_upload_external/mod_http_upload_external.lua @ 2877:d6badf56ab5f
mod_http_upload_external: add support for XEP-0363 version 0.3
| author | Jonas Wielicki <jonas@wielicki.name> |
|---|---|
| date | Sun, 11 Feb 2018 16:11:17 +0100 |
| parent | 2332:c2cf5b40b66d |
| child | 2939:280305c043b0 |
comparison
equal
deleted
inserted
replaced
| 2876:ea6b5321db50 | 2877:d6badf56ab5f |
|---|---|
| 19 | 19 |
| 20 -- depends | 20 -- depends |
| 21 module:depends("disco"); | 21 module:depends("disco"); |
| 22 | 22 |
| 23 -- namespace | 23 -- namespace |
| 24 local xmlns_http_upload = "urn:xmpp:http:upload"; | 24 local legacy_namespace = "urn:xmpp:http:upload"; |
| 25 local namespace = "urn:xmpp:http:upload:0"; | |
| 25 | 26 |
| 26 -- identity and feature advertising | 27 -- identity and feature advertising |
| 27 module:add_identity("store", "file", module:get_option_string("name", "HTTP File Upload")) | 28 module:add_identity("store", "file", module:get_option_string("name", "HTTP File Upload")) |
| 28 module:add_feature(xmlns_http_upload); | 29 module:add_feature(namespace); |
| 30 module:add_feature(legacy_namespace); | |
| 29 | 31 |
| 30 module:add_extension(dataform { | 32 module:add_extension(dataform { |
| 31 { name = "FORM_TYPE", type = "hidden", value = xmlns_http_upload }, | 33 { name = "FORM_TYPE", type = "hidden", value = namespace }, |
| 34 { name = "max-file-size", type = "text-single" }, | |
| 35 }:form({ ["max-file-size"] = tostring(file_size_limit) }, "result")); | |
| 36 | |
| 37 module:add_extension(dataform { | |
| 38 { name = "FORM_TYPE", type = "hidden", value = legacy_namespace }, | |
| 32 { name = "max-file-size", type = "text-single" }, | 39 { name = "max-file-size", type = "text-single" }, |
| 33 }:form({ ["max-file-size"] = tostring(file_size_limit) }, "result")); | 40 }:form({ ["max-file-size"] = tostring(file_size_limit) }, "result")); |
| 34 | 41 |
| 35 local function magic_crypto_dust(random, filename, filesize) | 42 local function magic_crypto_dust(random, filename, filesize) |
| 36 local message = string.format("%s/%s %d", random, filename, filesize); | 43 local message = string.format("%s/%s %d", random, filename, filesize); |
| 37 local digest = HMAC(secret, message, true); | 44 local digest = HMAC(secret, message, true); |
| 38 random, filename = http.urlencode(random), http.urlencode(filename); | 45 random, filename = http.urlencode(random), http.urlencode(filename); |
| 39 return base_url .. random .. "/" .. filename, "?v=" .. digest; | 46 return base_url .. random .. "/" .. filename, "?v=" .. digest; |
| 40 end | 47 end |
| 41 | 48 |
| 42 -- hooks | 49 local function handle_request(origin, stanza, xmlns, filename, filesize) |
| 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 | 50 -- local clients only |
| 47 if origin.type ~= "c2s" then | 51 if origin.type ~= "c2s" then |
| 48 module:log("debug", "Request for upload slot from a %s", origin.type); | 52 module:log("debug", "Request for upload slot from a %s", origin.type); |
| 49 origin.send(st.error_reply(stanza, "cancel", "not-authorized")); | 53 origin.send(st.error_reply(stanza, "cancel", "not-authorized")); |
| 50 return true; | 54 return nil, nil; |
| 51 end | 55 end |
| 52 -- validate | 56 -- validate |
| 53 local filename = request:get_child_text("filename"); | |
| 54 if not filename or filename:find("/") then | 57 if not filename or filename:find("/") then |
| 55 module:log("debug", "Filename %q not allowed", filename or ""); | 58 module:log("debug", "Filename %q not allowed", filename or ""); |
| 56 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid filename")); | 59 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid filename")); |
| 57 return true; | 60 return nil, nil; |
| 58 end | 61 end |
| 59 local filesize = tonumber(request:get_child_text("size")); | |
| 60 if not filesize then | 62 if not filesize then |
| 61 module:log("debug", "Missing file size"); | 63 module:log("debug", "Missing file size"); |
| 62 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing or invalid file size")); | 64 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing or invalid file size")); |
| 63 return true; | 65 return nil, nil; |
| 64 elseif filesize > file_size_limit then | 66 elseif filesize > file_size_limit then |
| 65 module:log("debug", "File too large (%d > %d)", filesize, file_size_limit); | 67 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", | 68 origin.send(st.error_reply(stanza, "modify", "not-acceptable", "File too large", |
| 67 st.stanza("file-too-large", {xmlns=xmlns_http_upload}) | 69 st.stanza("file-too-large", {xmlns=xmlns}) |
| 68 :tag("max-size"):text(tostring(file_size_limit)))); | 70 :tag("max-size"):text(tostring(file_size_limit)))); |
| 71 return nil, nil; | |
| 72 end | |
| 73 local random = uuid(); | |
| 74 local get_url, verify = magic_crypto_dust(random, filename, filesize); | |
| 75 local put_url = get_url .. verify; | |
| 76 | |
| 77 module:log("info", "Handing out upload slot %s to %s@%s", get_url, origin.username, origin.host); | |
| 78 | |
| 79 return get_url, put_url; | |
| 80 end | |
| 81 | |
| 82 -- hooks | |
| 83 module:hook("iq/host/"..legacy_namespace..":request", function (event) | |
| 84 local stanza, origin = event.stanza, event.origin; | |
| 85 local request = stanza.tags[1]; | |
| 86 local filename = request:get_child_text("filename"); | |
| 87 local filesize = tonumber(request:get_child_text("size")); | |
| 88 | |
| 89 local get_url, put_url = handle_request( | |
| 90 origin, stanza, legacy_namespace, filename, filesize); | |
| 91 | |
| 92 if not get_url then | |
| 93 -- error was already sent | |
| 69 return true; | 94 return true; |
| 70 end | 95 end |
| 71 local reply = st.reply(stanza); | 96 |
| 72 reply:tag("slot", { xmlns = xmlns_http_upload }); | 97 local reply = st.reply(stanza) |
| 73 local random = uuid(); | 98 :tag("slot", { xmlns = legacy_namespace }) |
| 74 local get_url, verify = magic_crypto_dust(random, filename, filesize); | 99 :tag("get"):text(get_url):up() |
| 75 reply:tag("get"):text(get_url):up(); | 100 :tag("put"):text(put_url):up() |
| 76 reply:tag("put"):text(get_url .. verify):up(); | 101 :up(); |
| 77 module:log("info", "Handed out upload slot %s to %s@%s", get_url, origin.username, origin.host); | |
| 78 origin.send(reply); | 102 origin.send(reply); |
| 79 return true; | 103 return true; |
| 80 end); | 104 end); |
| 105 | |
| 106 module:hook("iq/host/"..namespace..":request", function (event) | |
| 107 local stanza, origin = event.stanza, event.origin; | |
| 108 local request = stanza.tags[1]; | |
| 109 local filename = request.attr.filename; | |
| 110 local filesize = tonumber(request.attr.size); | |
| 111 local get_url, put_url = handle_request( | |
| 112 origin, stanza, legacy_namespace, filename, filesize); | |
| 113 | |
| 114 if not get_url then | |
| 115 -- error was already sent | |
| 116 return true; | |
| 117 end | |
| 118 | |
| 119 local reply = st.reply(stanza) | |
| 120 :tag("slot", { xmlns = namespace}) | |
| 121 :tag("get", { url = get_url }):up() | |
| 122 :tag("put", { url = put_url }):up() | |
| 123 :up(); | |
| 124 origin.send(reply); | |
| 125 return true; | |
| 126 end); |