From 85a730ebcb0631f628c2eaf700a7c3433a8c97ce Mon Sep 17 00:00:00 2001 From: Robin Malley Date: Sun, 10 Jan 2021 21:34:13 +0000 Subject: [PATCH] started working on unlisted posts --- assets/style.css | 3 ++- conf/smr.conf.in | 6 +++++ src/lua/endpoints/edit_post.lua | 4 ++++ src/lua/endpoints/paste_post.lua | 37 +++++++++++++++++++++++++------ src/lua/endpoints/read_get.lua | 30 +++++++++++++++++++++---- src/lua/util.lua | 21 +++++++++++++++++- src/pages/.gitignore | 14 ++++++++++++ src/pages/author_paste.etlua.in | 6 ++++- src/pages/paste.etlua.in | 6 ++++- src/sql/create_index_unlisted.sql | 1 + src/sql/create_table_posts.sql | 6 ++++- src/sql/insert_post.sql | 4 ++++ src/sql/select_author_index.sql | 4 ++-- src/sql/select_download.sql | 3 ++- src/sql/select_post.sql | 7 +++++- src/sql/select_site_index.sql | 5 +++-- src/sql/update_post.sql | 1 + 17 files changed, 136 insertions(+), 22 deletions(-) create mode 100644 src/pages/.gitignore create mode 100644 src/sql/create_index_unlisted.sql diff --git a/assets/style.css b/assets/style.css index 31bec02..77be118 100644 --- a/assets/style.css +++ b/assets/style.css @@ -29,7 +29,8 @@ p,.tag-list{margin-bottom:0px} flex:10 10 auto; translate: -100%; } -.column-0{margin-right:5px;} +.column-0{margin-right:5px} +.label-inline{margin:0.5rem} @media (prefers-color-scheme: dark){ body, input, select, textarea, pre, code{ diff --git a/conf/smr.conf.in b/conf/smr.conf.in index 5955320..6102105 100644 --- a/conf/smr.conf.in +++ b/conf/smr.conf.in @@ -25,6 +25,8 @@ validator v_storyid regex [a-zA-Z0-9$+!*'(),-]+ validator v_subdomain regex [a-z0-9]{1,30} validator v_markup regex (plain|imageboard) validator v_bool regex (0|1) +validator v_checkbox regex (|on) +validator v_hex_128 regex [0-9a-f]{128} domain * { attach tls @@ -65,6 +67,7 @@ domain * { validate pasteas v_subdomain validate markup v_markup validate tags v_any + validate unlisted v_checkbox } params post /_paste { validate title v_any @@ -72,6 +75,7 @@ domain * { validate pasteas v_subdomain validate markup v_markup validate tags v_any + validate unlisted v_checkbox } params post /_preview { validate title v_any @@ -79,12 +83,14 @@ domain * { validate pasteas v_subdomain validate markup v_markup validate tags v_any + validate unlisted v_checkbox } params get /_search { validate q v_any } params get ^/[^_].* { validate comments v_bool + validate pwd v_hex_128 } params post ^/[^_].* { validate text v_any diff --git a/src/lua/endpoints/edit_post.lua b/src/lua/endpoints/edit_post.lua index 659785b..b458462 100644 --- a/src/lua/endpoints/edit_post.lua +++ b/src/lua/endpoints/edit_post.lua @@ -32,6 +32,7 @@ local function edit_post(req) local text = assert(http_argument_get_string(req,"text")) local pasteas = assert(http_argument_get_string(req,"pasteas")) local markup = assert(http_argument_get_string(req,"markup")) + local unlisted = http_argument_get_string(req,"unlisted") == "on" local tags_str = http_argument_get_string(req,"tags") stmnt_author_of:bind_names{ id = storyid @@ -61,11 +62,14 @@ local function edit_post(req) assert(stmnt_update:bind_blob(2,compr) == sql.OK) assert(stmnt_update:bind(3,pasteas == "anonymous" and 1 or 0) == sql.OK) assert(stmnt_update:bind(4,storyid) == sql.OK) + assert(stmnt_update:bind(5,unlisted) == sql.OK) assert(util.do_sql(stmnt_update) == sql.DONE, "Failed to update text") stmnt_update:reset() tagslib.set(storyid,tags) local id_enc = util.encode_id(storyid) local loc = string.format("https://%s/%s",config.domain,id_enc) + --Turning something from not unlisted to unlisted should dirty all these + --places anyway, so the post can now be hidden. cache.dirty(string.format("%s/%s",config.domain,id_enc)) -- This place to read this post cache.dirty(string.format("%s",config.domain)) -- The site index (ex, if the author changed the paste from their's to "Anonymous", the cache should reflect that). cache.dirty(string.format("%s.%s",author,config.domain)) -- The author's index, same reasoning as above. diff --git a/src/lua/endpoints/paste_post.lua b/src/lua/endpoints/paste_post.lua index 5ab3c41..2283612 100644 --- a/src/lua/endpoints/paste_post.lua +++ b/src/lua/endpoints/paste_post.lua @@ -14,11 +14,18 @@ local stmnt_raw,stmnt_paste local oldconfigure = configure function configure(...) - stmnt_paste = assert(db.conn:prepare(queries.insert_post)) - stmnt_raw = assert(db.conn:prepare(queries.insert_raw)) + 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 --[[ @@ -34,16 +41,24 @@ local function anon_paste(req,ps) --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) @@ -61,10 +76,12 @@ local function anon_paste(req,ps) 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,"") - cache.dirty(string.format("%s/%s",config.domain,url)) - cache.dirty(string.format("%s",config.domain)) return elseif err == sql.ERROR or err == sql.MISUSE then error("Failed to paste:" .. tostring(err)) @@ -93,6 +110,8 @@ local function author_paste(req,ps) 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,sha3(ps.text)) err = util.do_sql(stmnt_paste) stmnt_paste:reset() if err == sql.DONE then @@ -120,11 +139,13 @@ local function author_paste(req,ps) 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,"") - 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)) return elseif err == sql.ERROR or err == sql.MISUSE then error("Failed to paste: " .. tostring(err) .. " : " .. db.conn:errmsg()) @@ -171,6 +192,8 @@ local function paste_post(req) --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 diff --git a/src/lua/endpoints/read_get.lua b/src/lua/endpoints/read_get.lua index 3dae457..26f1725 100644 --- a/src/lua/endpoints/read_get.lua +++ b/src/lua/endpoints/read_get.lua @@ -39,8 +39,10 @@ or nil if it wasn't local function populate_ps_story(req,ps) --Make sure our story exists stmnt_read:bind_names{ - id = ps.storyid + id = ps.storyid, } + print("populating, hash was:",ps.hash) + stmnt_read:bind(2,ps.hash or "") local err = util.do_sql(stmnt_read) if err == sql.DONE then --We got no story @@ -99,6 +101,7 @@ local function read_get(req) ps.storyid = util.decode_id(ps.idp) add_view(ps.storyid) + --If we're logged in, set author and authorid local author, authorid = session.get(req) if author and authorid then @@ -113,15 +116,34 @@ local function read_get(req) if ps.show_comments then ps.comments = get_comments(req,ps) end + + --If this post is unlisted, get the hash + local hashstr = http_argument_get_string(req,"pwd") + print("hashstr was:",hashstr) + if hashstr then + ps.hash = util.decode_unlisted(hashstr) + end + local text --normal story display if (not ps.loggedauthor) then - local cachestr = string.format("%s%s%s", + local params = {} + if ps.show_comments then + table.insert(params,"comments=1") + end + if ps.hash then + table.insert(params,"pwd=" .. hashstr) + end + local cachestrparts = { ps.host, ps.path, - ps.show_comments and "?comments=1" or "" - ) + } + if #params > 0 then + table.insert(cachestrparts,"?") + table.insert(cachestrparts,table.concat(params,"&")) + end + local cachestr = table.concat(cachestrparts) text = cache.render(cachestr,function() log(LOG_DEBUG,"Cache miss, rendering story " .. cachestr) if not populate_ps_story(req,ps) then diff --git a/src/lua/util.lua b/src/lua/util.lua index ca014e1..1a08589 100644 --- a/src/lua/util.lua +++ b/src/lua/util.lua @@ -1,6 +1,5 @@ local sql = require("lsqlite3") - local util = {} --[[ @@ -124,6 +123,26 @@ function util.decode_id(s) end end +--arbitary data to hex encoded string +function util.encode_unlisted(str) + local safe = {} + for i = 1,#str do + local byte = str:byte(i) + table.insert(safe,string.format("%02x",byte)) + end + return table.concat(safe) +end + +--hex encoded string to arbitrary data +function util.decode_unlisted(str) + print("str was:",str) + local output = {} + for byte in str:gmatch("%x%x") do + table.insert(output, string.char(tonumber(byte,16))) + end + return table.concat(output) +end + --[[ Parses a semicolon seperated string into it's parts: 1. seperates by semicolon diff --git a/src/pages/.gitignore b/src/pages/.gitignore new file mode 100644 index 0000000..8cda170 --- /dev/null +++ b/src/pages/.gitignore @@ -0,0 +1,14 @@ +author_edit.etlua +author_index.etlua +author_paste.etlua +cantedit.etlua +claim.etlua +edit.etlua +index.etlua +login.etlua +noauthor.etlua +nostory.etlua +paste.etlua +read.etlua +search.etlua +search_sql.etlua diff --git a/src/pages/author_paste.etlua.in b/src/pages/author_paste.etlua.in index e9a791b..2068110 100644 --- a/src/pages/author_paste.etlua.in +++ b/src/pages/author_paste.etlua.in @@ -6,7 +6,7 @@
- + +
+ + +
diff --git a/src/pages/paste.etlua.in b/src/pages/paste.etlua.in index 92a5f9f..1f693d2 100644 --- a/src/pages/paste.etlua.in +++ b/src/pages/paste.etlua.in @@ -5,11 +5,15 @@ <% if err then %><%= err %><% end %>
- + +
+ + +
diff --git a/src/sql/create_index_unlisted.sql b/src/sql/create_index_unlisted.sql new file mode 100644 index 0000000..57a9e76 --- /dev/null +++ b/src/sql/create_index_unlisted.sql @@ -0,0 +1 @@ +CREATE INDEX unlisted_index ON posts(hash); diff --git a/src/sql/create_table_posts.sql b/src/sql/create_table_posts.sql index 1f6f5dc..b898960 100644 --- a/src/sql/create_table_posts.sql +++ b/src/sql/create_table_posts.sql @@ -6,6 +6,8 @@ means that all comments by other users on a post an author makes will also be deleted. Post text uses zlib compression + +Unlisted hashes are SHAv3 521 */ CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, @@ -15,5 +17,7 @@ CREATE TABLE IF NOT EXISTS posts ( isanon INTEGER, hashedip BLOB, post_time INTEGER, - views INTEGER DEFAULT 0 + views INTEGER DEFAULT 0, + unlisted INTEGER, + hash BLOB ); diff --git a/src/sql/insert_post.sql b/src/sql/insert_post.sql index 9b86bf3..747feca 100644 --- a/src/sql/insert_post.sql +++ b/src/sql/insert_post.sql @@ -4,6 +4,8 @@ INSERT INTO posts ( authorid, isanon, hashedip, + unlisted, + hash, post_time ) VALUES ( ?, @@ -11,5 +13,7 @@ INSERT INTO posts ( ?, ?, ?, + ?, + ?, strftime('%s','now') ); diff --git a/src/sql/select_author_index.sql b/src/sql/select_author_index.sql index 7eecab9..9db8c84 100644 --- a/src/sql/select_author_index.sql +++ b/src/sql/select_author_index.sql @@ -11,7 +11,7 @@ FROM WHERE posts.isanon = 0 AND posts.authorid = authors.id AND - authors.name = :author + authors.name = :author AND + posts.unlisted = 0 ORDER BY posts.post_time DESC -LIMIT 10; diff --git a/src/sql/select_download.sql b/src/sql/select_download.sql index d45cb04..7cb727b 100644 --- a/src/sql/select_download.sql +++ b/src/sql/select_download.sql @@ -4,4 +4,5 @@ FROM raw_text, posts WHERE raw_text.id = posts.id AND - raw_text.id = :postid + raw_text.id = :postid AND + posts.unlisted = 0 diff --git a/src/sql/select_post.sql b/src/sql/select_post.sql index 9e36fdc..5e3cded 100644 --- a/src/sql/select_post.sql +++ b/src/sql/select_post.sql @@ -13,4 +13,9 @@ FROM posts,authors WHERE posts.authorid = authors.id AND - posts.id = :id; + posts.id = :id AND + ( + posts.unlisted = 1 AND + posts.hash = :hash + ) OR + posts.unlisted = 0; diff --git a/src/sql/select_site_index.sql b/src/sql/select_site_index.sql index 664fd17..88a39a1 100644 --- a/src/sql/select_site_index.sql +++ b/src/sql/select_site_index.sql @@ -10,7 +10,8 @@ FROM posts, authors WHERE - posts.authorid = authors.id + posts.authorid = authors.id AND + posts.unlisted = 0 ORDER BY posts.post_time DESC -LIMIT 10; +LIMIT 50; diff --git a/src/sql/update_post.sql b/src/sql/update_post.sql index 4838247..7e75732 100644 --- a/src/sql/update_post.sql +++ b/src/sql/update_post.sql @@ -4,6 +4,7 @@ SET post_title = ?, post_text = ?, isanon = ?, + unlisted = ?, post_time = strftime('%s','now') WHERE posts.id = ?;