Mercurial > prosody-modules
diff mod_muc_cache_media/mod_muc_cache_media.lua @ 6414:48fca0914fcf
mod_muc_cache_media: simplify code with target_filter
| author | Stephen Paul Weber <singpolyma@singpolyma.net> |
|---|---|
| date | Mon, 23 Feb 2026 12:41:36 -0500 |
| parents | c25b98d42ed0 |
| children | 420aaf113272 |
line wrap: on
line diff
--- a/mod_muc_cache_media/mod_muc_cache_media.lua Mon Feb 23 16:22:33 2026 +0100 +++ b/mod_muc_cache_media/mod_muc_cache_media.lua Mon Feb 23 12:41:36 2026 -0500 @@ -12,7 +12,6 @@ local storage_path = module:get_option_string("muc_media_store_path", "/var/lib/prosody/muc-media") local public_base_url = module:get_option_string("muc_media_public_base", "https://cache.example.com") local max_size = module:get_option_number("muc_media_max_size", 10 * 1024 * 1024) -local MAX_REDIRECTS = 5 lfs.mkdir(storage_path) @@ -66,23 +65,6 @@ return false end -local function is_safe_host(host) - if not host then return false end - host = host:lower() - if host == "localhost" then return false end - local records = socket_dns.getaddrinfo(host) - if not records then return false end - for _, r in ipairs(records) do - local ip = r.addr - if ip:match("^%d+%.%d+%.%d+%.%d+$") then - if is_private_ipv4(ip) then return false end - else - if is_private_ipv6(ip) then return false end - end - end - return true -end - local function sha256_file(path) local f = io.open(path,"rb") if not f then return nil end @@ -109,86 +91,34 @@ return url:match(".*/([^/?]+)$") or "file" end -local function resolve_redirects(url) - return promise.new(function(resolve, reject) - local current_url = url - local redirects = 0 +local function secure_download_to_disk(url) + local parsed = socket_url.parse(url) + if not parsed or (parsed.scheme~="http" and parsed.scheme~="https") then return promise.reject("invalid scheme") end - local function step() - if redirects > MAX_REDIRECTS then reject("too many redirects"); return end - http.request(current_url,{ - method="HEAD"; - redirect=false; - }, function(_, code, response) - if not code or code == 0 then reject("request failed"); return end - if code>=300 and code<400 and response.headers.location then - redirects = redirects + 1 - local location = response.headers.location - local parsed = socket_url.parse(current_url) - if not location:match("^https?://") then - location = parsed.scheme.."://"..parsed.host..(parsed.port and ":"..parsed.port or "")..location - end - current_url = location - step() - return - end - resolve(current_url) - end) - end - - step() - end) -end - -local function all_safe(resolv, didnext, net, ip) - if didnext and not ip then - return promise.resolve(true) - end + local tmpname = storage_path.."/tmp_"..tostring(math.random(1e9)) + local file = io.open(tmpname,"wb") + if not file then return promise.reject("cannot open temp file") end - if not didnext or (net == "tcp4" and not is_private_ipv4(ip)) - or (net == "tcp6" and not is_private_ipv6(ip)) then - return promise.new(function(resolve) - resolv:next(function(net,ip,port,extra) - all_safe(resolv, true, net, ip):next(resolve) - end) - end) - else - return promise.resolve(false) - end -end - -local function secure_download_to_disk(original_url) - return resolve_redirects(original_url):next(function(final_url) - local parsed = socket_url.parse(final_url) - if not parsed or (parsed.scheme~="http" and parsed.scheme~="https") then return promise.reject("invalid scheme") end - - return all_safe(resolver.new(parsed.host, 443, "tcp", { servername = parsed.host })):next(function(is_all_safe) - if not is_all_safe then return promise.reject("no safe IP") end - - local tmpname = storage_path.."/tmp_"..tostring(math.random(1e9)) - local file = io.open(tmpname,"wb") - if not file then return promise.reject("cannot open temp file") end - - return promise.new(function(resolve, reject) - http.request(final_url,{ - method = "GET"; - redirect = false; - body_size_limit = max_size; - streaming_handler = function(r) return file, function() file:close(); file = nil end end; - }, function(_, code, response) - if code ~= 200 then - if file then file:close() end - os.remove(tmpname) - reject("bad status") - return - end - if file then - file:write(response.body) - file:close() - end - resolve({ tmpname, response.headers["content-type"] }) - end) - end) + return promise.new(function(resolve, reject) + http.request(url,{ + method = "GET"; + body_size_limit = max_size; + streaming_handler = function(r) return file, function() file:close(); file = nil end end; + target_filter = function(net, ip, port, extra) + return (net == "tcp4" and not is_private_ipv4(ip)) or (net == "tcp6" and not is_private_ipv6(ip)) + end; + }, function(_, code, response) + if code ~= 200 then + if file then file:close() end + os.remove(tmpname) + reject("bad status") + return + end + if file then + file:write(response.body) + file:close() + end + resolve({ tmpname, response.headers["content-type"] }) end) end) end
