Software / code / prosody
Comparison
plugins/mod_http_file_share.lua @ 13777:093ced3f8309
Merge 13.0->trunk
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Mon, 17 Mar 2025 15:23:50 +0100 |
| parent | 13776:977415e0122d |
| child | 13785:d7e54a2475cc |
comparison
equal
deleted
inserted
replaced
| 13775:d1f9924516d7 | 13777:093ced3f8309 |
|---|---|
| 60 | 60 |
| 61 local upload_errors = errors.init(module.name, namespace, { | 61 local upload_errors = errors.init(module.name, namespace, { |
| 62 access = { type = "auth"; condition = "forbidden" }; | 62 access = { type = "auth"; condition = "forbidden" }; |
| 63 filename = { type = "modify"; condition = "bad-request"; text = "Invalid filename" }; | 63 filename = { type = "modify"; condition = "bad-request"; text = "Invalid filename" }; |
| 64 filetype = { type = "modify"; condition = "not-acceptable"; text = "File type not allowed" }; | 64 filetype = { type = "modify"; condition = "not-acceptable"; text = "File type not allowed" }; |
| 65 filesize = { type = "modify"; condition = "not-acceptable"; text = "File too large"; | 65 filesize = { |
| 66 extra = {tag = st.stanza("file-too-large", {xmlns = namespace}):tag("max-file-size"):text(tostring(file_size_limit)) }; | 66 code = 413; |
| 67 type = "modify"; | |
| 68 condition = "not-acceptable"; | |
| 69 text = "File too large"; | |
| 70 extra = { | |
| 71 tag = st.stanza("file-too-large", { xmlns = namespace }):tag("max-file-size"):text(tostring(file_size_limit)); | |
| 72 }; | |
| 67 }; | 73 }; |
| 68 filesizefmt = { type = "modify"; condition = "bad-request"; text = "File size must be positive integer"; }; | 74 filesizefmt = { type = "modify"; condition = "bad-request"; text = "File size must be positive integer"; }; |
| 69 quota = { type = "wait"; condition = "resource-constraint"; text = "Daily quota reached"; }; | 75 quota = { type = "wait"; condition = "resource-constraint"; text = "Daily quota reached"; }; |
| 70 outofdisk = { type = "wait"; condition = "resource-constraint"; text = "Server global storage quota reached" }; | 76 outofdisk = { type = "wait"; condition = "resource-constraint"; text = "Server global storage quota reached" }; |
| 77 authzmalformed = { | |
| 78 code = 401; | |
| 79 type = "auth"; | |
| 80 condition = "not-authorized"; | |
| 81 text = "Missing or malformed Authorization header"; | |
| 82 }; | |
| 83 unauthz = { code = 403; type = "auth"; condition = "forbidden"; text = "Unauthorized or invalid token" }; | |
| 84 invalidslot = { | |
| 85 code = 400; | |
| 86 type = "modify"; | |
| 87 condition = "bad-request"; | |
| 88 text = "Invalid upload slot, must not contain '/'"; | |
| 89 }; | |
| 90 alreadycompleted = { code = 409; type = "cancel"; condition = "conflict"; text = "Upload already completed" }; | |
| 91 writefail = { code = 500; type = "wait"; condition = "internal-server-error" } | |
| 71 }); | 92 }); |
| 72 | 93 |
| 73 local upload_cache = cache.new(1024); | 94 local upload_cache = cache.new(1024); |
| 74 local quota_cache = cache.new(1024); | 95 local quota_cache = cache.new(1024); |
| 75 | 96 |
| 258 authz = authz:match("^Bearer (.*)") | 279 authz = authz:match("^Bearer (.*)") |
| 259 end | 280 end |
| 260 if not authz then | 281 if not authz then |
| 261 module:log("debug", "Missing or malformed Authorization header"); | 282 module:log("debug", "Missing or malformed Authorization header"); |
| 262 event.response.headers.www_authenticate = "Bearer"; | 283 event.response.headers.www_authenticate = "Bearer"; |
| 263 return 401; | 284 return upload_errors.new("authzmalformed", { request = request }); |
| 264 end | 285 end |
| 265 local authed, authed_upload_info = verify_jwt(authz); | 286 local authed, authed_upload_info = verify_jwt(authz); |
| 266 if not authed then | 287 if not authed then |
| 267 module:log("debug", "Unauthorized or invalid token: %s, %q", authz, authed_upload_info); | 288 module:log("debug", "Unauthorized or invalid token: %s, %q", authz, authed_upload_info); |
| 268 return 401; | 289 return upload_errors.new("unauthz", { request = request; wrapped_error = authed_upload_info }); |
| 269 end | 290 end |
| 270 if not path or authed_upload_info.slot ~= path:match("^[^/]+") then | 291 if not path or authed_upload_info.slot ~= path:match("^[^/]+") then |
| 271 module:log("debug", "Invalid upload slot: %q, path: %q", authed_upload_info.slot, path); | 292 module:log("debug", "Invalid upload slot: %q, path: %q", authed_upload_info.slot, path); |
| 272 return 400; | 293 return upload_errors.new("unauthz", { request = request }); |
| 273 end | 294 end |
| 274 if request.headers.content_length and tonumber(request.headers.content_length) ~= authed_upload_info.filesize then | 295 if request.headers.content_length and tonumber(request.headers.content_length) ~= authed_upload_info.filesize then |
| 275 return 413; | 296 return upload_errors.new("filesize", { request = request }); |
| 276 -- Note: We don't know the size if the upload is streamed in chunked encoding, | 297 -- Note: We don't know the size if the upload is streamed in chunked encoding, |
| 277 -- so we also check the final file size on completion. | 298 -- so we also check the final file size on completion. |
| 278 end | 299 end |
| 279 upload_info = authed_upload_info; | 300 upload_info = authed_upload_info; |
| 280 request.http_file_share_upload_info = upload_info; | 301 request.http_file_share_upload_info = upload_info; |
| 286 -- check if upload has been completed already | 307 -- check if upload has been completed already |
| 287 -- we want to allow retry of a failed upload attempt, but not after it's been completed | 308 -- we want to allow retry of a failed upload attempt, but not after it's been completed |
| 288 local f = io.open(filename, "r"); | 309 local f = io.open(filename, "r"); |
| 289 if f then | 310 if f then |
| 290 f:close(); | 311 f:close(); |
| 291 return 409; | 312 return upload_errors.new("alreadycompleted", { request = request }); |
| 292 end | 313 end |
| 293 end | 314 end |
| 294 | 315 |
| 295 if not request.body_sink then | 316 if not request.body_sink then |
| 296 module:log("debug", "Preparing to receive upload into %q, expecting %s", filename, B(upload_info.filesize)); | 317 module:log("debug", "Preparing to receive upload into %q, expecting %s", filename, B(upload_info.filesize)); |
| 297 local fh, err = io.open(filename.."~", "w"); | 318 local fh, err = io.open(filename.."~", "w"); |
| 298 if not fh then | 319 if not fh then |
| 299 module:log("error", "Could not open file for writing: %s", err); | 320 module:log("error", "Could not open file for writing: %s", err); |
| 300 return 500; | 321 return upload_errors.new("writefail", { request = request; wrapped_error = err }); |
| 301 end | 322 end |
| 302 function event.response:on_destroy() -- luacheck: ignore 212/self | 323 function event.response:on_destroy() -- luacheck: ignore 212/self |
| 303 -- Clean up incomplete upload | 324 -- Clean up incomplete upload |
| 304 if io.type(fh) == "file" then -- still open | 325 if io.type(fh) == "file" then -- still open |
| 305 fh:close(); | 326 fh:close(); |
| 328 if request.body_sink then | 349 if request.body_sink then |
| 329 local final_size = request.body_sink:seek(); | 350 local final_size = request.body_sink:seek(); |
| 330 local uploaded, err = errors.coerce(request.body_sink:close()); | 351 local uploaded, err = errors.coerce(request.body_sink:close()); |
| 331 if final_size ~= upload_info.filesize then | 352 if final_size ~= upload_info.filesize then |
| 332 -- Could be too short as well, but we say the same thing | 353 -- Could be too short as well, but we say the same thing |
| 333 uploaded, err = false, 413; | 354 uploaded, err = false, upload_errors.new("filesize", { request = request }); |
| 334 end | 355 end |
| 335 if uploaded then | 356 if uploaded then |
| 336 module:log("debug", "Upload of %q completed, %s", filename, B(final_size)); | 357 module:log("debug", "Upload of %q completed, %s", filename, B(final_size)); |
| 337 assert(os.rename(filename.."~", filename)); | 358 assert(os.rename(filename.."~", filename)); |
| 338 measure_uploads(final_size); | 359 measure_uploads(final_size); |