From e346128d4d8e76aedb47a490b491c64205478319 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Wed, 19 Jan 2022 17:24:29 +0100 Subject: [PATCH] Add web.uri.encode[_component] --- test.lua | 6 ++++-- web.lua | 35 +++++++---------------------------- web/html.lua | 27 +++++++++++++++++++++++++++ web/uri.lua | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 web/html.lua create mode 100644 web/uri.lua diff --git a/test.lua b/test.lua index 5031a48..cf8cc09 100644 --- a/test.lua +++ b/test.lua @@ -367,8 +367,10 @@ do local text = " & '\"" local escaped = web.html.escape(text) assert(web.html.unescape(escaped) == text) - assert(web.html.unescape("*") == _G.string.char(42)) - assert(web.html.unescape("B") == _G.string.char(0x42)) + assert(web.html.unescape"*" == _G.string.char(42)) + assert(web.html.unescape"B" == _G.string.char(0x42)) + assert(web.uri.encode"https://example.com/foo bar" == "https://example.com/foo%20bar") + assert(web.uri.encode_component"foo/bar baz" == "foo%2Fbar%20baz") end if not _G.minetest then return end diff --git a/web.lua b/web.lua index dafcbd2..4a1ba97 100644 --- a/web.lua +++ b/web.lua @@ -1,28 +1,7 @@ --- TODO incorporate https://github.com/minetest/minetest/pull/11578 -return { - html = setmetatable({ - escape = function(text) - return text:gsub(".", { - ["<"] = "<", - [">"] = ">", - ["&"] = "&", - ["'"] = "'", - ['"'] = """, - }) - end - }, {__index = function(self, key) - if key == "unescape" then - local func = assert(loadfile(modlib.mod.get_resource("modlib", "web", "html", "entities.lua"))) - setfenv(func, {}) - local named_entities = assert(func()) - local function unescape(text) - return text - :gsub("&([A-Za-z]+);", named_entities) -- named - :gsub("&#(%d+);", function(digits) return modlib.text.utf8(tonumber(digits)) end) -- decimal - :gsub("&#x(%x+);", function(digits) return modlib.text.utf8(tonumber(digits, 16)) end) -- hex - end - self.unescape = unescape - return unescape - end - end}) -} \ No newline at end of file +return setmetatable({}, {__index = function(self, module_name) + if module_name == "uri" or module_name == "html" then + local module = assert(loadfile(modlib.mod.get_resource(modlib.modname, "web", module_name .. ".lua")))() + self[module_name] = module + return module + end +end}) \ No newline at end of file diff --git a/web/html.lua b/web/html.lua new file mode 100644 index 0000000..f1b9aad --- /dev/null +++ b/web/html.lua @@ -0,0 +1,27 @@ +local html = setmetatable({}, {__index = function(self, key) + if key == "unescape" then + local func = assert(loadfile(modlib.mod.get_resource("modlib", "web", "html", "entities.lua"))) + setfenv(func, {}) + local named_entities = assert(func()) + local function unescape(text) + return text + :gsub("&([A-Za-z]+);", named_entities) -- named + :gsub("&#(%d+);", function(digits) return modlib.text.utf8(tonumber(digits)) end) -- decimal + :gsub("&#x(%x+);", function(digits) return modlib.text.utf8(tonumber(digits, 16)) end) -- hex + end + self.unescape = unescape + return unescape + end +end}) + +function html.escape(text) + return text:gsub(".", { + ["<"] = "<", + [">"] = ">", + ["&"] = "&", + ["'"] = "'", + ['"'] = """, + }) +end + +return html \ No newline at end of file diff --git a/web/uri.lua b/web/uri.lua new file mode 100644 index 0000000..87e6aca --- /dev/null +++ b/web/uri.lua @@ -0,0 +1,42 @@ +-- URI escaping utilities +-- See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI + +local uri_unescaped_chars = {} +for char in ("-_.!~*'()"):gmatch(".") do + uri_unescaped_chars[char] = true +end +local function add_unescaped_range(from, to) + for byte = from:byte(), to:byte() do + uri_unescaped_chars[string.char(byte)] = true + end +end +add_unescaped_range("0", "9") +add_unescaped_range("a", "z") +add_unescaped_range("A", "Z") + +local uri_allowed_chars = table.copy(uri_unescaped_chars) +for char in (";,/?:@&=+$#"):gmatch(".") do + -- Reserved characters are allowed + uri_allowed_chars[char] = true +end + +local function encode(text, allowed_chars) + return text:gsub(".", function(char) + if allowed_chars[char] then + return char + end + return ("%%%02X"):format(char:byte()) + end) +end + +local uri = {} + +function uri.encode_component(text) + return encode(text, uri_unescaped_chars) +end + +function uri.encode(text) + return encode(text, uri_allowed_chars) +end + +return uri \ No newline at end of file