changeset 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 0703ef9f8db8
children 420aaf113272
files mod_muc_cache_media/mod_muc_cache_media.lua
diffstat 1 files changed, 26 insertions(+), 96 deletions(-) [+]
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