You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
3.4 KiB
107 lines
3.4 KiB
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
|