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
parent 11345:0fec04b64a49
child 11347: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