From 1a93f626e6c0783b1a7e8a31650ca560f068c1b3 Mon Sep 17 00:00:00 2001 From: V1K1NGbg Date: Thu, 22 Aug 2024 17:48:56 -0300 Subject: Teal: convert luarocks.upload.multipart --- src/luarocks/upload/multipart.lua | 109 ----------------------------------- src/luarocks/upload/multipart.tl | 118 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 109 deletions(-) delete mode 100644 src/luarocks/upload/multipart.lua create mode 100644 src/luarocks/upload/multipart.tl (limited to 'src') diff --git a/src/luarocks/upload/multipart.lua b/src/luarocks/upload/multipart.lua deleted file mode 100644 index 790e368f..00000000 --- a/src/luarocks/upload/multipart.lua +++ /dev/null @@ -1,109 +0,0 @@ - -local multipart = {} - -local File = {} - -local unpack = unpack or table.unpack - --- socket.url.escape(s) from LuaSocket 3.0rc1 -function multipart.url_escape(s) - return (string.gsub(s, "([^A-Za-z0-9_])", function(c) - return string.format("%%%02x", string.byte(c)) - end)) -end - -function File:mime() - if not self.mimetype then - local mimetypes_ok, mimetypes = pcall(require, "mimetypes") - if mimetypes_ok then - self.mimetype = mimetypes.guess(self.fname) - end - self.mimetype = self.mimetype or "application/octet-stream" - end - return self.mimetype -end - -function File:content() - local fd = io.open(self.fname, "rb") - if not fd then - return nil, "Failed to open file: "..self.fname - end - local data = fd:read("*a") - fd:close() - return data -end - -local function rand_string(len) - local shuffled = {} - for i = 1, len do - local r = math.random(97, 122) - if math.random() >= 0.5 then - r = r - 32 - end - shuffled[i] = r - end - return string.char(unpack(shuffled)) -end - --- multipart encodes params --- returns encoded string,boundary --- params is an a table of tuple tables: --- params = { --- {key1, value2}, --- {key2, value2}, --- key3: value3 --- } -function multipart.encode(params) - local tuples = { } - for i = 1, #params do - tuples[i] = params[i] - end - for k,v in pairs(params) do - if type(k) == "string" then - table.insert(tuples, {k, v}) - end - end - local chunks = {} - for _, tuple in ipairs(tuples) do - local k,v = unpack(tuple) - k = multipart.url_escape(k) - local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' } - local content - if type(v) == "table" and v.__class == File then - buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*[/\\]", "") .. '"') - table.insert(buffer, "Content-type: " .. v:mime()) - content = v:content() - else - content = v - end - table.insert(buffer, "") - table.insert(buffer, content) - table.insert(chunks, table.concat(buffer, "\r\n")) - end - local boundary - while not boundary do - boundary = "Boundary" .. rand_string(16) - for _, chunk in ipairs(chunks) do - if chunk:find(boundary) then - boundary = nil - break - end - end - end - local inner = "\r\n--" .. boundary .. "\r\n" - return table.concat({ "--", boundary, "\r\n", - table.concat(chunks, inner), - "\r\n", "--", boundary, "--", "\r\n" }), boundary -end - -function multipart.new_file(fname, mime) - local self = {} - setmetatable(self, { __index = File }) - self.__class = File - self.fname = fname - self.mimetype = mime - return self -end - -return multipart - diff --git a/src/luarocks/upload/multipart.tl b/src/luarocks/upload/multipart.tl new file mode 100644 index 00000000..7fcf73b5 --- /dev/null +++ b/src/luarocks/upload/multipart.tl @@ -0,0 +1,118 @@ + +local record multipart + -- record Parameters + -- {{string, (string | File)}} + -- map: {string: (string | File)} + -- end + type Parameters = {string: (string | File)} + + record File + mimetype: string + fname: string + mime: function(File): string + end +end + +local type Parameters = multipart.Parameters +local type File = multipart.File + +-- socket.url.escape(s) from LuaSocket 3.0rc1 --? +function multipart.url_escape(s: string): string + return (string.gsub(s, "([^A-Za-z0-9_])", function(c: string): string + return string.format("%%%02x", string.byte(c)) + end)) +end + +function multipart.File:mime(): string + if not self.mimetype then + local mimetypes_ok, mimetypes = pcall(require, "mimetypes") + if mimetypes_ok then + self.mimetype = mimetypes.guess(self.fname) + end + self.mimetype = self.mimetype or "application/octet-stream" + end + return self.mimetype +end + +function multipart.File:content(): string, string + local fd = io.open(self.fname, "rb") + if not fd then + return nil, "Failed to open file: "..self.fname + end + local data = fd:read("*a") + fd:close() + return data +end + +local function rand_string(len: integer): string + local shuffled: {integer} = {} + for i = 1, len do + local r = math.random(97, 122) + if math.random() >= 0.5 then + r = r - 32 + end + shuffled[i] = r + end + return string.char(table.unpack(shuffled)) +end + +-- multipart encodes params +-- returns encoded string,boundary +-- params is an a table of tuple tables: +-- params = { +-- {key1, value2}, +-- {key2, value2}, +-- key3: value3 +-- } +function multipart.encode(params: Parameters): string, string + local tuples: {{string, string | File}} = {} + for k,v in pairs(params) do + if k is string then + table.insert(tuples, {k, v}) + end + end + local chunks: {string} = {} + for _, tuple in ipairs(tuples) do + local k,v = table.unpack(tuple) + k = multipart.url_escape(k) + local buffer: {string} = { 'Content-Disposition: form-data; name="' .. k .. '"' } + local content: string + if v is File then + buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*[/\\]", "") .. '"') + table.insert(buffer, "Content-type: " .. v:mime()) + content = v:content() + else + content = v + end + table.insert(buffer, "") + table.insert(buffer, content) + table.insert(chunks, table.concat(buffer, "\r\n")) + end + local boundary: string + while not boundary do + boundary = "Boundary" .. rand_string(16) + for _, chunk in ipairs(chunks) do + if chunk:find(boundary) then + boundary = nil + break + end + end + end + local inner = "\r\n--" .. boundary .. "\r\n" + return table.concat({ "--", boundary, "\r\n", + table.concat(chunks, inner), + "\r\n", "--", boundary, "--", "\r\n" }), boundary +end + +function multipart.new_file(fname: string, mime?: string): File + local self: File = {} + + setmetatable(self, { __index = File }) + + self.fname = fname + self.mimetype = mime + return self +end + +return multipart + -- cgit v1.2.3-55-g6feb