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