209 lines
6.3 KiB
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)))
|
|
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))
|
|
cache.dirty(string.format("%s-logout",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
|