smr/src/lua/endpoints/paste_post.lua

209 lines
6.3 KiB
Lua

local sql = require("lsqlite3")
local zlib = require("zlib")
local util = require("util")
local parsers = require("parsers")
local config = require("config")
local queries = require("queries")
local db = require("db")
local cache = require("cache")
local tags = require("tags")
local session = require("session")
local pages = require("pages")
local stmnt_raw,stmnt_paste
local oldconfigure = configure
function configure(...)
stmnt_paste = assert(db.conn:prepare(queries.insert_post),db.conn:errmsg())
stmnt_raw = assert(db.conn:prepare(queries.insert_raw),db.conn:errmsg())
return oldconfigure(...)
end
local function get_random_bytes(n)
local f = assert(io.open("/dev/urandom","r"))
local ret = assert(f:read(n))
assert(f:close())
return ret
end
local function anon_paste(req,ps)
--Public paste
--[[
This doesn't actually do much for IPv4 addresses,
since there are only 32 bits of address. Someone who
got a copy of the database could
just generate all 2^32 hashes and look up who posted
what. Use IPv6, Tor or I2P where possible. (but then I
guess it's harder to ban spammers... hmm..)
]]
--local ip = http_request_get_ip(req)
--local iphash = sha3(ip)
--Don't store this information for now, until I come up
--with a more elegent solution.
log(LOG_DEBUG,string.format("new story: %q, length: %d",ps.title,string.len(ps.text)))
print("Unlisted:",ps.unlisted)
local textsha3 = sha3(ps.text .. get_random_bytes(32))
util.sqlbind(stmnt_paste,"bind_blob",1,ps.text)
util.sqlbind(stmnt_paste,"bind",2,ps.title)
util.sqlbind(stmnt_paste,"bind",3,-1)
util.sqlbind(stmnt_paste,"bind",4,true)
util.sqlbind(stmnt_paste,"bind_blob",5,"")
util.sqlbind(stmnt_paste,"bind",6,ps.unlisted)
util.sqlbind(stmnt_paste,"bind_blob",7,textsha3)
err = util.do_sql(stmnt_paste)
stmnt_paste:reset()
if err == sql.DONE then
local rowid = stmnt_paste:last_insert_rowid()
local url = util.encode_id(rowid)
if ps.unlisted then
url = url .. "?pwd=" .. util.encode_unlisted(textsha3)
end
assert(stmnt_raw:bind(1,rowid) == sql.OK)
assert(stmnt_raw:bind_blob(2,ps.raw) == sql.OK)
assert(stmnt_raw:bind(3,ps.markup) == sql.OK)
err = util.do_sql(stmnt_raw)
stmnt_raw:reset()
if err ~= sql.DONE then
local msg = string.format(
[[Failed to save raw text for %d(%s) but paste still went though: %d: %s]],
rowid,
url,
err,
db.conn:errmsg()
)
log(LOG_CRIT,msg)
end
tags.set(rowid,ps.tags)
local loc = string.format("https://%s/%s",config.domain,url)
if not ps.unlisted then
cache.dirty(string.format("%s/%s",config.domain,url))
cache.dirty(string.format("%s",config.domain))
end
http_response_header(req,"Location",loc)
http_response(req,303,"")
return
elseif err == sql.ERROR or err == sql.MISUSE then
error("Failed to paste:" .. tostring(err))
else
error("Error pasting:" .. tostring(err))
end
stmnt_paste:reset()
end
local function author_paste(req,ps)
--Author paste
local author, authorid = session.get(req)
local ret
if author == nil then
ret = pages.author_paste{
domain = config.domain,
author = ps.subdomain,
err = "You are not logged in, you must be logged in to post as " .. ps.subdomain .. ".",
text = ps.text
}
end
local asanon = assert(http_argument_get_string(req,"pasteas"))
local textsha3 = sha3(ps.text .. get_random_bytes(32))
--No need to check if the author is posting to the
--"right" sudomain, just post it to the one they have
--the session key for.
assert(stmnt_paste:bind_blob(1,ps.text) == sql.OK)
assert(stmnt_paste:bind(2,ps.title) == sql.OK)
assert(stmnt_paste:bind(3,authorid) == sql.OK)
assert(stmnt_paste:bind(4,asanon == "anonymous") == sql.OK)
assert(stmnt_paste:bind_blob(5,"") == sql.OK)
util.sqlbind(stmnt_paste,"bind",6,ps.unlisted)
util.sqlbind(stmnt_paste,"bind_blob",7,textsha3)
local err = util.do_sql(stmnt_paste)
stmnt_paste:reset()
if err == sql.DONE then
local rowid = stmnt_paste:last_insert_rowid()
local url = util.encode_id(rowid)
if ps.unlisted then
url = url .. "?pwd=" .. util.encode_unlisted(textsha3)
end
assert(stmnt_raw:bind(1,rowid) == sql.OK)
assert(stmnt_raw:bind_blob(2,ps.raw) == sql.OK)
assert(stmnt_raw:bind(3,ps.markup) == sql.OK)
err = util.do_sql(stmnt_raw)
stmnt_raw:reset()
if err ~= sql.DONE then
local msg = string.format(
[[Failed to save raw text for %d(%s) but paste still went though: %d: %s]],
rowid,
url,
err,
db.conn:errmsg()
)
log(LOG_CRIT,msg)
end
tags.set(rowid,ps.tags)
local loc
if asanon == "anonymous" then
loc = string.format("https://%s/%s",config.domain,url)
else
loc = string.format("https://%s.%s/%s",author,config.domain,url)
end
if not ps.unlisted then
cache.dirty(string.format("%s.%s",author,config.domain))
cache.dirty(string.format("%s/%s",config.domain,url))
cache.dirty(string.format("%s",config.domain))
end
http_response_header(req,"Location",loc)
http_response(req,303,"")
return
elseif err == sql.ERROR or err == sql.MISUSE then
error("Failed to paste: " .. tostring(err) .. " : " .. db.conn:errmsg())
else
error("Error pasting:",err)
end
stmnt_paste:reset()
end
local function decodeentities(capture)
local n = tonumber(capture,16)
local c = string.char(n)
if escapes[c] then
return escapes[c]
else
return c
end
end
local function paste_post(req)
local host = http_request_get_host(req)
local path = http_request_get_path(req)
local ps = {}
--We're creating a new paste
ps.subdomain = host:match("([^\\.]+)")
http_request_populate_post(req)
local title = assert(http_argument_get_string(req,"title"))
local text = assert(http_argument_get_string(req,"text"))
ps.markup = assert(http_argument_get_string(req,"markup"))
local tag_str = http_argument_get_string(req,"tags")
ps.tags = {}
if tag_str then
ps.tags = util.parse_tags(tag_str)
end
local pasteas
ps.raw = zlib.compress(text)
text = util.decodeentities(text)
text = parsers[ps.markup](text)
assert(text,"Failed to parse text")
text = zlib.compress(text)
assert(text,"Failed to compress text")
ps.text = text
--Always sanatize the title with the plain parser. no markup
--in the title.
ps.title = parsers.plain(title)
local unlisted = http_argument_get_string(req,"unlisted")
ps.unlisted = unlisted == "on" --might be nil
if host == config.domain then
anon_paste(req,ps)
else
author_paste(req,ps)
end
end
return paste_post