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. --- We are doing some severe assumptions here. --- - Firstly we assume that if the first line of a header doesn't start with --- `Content-Disposition`, it is invalid and we can ignore it. --- - Secondly we assume that in the headers, any `CR` is always gonna be --- followed by a `LF` thus we only check for CR and advance by 2 when found --- - Thirdly we assume that the only headers that can matter are --- `Content-Disposition` (for the field name) and `Content-Type` (if this is a --- file upload for the type of the uploaded file. --- - Fourthly we assume a field name can't contain a double quote _m.parse_form_entry = function(entry) if #entry < 10 then return "", nil, "" end local cursor = 3 local name, ctype while true do local oldcursor = cursor if entry:sub(cursor, cursor) == "\r" then cursor = cursor + 2 break elseif entry:sub(cursor, cursor+18) == 'Content-Disposition' then cursor = cursor + 38 name = string.match(entry, "(.*)\"", cursor) cursor = cursor + #name + 1 --[[ the closing quote ]] -- Find the end of line while entry:sub(cursor, cursor) ~= "\r" do cursor = cursor + 1 end cursor = cursor + 2 elseif entry:sub(cursor, cursor+11) == 'Content-Type' then cursor = cursor + 14 ctype = string.match(entry, "(.*)\r", cursor) cursor = cursor + #ctype while entry:sub(cursor, cursor) ~= "\r" do cursor = cursor + 1 end cursor = cursor + 2 --[[ CRLF ]] end if cursor == oldcursor then print(entry) os.exit(1); end end return name, ctype, entry:sub(cursor, -1) end return _m