Mercurial > prosody-hg
comparison plugins/mod_http_file_share.lua @ 11346:315faec1a920
mod_http_file_share: Add support for daily upload quotas.
Daily instead of total quotas, should be more efficient to calculate.
Still O(n), but a smaller n. Less affected by total retention period.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sun, 31 Jan 2021 14:43:42 +0100 |
| parents | 0fec04b64a49 |
| children | 5b3ad3c7fe47 |
comparison
equal
deleted
inserted
replaced
| 11345:0fec04b64a49 | 11346:315faec1a920 |
|---|---|
| 33 local external_base_url = module:get_option_string(module.name .. "_base_url"); | 33 local external_base_url = module:get_option_string(module.name .. "_base_url"); |
| 34 local file_size_limit = module:get_option_number(module.name .. "_size_limit", 10 * 1024 * 1024); -- 10 MB | 34 local file_size_limit = module:get_option_number(module.name .. "_size_limit", 10 * 1024 * 1024); -- 10 MB |
| 35 local file_types = module:get_option_set(module.name .. "_allowed_file_types", {}); | 35 local file_types = module:get_option_set(module.name .. "_allowed_file_types", {}); |
| 36 local safe_types = module:get_option_set(module.name .. "_safe_file_types", {"image/*","video/*","audio/*","text/plain"}); | 36 local safe_types = module:get_option_set(module.name .. "_safe_file_types", {"image/*","video/*","audio/*","text/plain"}); |
| 37 local expiry = module:get_option_number(module.name .. "_expires_after", 7 * 86400); | 37 local expiry = module:get_option_number(module.name .. "_expires_after", 7 * 86400); |
| 38 local daily_quota = module:get_option_number(module.name .. "_daily_quota", file_size_limit*10); -- 100 MB / day | |
| 38 | 39 |
| 39 local access = module:get_option_set(module.name .. "_access", {}); | 40 local access = module:get_option_set(module.name .. "_access", {}); |
| 40 | 41 |
| 41 if not external_base_url then | 42 if not external_base_url then |
| 42 module:depends("http"); | 43 module:depends("http"); |
| 53 filetype = { type = "modify"; condition = "not-acceptable"; text = "File type not allowed" }; | 54 filetype = { type = "modify"; condition = "not-acceptable"; text = "File type not allowed" }; |
| 54 filesize = { type = "modify"; condition = "not-acceptable"; text = "File too large"; | 55 filesize = { type = "modify"; condition = "not-acceptable"; text = "File too large"; |
| 55 extra = {tag = st.stanza("file-too-large", {xmlns = namespace}):tag("max-file-size"):text(tostring(file_size_limit)) }; | 56 extra = {tag = st.stanza("file-too-large", {xmlns = namespace}):tag("max-file-size"):text(tostring(file_size_limit)) }; |
| 56 }; | 57 }; |
| 57 filesizefmt = { type = "modify"; condition = "bad-request"; text = "File size must be positive integer"; }; | 58 filesizefmt = { type = "modify"; condition = "bad-request"; text = "File size must be positive integer"; }; |
| 59 quota = { type = "wait"; condition = "resource-constraint"; text = "Daily quota reached"; }; | |
| 58 }); | 60 }); |
| 59 | 61 |
| 60 local upload_cache = cache.new(1024); | 62 local upload_cache = cache.new(1024); |
| 61 | 63 |
| 62 -- Convenience wrapper for logging file sizes | 64 -- Convenience wrapper for logging file sizes |
| 63 local function B(bytes) return hi.format(bytes, "B", "b"); end | 65 local function B(bytes) return hi.format(bytes, "B", "b"); end |
| 64 | 66 |
| 65 local function get_filename(slot, create) | 67 local function get_filename(slot, create) |
| 66 return dm.getpath(slot, module.host, module.name, "bin", create) | 68 return dm.getpath(slot, module.host, module.name, "bin", create) |
| 69 end | |
| 70 | |
| 71 -- TODO cache | |
| 72 function get_daily_quota(uploader) | |
| 73 local iter, err = uploads:find(nil, {with = uploader; start = os.time() - 86400}); | |
| 74 if not iter then return iter, err; end | |
| 75 local total_bytes = 0; | |
| 76 for _, slot in iter do | |
| 77 local size = tonumber(slot.attr.size); | |
| 78 if size then total_bytes = total_bytes + size; end | |
| 79 end | |
| 80 return total_bytes; | |
| 67 end | 81 end |
| 68 | 82 |
| 69 function may_upload(uploader, filename, filesize, filetype) -- > boolean, error | 83 function may_upload(uploader, filename, filesize, filetype) -- > boolean, error |
| 70 local uploader_host = jid.host(uploader); | 84 local uploader_host = jid.host(uploader); |
| 71 if not ((access:empty() and prosody.hosts[uploader_host]) or access:contains(uploader) or access:contains(uploader_host)) then | 85 if not ((access:empty() and prosody.hosts[uploader_host]) or access:contains(uploader) or access:contains(uploader_host)) then |
| 80 if not filesize or filesize < 0 or filesize % 1 ~= 0 then | 94 if not filesize or filesize < 0 or filesize % 1 ~= 0 then |
| 81 return false, upload_errors.new("filesizefmt"); | 95 return false, upload_errors.new("filesizefmt"); |
| 82 end | 96 end |
| 83 if filesize > file_size_limit then | 97 if filesize > file_size_limit then |
| 84 return false, upload_errors.new("filesize"); | 98 return false, upload_errors.new("filesize"); |
| 99 end | |
| 100 | |
| 101 local uploader_quota = get_daily_quota(uploader); | |
| 102 if uploader_quota + filesize > daily_quota then | |
| 103 return false, upload_errors.new("quota"); | |
| 85 end | 104 end |
| 86 | 105 |
| 87 if not ( file_types:empty() or file_types:contains(filetype) or file_types:contains(filetype:gsub("/.*", "/*")) ) then | 106 if not ( file_types:empty() or file_types:contains(filetype) or file_types:contains(filetype:gsub("/.*", "/*")) ) then |
| 88 return false, upload_errors.new("filetype"); | 107 return false, upload_errors.new("filetype"); |
| 89 end | 108 end |
