diff --git a/vrel.lua b/vrel.lua index 4fc3c03..125a488 100644 --- a/vrel.lua +++ b/vrel.lua @@ -4,7 +4,7 @@ math.randomseed(os.time()) local hasConfigFile, config = pcall(dofile, "config.lua") if not hasConfigFile then config = {} end -- Basic HTTP 1.0 server -- -local httpd, requestMaxDataSize = nil, config.requestMaxDataSize or 10485760 -- max post/paste data size (bytes) (10MB) +local httpd, requestMaxDataSize = nil, config.requestMaxDataSize or 5242880 -- max post/paste data size (bytes) (5MB) httpd = { log = function(str, ...) print("["..os.date().."] "..str:format(...)) end, -- log a message (str:format(...)) peername = function(client) return ("%s:%s"):format(client:getpeername()) end, -- returns a nice display name for the client (address:port) @@ -25,7 +25,7 @@ httpd = { version = "HTTP/1.0", -- HTTP version string headers = {}, -- headers table: {headerName=headerValue,...} (strings) body = "", -- request body - post = {}, -- POST args {argName=argValue,...} (strings) + post = { mimetype = {} }, -- POST args {argName=argValue,...,mimetype={argName=argMimeType}} (strings) get = {} -- GET args {argName=argValue,...} (strings) } local lines = {} -- Headers @@ -48,11 +48,9 @@ httpd = { if request.headers["Content-Type"]:match("multipart%/form%-data") then local boundary = request.headers["Content-Type"]:match("multipart%/form%-data%; boundary%=([^;]+)"):gsub("%p", "%%%1") for part in request.body:match("%-%-"..boundary.."(.*)"):gmatch("\r\n(.-)\r\n%-%-"..boundary) do - for l in part:gmatch("(.-)\r\n") do -- parse part headers - local name, value = l:match("^(.-)%:%s(.*)$") - if name == "Content-Disposition" then request.post[value:match("form%-data; name%=\"([^;\"]+)\"")] = part:match("\r\n\r\n(.*)") break - elseif name == nil or #l == 0 then break end - end + local headers, content = part:match("(.-\r\n)\r\n(.*)") + local name = headers:match("Content%-Disposition:%sform%-data; name%=\"([^\";\r]-)\"") + request.post[name], request.post.mimetype[name] = content, headers:match("Content%-Type:%s(.-)\r\n") end else request.post = httpd.parseUrlEncoded(request.body) end -- application/x-www-form-urlencoded end @@ -147,16 +145,17 @@ local data = {} -- { ["name"] = { expire = os.time()+lifetime, burnOnRead = fals local sqliteAvailable, sqlite3 = pcall(require, "lsqlite3") if sqliteAvailable then httpd.log("Using SQlite3 storage backend") -- SQlite backend local db = sqlite3.open("database.sqlite3") - db:exec("CREATE TABLE IF NOT EXISTS data (name STRING PRIMARY KEY NOT NULL UNIQUE, expire INTEGER NOT NULL, burnOnRead INTEGER NOT NULL DEFAULT 0, senderId STRING NOT NULL, syntax STRING NOT NULL DEFAULT 'text', data STRING NOT NULL)") + db:exec("CREATE TABLE IF NOT EXISTS data (name TEXT PRIMARY KEY NOT NULL UNIQUE, expire INTEGER NOT NULL, burnOnRead INTEGER NOT NULL DEFAULT 0, senderId TEXT NOT NULL, syntax TEXT NOT NULL DEFAULT 'text', ".. + "mimetype TEXT NOT NULL DEFAULT 'text/plain; charset=utf-8', data BLOB NOT NULL)") setmetatable(data, { __index = function(self, key) -- data[name]: get paste { expire = integer, burnOnRead = boolean, data = string } - local stmt = db:prepare("SELECT expire, burnOnRead, senderId, syntax, data FROM data WHERE name = ?") stmt:bind_values(key) + local stmt = db:prepare("SELECT expire, burnOnRead, senderId, syntax, mimetype, data FROM data WHERE name = ?") stmt:bind_values(key) local r for row in stmt:nrows() do r = row r.burnOnRead = r.burnOnRead == 1 break end stmt:finalize() return r end, __newindex = function(self, key, value) if value ~= nil then -- data[name] = { expire = integer, burnOnRead = boolean, syntax = string, data = string }: add paste - local stmt = db:prepare("INSERT INTO data VALUES (?, ?, ?, ?, ?, ?)") stmt:bind_values(key, value.expire, value.burnOnRead, value.senderId, value.syntax, value.data) stmt:step() stmt:finalize() + local stmt = db:prepare("INSERT INTO data VALUES (?, ?, ?, ?, ?, ?, ?)") stmt:bind_values(key, value.expire, value.burnOnRead, value.senderId, value.syntax, value.mimetype, value.data) stmt:step() stmt:finalize() else local stmt = db:prepare("DELETE FROM data WHERE name = ?") stmt:bind_values(key) stmt:step() stmt:finalize() end -- data[name] = nil: delete paste end, __clean = function(self, time) -- clean database @@ -168,7 +167,7 @@ else httpd.log("Using in-memory storage backend") -- In-memory (table) backend setmetatable(data, { __clean = function(self, time) for name, d in pairs(self) do if d.expire < time then self[name] = nil end end end }) end -- Helpers functions -local forbiddenName = { ["g"] = true, ["p"] = true } +local forbiddenName = { ["g"] = true, ["t"] = true, ["p"] = true } local function generateName(size) -- generate a paste name. If size ~= nil, will generate a random ID of this lenght. local name = "" repeat @@ -199,9 +198,8 @@ local function post(paste, request) clean() -- add a paste, will check data and local name = generateName() if paste.lifetime then paste.expire = os.time() + (tonumber(paste.lifetime) or defaultLifetime) end paste.expire = math.min(tonumber(paste.expire) or os.time()+defaultLifetime, os.time()+maxLifetime) - paste.burnOnRead = paste.burnOnRead == true - paste.senderId = paste.senderId or getClientId(request) or "unknown" - paste.syntax = (paste.syntax or "text"):lower():match("[a-z]*") + paste.burnOnRead, paste.senderId = paste.burnOnRead == true, paste.senderId or getClientId(request) or "unknown" + paste.syntax, paste.mimetype = (paste.syntax or (paste.mimetype or ""):match("text%/x?%-?(%a+)") or "text"):lower():match("[a-z]*"), paste.mimetype or "text/plain; charset=utf-8" paste.data = tostring(paste.data) data[name] = paste return name, data[name] @@ -225,7 +223,7 @@ httpd.start(config.address or "*", config.port or 8155, { -- Pages if #name == 0 then return { cache = config.cacheDuration or 3600, "200 OK", {["Content-Type"] = "text/html"}, [[