jbe/bsw@20: --[[-- jbe@320: url = -- normalized URL or nil jbe/bsw@20: auth.openid._normalize_url( jbe/bsw@20: url -- unnormalized URL jbe/bsw@20: ) jbe/bsw@20: jbe/bsw@20: This function normalizes an URL, and returns nil if the given URL is not a jbe/bsw@20: valid absolute URL. For security reasons only a restricted set of URLs is jbe/bsw@20: valid. jbe/bsw@20: jbe/bsw@20: --]]-- jbe/bsw@20: jbe/bsw@20: function auth.openid._normalize_url(url) jbe/bsw@20: local url = string.match(url, "^(.-)??$") -- remove "?" at end jbe/bsw@20: local proto, host, path = string.match( jbe/bsw@20: url, jbe/bsw@20: "([A-Za-z]+)://([0-9A-Za-z.:_-]+)/?([0-9A-Za-z%%/._~-]*)$" jbe/bsw@20: ) jbe/bsw@20: if not proto then jbe/bsw@20: return nil jbe/bsw@20: end jbe/bsw@20: proto = string.lower(proto) jbe/bsw@20: host = string.lower(host) jbe/bsw@20: local port = string.match(host, ":(.*)") jbe/bsw@20: if port then jbe/bsw@20: if string.find(port, "^[0-9]+$") then jbe/bsw@20: port = tonumber(port) jbe/bsw@20: host = string.match(host, "^(.-):") jbe/bsw@20: if port < 1 or port > 65535 then jbe/bsw@20: return nil jbe/bsw@20: end jbe/bsw@20: else jbe/bsw@20: return nil jbe/bsw@20: end jbe/bsw@20: end jbe/bsw@20: if proto == "http" then jbe/bsw@20: if port == 80 then port = nil end jbe/bsw@20: elseif proto == "https" then jbe/bsw@20: if port == 443 then port = nil end jbe/bsw@20: else jbe/bsw@20: return nil jbe/bsw@20: end jbe/bsw@20: if jbe/bsw@20: string.find(host, "^%.") or jbe/bsw@20: string.find(host, "%.$") or jbe/bsw@20: string.find(host, "%.%.") jbe/bsw@20: then jbe/bsw@20: return nil jbe/bsw@20: end jbe/bsw@20: for part in string.gmatch(host, "[^.]+") do jbe/bsw@20: if not string.find(part, "[A-Za-z]") then jbe/bsw@20: return nil jbe/bsw@20: end jbe/bsw@20: end jbe/bsw@20: local path_parts = {} jbe/bsw@20: for part in string.gmatch(path, "[^/]+") do jbe/bsw@20: if part == "." then jbe/bsw@20: -- do nothing jbe/bsw@20: elseif part == ".." then jbe/bsw@20: path_parts[#path_parts] = nil jbe/bsw@20: else jbe/bsw@20: local fail = false jbe/bsw@20: local part = string.gsub( jbe/bsw@20: part, jbe/bsw@20: "%%([0-9A-Fa-f]?[0-9A-Fa-f]?)", jbe/bsw@20: function (hex) jbe/bsw@20: if #hex ~= 2 then jbe/bsw@20: fail = true jbe/bsw@20: return jbe/bsw@20: end jbe/bsw@20: local char = string.char(tonumber("0x" .. hex)) jbe/bsw@20: if string.find(char, "[0-9A-Za-z._~-]") then jbe/bsw@20: return char jbe/bsw@20: else jbe/bsw@20: return "%" .. string.upper(hex) jbe/bsw@20: end jbe/bsw@20: end jbe/bsw@20: ) jbe/bsw@20: if fail then jbe/bsw@20: return nil jbe/bsw@20: end jbe/bsw@20: path_parts[#path_parts+1] = part jbe/bsw@20: end jbe/bsw@20: end jbe/bsw@20: if string.find(path, "/$") then jbe/bsw@20: path_parts[#path_parts+1] = "" jbe/bsw@20: end jbe/bsw@20: path = table.concat(path_parts, "/") jbe/bsw@20: if port then jbe/bsw@20: host = host .. ":" .. tostring(port) jbe/bsw@20: end jbe/bsw@20: return proto .. "://" .. host .. "/" .. path jbe/bsw@20: end