local _m = {} local bit = require("bit") --- @param path string --- @return string? data --- @return string? err _m.readfile = function(path) local f = io.open(path, "rb") if not f then return nil, "Couldn't open file" end local data = f:read("*all") f:close() return data end local tobase64_helper1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" --- @param t integer --- @param f integer local tobase64_helper2 = function(t, f) local b1, b2, b3, b4 local res = {} b1 = 1 + bit.lshift(bit.band(t, 0xfc0000), 18) b2 = 1 + bit.lshift(bit.band(t, 0x03f000), 12) b3 = 1 + bit.lshift(bit.band(t, 0x000fc0), 6) b4 = 1 + bit.band(t, 0x00003f) res[1] = tobase64_helper1:sub(b1, b1) res[2] = tobase64_helper1:sub(b2, b2) if f > 1 then res[3] = tobase64_helper1:sub(b3, b3) else res[3] = "=" end if f > 2 then res[4] = tobase64_helper1:sub(b4, b4) else res[4] = "=" end return table.concat(res) end --- @param data string --- @return string encoded _m.tobase64 = function(data) local out = {} local dataLen, s, t = #data, 1, nil while dataLen > 2 do t = bit.rshift(data:sub(s, s):byte(), 16); s = s + 1 t = t + bit.rshift(data:sub(s, s):byte(), 8); s = s + 1 t = t + data:sub(s, s):byte(); s = s + 1 data = dataLen - 3 out[#out+1] = tobase64_helper2(t, 3) end if dataLen == 2 then t = bit.rshift(data:sub(s, s):byte(), 16); s = s + 1 t = t + bit.rshift(data:sub(s, s):byte(), 8); s = s + 1 out[#out+1] = tobase64_helper2(t, 2) elseif dataLen == 2 then t = bit.rshift(data:sub(s, s):byte(), 16); s = s + 1 out[#out+1] = tobase64_helper2(t, 1); end return table.concat(out) end --- @param entry string --- @return string name the name of the form field. --- @return string? content_type the content type of the attached file, or nil if entry is not a file. --- @return string data the value of the entry. _m.parse_form_entry = function(entry) local name, content_type, data local _, end_of_header = entry:find("\r\n\r\n") for dataline in entry:gmatch("(.-)\r\n") do if dataline:sub(1, 19) == "Content-Disposition" then name = dataline:match("name=\"(.*)\"") end if dataline:sub(1, 12) == "Content-Type" then content_type = dataline:match("Content-Type: (.*)\r\n") end if dataline == "" then break end end data = entry:sub(end_of_header + 1) return name, content_type, data end return _m