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 --- @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 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 --- - Secondly 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. --- - Thirdly we assume a field name can't contain a double quote, even escaped --- --- Additionaly if the entry is bogus or something goes wrong the function may --- abort and return `"", nil, ""` instead. _m.parse_form_entry = function(entry) -- If an entry is less than 32 bytes, it's bogus, skip if #entry < 32 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:sub(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:sub(cursor), "^(.-)\r") cursor = cursor + #ctype while entry:sub(cursor, cursor) ~= "\r" do cursor = cursor + 1 end cursor = cursor + 2 --[[ CRLF ]] end -- If we didn't advance the cursor, something went very wrong, skip if cursor == oldcursor then print(entry) return "", nil, "" end end return name, ctype, entry:sub(cursor, -1) end return _m