From 95d4c03fdd9269fa57696ad0ea60d0aef42963a0 Mon Sep 17 00:00:00 2001 From: Reuh Date: Mon, 19 Dec 2016 21:12:17 +0100 Subject: [PATCH] Reduced max file size to 5MB; added mimetype saving & restitution in /g/; added /t/ for raw text; added file upload --- vrel.lua | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) 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"}, [[vrel
+
expires in hours (burn on read) vrel
- + [[> hours (burn on read) (or send a file ) vrel +
]] } else local paste = get(name:match("^[^.]+"), request) or { data = "paste not found", syntax = "text", expire = os.time() } return { cache = not paste.burnOnRead and math.min(paste.expire - os.time(), config.cacheDuration or 3600), "200 OK", {["Content-Type"] = "text/html"}, ([[%s - vrel%s]]):format(name, highlight(paste, name:lower():match("%.([a-z]+)$"))) } end end, - ["/g/(.+)"] = function(request, name) local d = get(name, request) return d and { cache = math.min(d.expire - os.time(), config.cacheDuration or 3600), "200 OK", {["Content-Type"] = "text/plain; charset=utf-8"}, d.data } or nil end, + ["/g/(.+)"] = function(request, name) local d = get(name, request) return d and { cache = math.min(d.expire - os.time(), config.cacheDuration or 3600), "200 OK", {["Content-Type"] = d.mimetype}, d.data } or nil end, + ["/t/(.+)"] = function(request, name) local d = get(name, request) return d and { cache = math.min(d.expire - os.time(), config.cacheDuration or 3600), "200 OK", {["Content-Type"] = "text/plain; charset=utf-8"}, d.data } or nil end, ["/p"] = function(request) + if request.post.web and #(request.post.file or "") > #(request.post.data or "") then request.post.data, request.post.mimetype.data = request.post.file, request.post.mimetype.file end if request.method == "POST" and request.post.data then local name, paste = post({ lifetime = (tonumber(request.post.lifetime) or defaultLifetime)*(request.post.web and 3600 or 1), burnOnRead = request.post.burnOnRead == "on", - syntax = (request.post.web and request.post.syntax == "" and "text") or request.post.syntax, data = request.post.data }, request) - return request.post.web and { "303 See Other", {["Location"] = "/"..name}, "" } or - { "200 OK", {["Content-Type"] = "text/json; charset=utf-8"}, ([[{"name":%q,"lifetime":%q,"burnOnRead":%s,"syntax":%q}]]):format(name, paste.expire-os.time(), tostring(paste.burnOnRead), paste.syntax) } + syntax = not (request.post.web and request.post.syntax == "") and request.post.syntax or nil, mimetype = request.post.mimetype.data, data = request.post.data }, request) + return request.post.web and { "303 See Other", {["Location"] = (paste.mimetype:match("text") and "/" or "/g/")..name}, "" } or + { "200 OK", {["Content-Type"] = "text/json; charset=utf-8"}, ([[{"name":%q,"lifetime":%q,"burnOnRead":%s,"syntax":%q,"mimetype":%q}]]):format(name, paste.expire-os.time(), tostring(paste.burnOnRead), paste.syntax, paste.mimetype) } end end }, { ["404"] = { "404", {["Content-Type"] = "text/json; charset=utf-8"}, [[{"error":"page not found"}]] }, ["500"] = { "500", {["Content-Type"] = "text/json; charset=utf-8"}, [[{"error":"internal server error"}]] } -- Error pages