Add comments

Add comments for each paste,
Also add a "Download TXT" button.
This commit is contained in:
Robin Malley 2020-08-13 17:59:33 +00:00
parent 0ba9e22f70
commit bce0788b62
11 changed files with 322 additions and 109 deletions

View File

@ -3,8 +3,8 @@ chroot_dir=kore_chroot/
mirror=http://dl-cdn.alpinelinux.org/alpine/
arch=aarch64
version=2.10.5-r0
#certbot_email=--register-unsafely-without-email
certbot_email=-m you@cock.li
certbot_email=--register-unsafely-without-email
#certbot_email=-m you@cock.li
domain=test.monster
#Probably don't change stuff past here
@ -20,17 +20,18 @@ all: $(chroot_dir) smr.so $(built_files) $(built_pages) $(built_sql)
echo $(built_files)
kodev run
apk-tools-static-$(version).apk: Makefile
wget -q $(mirror)latest-stable/main/$(arch)/apk-tools-static-$(version).apk
apk-tools-static-$(version).apk:
# wget -q $(mirror)latest-stable/main/$(arch)/apk-tools-static-$(version).apk
clean:
kodev clean
$(chroot_dir): #apk-tools-static-$(version).apk
$(chroot_dir): apk-tools-static-$(version).apk
mkdir -p $(chroot_dir)
mkdir -p $(chroot_dir)/pages
mkdir -p $(chroot_dir)/sql
#cd $(chroot_dir) && tar -xvzf ../../apk-tools-static-*.apk
mkdir -p $(chroot_dir)/data
#cd $(chroot_dir) && tar -xvzf ../apk-tools-static-*.apk
#cd $(chroot_dir) && sudo ./sbin/apk.static -X $(mirror)latest-stable/main -U --allow-untrusted --root $(chroot_dir) --no-cache --initdb add alpine-base
#ln -s /dev/urandom $(chroot_dir)/dev/random #Prevent an attacker with access to the chroot from exhausting our entropy pool and causing a dos
#ln -s /dev/urandom $(chroot_dir)/dev/urandom
@ -39,13 +40,16 @@ $(chroot_dir): #apk-tools-static-$(version).apk
#mount -t proc none $(chroot_dir)/proc
#mount -o bind /sys $(chroot_dir)/sys
#cp /etc/resolv.conf $(chroot_dir)/etc/resolv.conf
#echo "$(mirror)/$(branch)/main" > $(chroot)/etc/apk/repositories
#echo "$(mirror)/$(branch)/community" >> $(chroot)/etc/apk/repositories
#cp /etc/apk/repositories $(chroot_dir)/etc/apk/repositories
#mkdir $(chroot_dir)/var/sm
## Things to build lua libraries
#chroot $(chroot_dir) apk add luarocks5.1 sqlite sqlite-dev lua5.1-dev build-base zlib zlib-dev
#chroot $(chroot_dir) luarocks-5.1 install etlua
#chroot $(chroot_dir) luarocks-5.1 install lsqlite3
#chroot $(chroot_dir) luarocks-5.1 install lzlib ZLIB_LIBDIR=/lib #for some reason lzlib looks in /usr/lib for libz, when it needs to look at /lib
#chroot $(chroot_dir) luarocks-5.1 install lpeg
#chroot $(chroot_dir) luarocks-5.1 install lua-zlib ZLIB_LIBDIR=/lib #for some reason lzlib looks in /usr/lib for libz, when it needs to look at /lib
## Once we've built + installed everything, delete extra stuff from the chroot
#chroot $(chroot_dir) apk del sqlite-dev lua5.1-dev build-base zlib-dev
## SSL certificates, if you don't trust EFF (they have an antifa black block member as their favicon at time of writing) you may want to replace this.
@ -56,6 +60,7 @@ $(chroot_dir): #apk-tools-static-$(version).apk
code : $(built_files)
$(built_files): $(chroot_dir)%.lua : src/lua/%.lua
echo built files: $(built_files)
cp $^ $@
$(built_pages): $(chroot_dir)pages/%.etlua : src/pages/%.etlua

View File

@ -25,8 +25,9 @@ mime_add=css:text/css
dev {
# These flags are added to the shared ones when
# you build the "dev" flavor.
ldflags=-llua
ldflags=-llua5.1
cflags=-g -Wextra
cflags=-I/usr/include/lua5.1
cxxflags=-g -Wextra
}

View File

@ -7,9 +7,9 @@ server tls {
seccomp_tracing yes
load ./smr.so
root kore_chroot
runas root
#keymgr_runas demo
#keymgr_root ./
runas robin
keymgr_runas robin
keymgr_root .
workers 1
http_body_max 8388608
@ -20,12 +20,13 @@ validator v_any regex .*
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)
domain * {
attach tls
certfile server.pem
certkey key.pem
certfile cert/server.pem
certkey cert/key.pem
#I run kore behind a lighttpd reverse proxy, so this is a bit useless to me
accesslog /dev/null
@ -40,12 +41,16 @@ domain * {
route /_bio edit_bio
route /_login login
route /_claim claim
route /_download download
# Leading ^ is needed for dynamic routes, kore says the route is dynamic if it does not start with '/'
route ^/[^_].* read_story
params get /_edit {
validate story v_storyid
}
params get /_download {
validate story v_storyid
}
params post /_edit {
validate title v_any
validate story v_storyid
@ -59,9 +64,14 @@ domain * {
validate pasteas v_subdomain
validate markup v_markup
}
#params get /[^_].* {
params get ^/[^_].* {
validate comments v_bool
#validate story v_storyid
#}
}
params post ^/[^_].* {
validate text v_any
validate postas v_subdomain
}
params post /_login {
validate user v_subdomain
validate pass v_any
@ -69,4 +79,5 @@ domain * {
params post /_claim {
validate user v_any
}
}

View File

@ -1,5 +1,5 @@
/*
borrowed sha3 implementation from keccak.team
borrowed sha3 implementation from https://keccak.team
*/
#ifdef BUILD_PROD
#include <luajit.h>

View File

@ -1,6 +1,9 @@
print("Really fast print from init.lua")
local et = require("etlua")
local sql = require("lsqlite3")
local zlib = require("zlib")
if PRODUCTION then
local function print() end --squash prints
end
@ -50,6 +53,7 @@ local stmnt_author_create, stmnt_author_acct, stmnt_author_bio
local stmnt_cache, stmnt_insert_cache, stmnt_dirty_cache
local stmnt_get_session, stmnt_insert_session
local stmnt_edit, stmnt_update, stmnt_update_raw, stmnt_author_of
local stmnt_comments, stmnt_comment_insert
--see https://perishablepress.com/stop-using-unsafe-characters-in-urls/
--no underscore because we use that for our operative pages
local url_characters =
@ -82,8 +86,10 @@ end
print("Hello from init.lua")
function configure()
db = sqlassert(sql.open("data/posts.db"))
--db = sqlassert(sql.open_memory())
cache = sqlassert(sql.open_memory())
print("Compiled pages...")
--Test that compression works
local msg = "test message"
local one = zlib.compress(msg)
local two = zlib.decompress(one)
@ -132,6 +138,8 @@ function configure()
--Select the data we need to read a story (and maybe display an edit
--button
stmnt_read = assert(db:prepare(queries.select_post))
stmnt_comments = assert(db:prepare(queries.select_comments))
stmnt_comment_insert = assert(db:prepare(queries.insert_comment))
--TODO: actually let authors edit their bio
stmnt_author_bio = assert(db:prepare([[
SELECT authors.biography FROM authors WHERE authors.name = :author;
@ -158,6 +166,8 @@ function configure()
stmnt_raw = assert(db:prepare(queries.insert_raw))
--Get the data we need to display the edit screen
stmnt_edit = assert(db:prepare(queries.select_edit))
--Get the data we need when someone wants to download a paste
stmnt_download = assert(db:prepare(queries.select_download))
--When we update a post, store the plaintext again
stmnt_update_raw = assert(db:prepare(queries.update_raw))
--Should we really reset the update time every time someone makes a post?
@ -172,7 +182,7 @@ function configure()
FROM cache
WHERE
path = :path AND
((dirty = 0) OR (strftime('%s','now') - updated) < 10)
((dirty = 0) OR (strftime('%s','now') - updated) < 2)
;
]])
stmnt_insert_cache = cache:prepare([[
@ -189,6 +199,7 @@ function configure()
]])
--[=[
]=]
print("finished running configure()")
end
print("Created configure function")
@ -232,6 +243,7 @@ local function do_sql(stmnt)
end
local function dirty_cache(url)
print("Dirtying cache:",url)
stmnt_dirty_cache:bind_names{
path = url
}
@ -475,25 +487,33 @@ function paste(req)
if method == "GET" then
--Get the paste page
if host == domain then
--For an anonymous user
ret = render(string.format("%s/_paste",host),function()
print("Cache missing, rendering post page")
return pages.paste{
domain = domain,
err = "",
}
end)
local author,_ = get_session(req)
if author then
http_response_header(req,"Location",string.format("https://%s.%s/_paste",author,domain))
http_response(req,303,"")
return
else
--For an anonymous user
ret = render(string.format("%s/_paste",host),function()
print("Cache missing, rendering post page")
return pages.paste{
domain = domain,
err = "",
}
end)
end
else
--Or for someone that's logged in
print("Looks like a logged in user wants to paste!")
local subdomain = host:match("([^\\.]+)")
local subdomain = host:match("([^%.]+)")
local author,_ = get_session(req)
print("subdomain:",subdomain,"author:",author)
--If they try to paste as an author, but are on the
--wrong subdomain, or or not logged in, redirect them
--to the right place. Their own subdomain for authors
--or the anonymous paste page for not logged in users.
if author == nil then
print("sessionid was nil")
http_response_header(req,"Location","https://"..domain.."/_paste")
http_response(req,303,"")
return
@ -557,7 +577,6 @@ function paste(req)
if err ~= sql.DONE then
print("Failed to save raw text, but paste still went though")
end
print("Successful paste, rowid:", rowid)
local url = encode_id(rowid)
local loc = string.format("https://%s/%s",domain,url)
http_response_header(req,"Location",loc)
@ -570,7 +589,7 @@ function paste(req)
elseif err == sql.ERROR or err == sql.MISUSE then
ret = "Failed to paste: " .. tostring(err)
else
error("Error pasting:",err)
error("Error pasting:" .. tostring(err))
end
stmnt_paste:reset()
@ -636,11 +655,15 @@ function paste(req)
end
--A helper function for below
local function read_story(host,path,idp)
return render(string.format("%s%s",host,path),function()
print("Trying to read, id is",idp,":",decode_id(idp))
local function read_story(host,path,idp,show_comments,iam)
local cachestr
if show_comments then
cachestr = string.format("%s%s?comments=1",host,path)
else
cachestr = string.format("%s%s",host,path)
end
local readstoryf = function()
local id = decode_id(idp)
print("id:",id,type(id))
stmnt_read:bind_names{
id = id
}
@ -653,6 +676,20 @@ local function read_story(host,path,idp)
end
assert(err == sql.ROW,"Could not get row:" .. tostring(id) .. " Error:" .. tostring(err))
local title, text, authorid, isanon, authorname = unpack(stmnt_read:get_values())
stmnt_comments:bind_names{
id = id
}
err = do_sql(stmnt_comments)
local comments = {}
while err ~= sql.DONE do
local com_author, com_isanon, com_text = unpack(stmnt_comments:get_values())
table.insert(comments,{
author = com_author,
isanon = com_isanon == 1, --int to boolean
text = com_text
})
err = stmnt_comments:step()
end
text = zlib.decompress(text)
stmnt_read:reset()
return pages.read{
@ -661,61 +698,108 @@ local function read_story(host,path,idp)
text = text,
idp = idp,
isanon = isanon == 1,
author = authorname
author = authorname,
comments = comments,
show_comments = show_comments,
iam = iam,
}
end)
end
--Don't cache if we're logged in, someone might see dirty cache information on the page.
if not iam then
return render(cachestr,readstoryf)
else
return readstoryf()
end
end
function read(req)
local host = http_request_get_host(req)
local path = http_request_get_path(req)
local idp = string.sub(path,2)--remove leading "/"
assert(string.len(path) > 0,"Tried to read 0-length story id")
local author, authorid = get_session(req)
local text
if author then
--We're logged in as someone
local id = decode_id(idp)
stmnt_read:bind_names{
id = id
}
local err = do_sql(stmnt_read)
if err == sql.DONE then
--We got no story
stmnt_read:reset()
text = pages.nostory{
path = path
local method = http_method_text(req)
if method == "GET" then
local idp = string.sub(path,2)--remove leading "/"
assert(string.len(path) > 0,"Tried to read 0-length story id")
local author, authorid = get_session(req)
http_request_populate_qs(req)
local show_comments = http_argument_get_string(req,"comments")
--parameters needed for the read page
local text
if author then
--We're logged in as someone
local id = decode_id(idp)
stmnt_read:bind_names{
id = id
}
else
assert(err == sql.ROW)
local title, storytext, tauthor, isanon, authorname = unpack(stmnt_read:get_values())
storytext = zlib.decompress(storytext)
stmnt_read:reset()
if tauthor == authorid then
print("we're the author!")
--The story exists and we're logged in as the
--owner, display the edit button
text = pages.read{
domain = domain,
title = title,
text = storytext,
idp = idp,
isanon = isanon == 1,
author = authorname,
owner = true
local err = do_sql(stmnt_read)
if err == sql.DONE then
--We got no story
stmnt_read:reset()
text = pages.nostory{
path = path
}
else
print("we're not the author!")
text = read_story(host,path,idp)
--If we can edit this story, we don't want to cache
--the page, since it'll have an edit button on it.
assert(err == sql.ROW)
local title, storytext, tauthor, isanon, authorname = unpack(stmnt_read:get_values())
storytext = zlib.decompress(storytext)
stmnt_read:reset()
if tauthor == authorid then
--The story exists and we're logged in as the
--owner, display the edit button
text = pages.read{
domain = domain,
title = title,
text = storytext,
idp = idp,
isanon = isanon == 1,
author = authorname,
iam = authorname,
owner = true
}
else
text = read_story(host,path,idp,show_comments,author)
end
end
else
--We're not logged in as anyone
http_request_populate_qs(req)
text = read_story(host,path,idp,show_comments,author)
end
assert(text)
http_response(req,200,text)
return
elseif method == "POST" then
--We're posting a comment
http_request_populate_post(req)
http_populate_cookies(req)
local author, authorid = get_session(req)
local comment_text = assert(http_argument_get_string(req,"text"))
local pasteas = assert(http_argument_get_string(req,"postas"))
local idp = string.sub(path,2)--remove leading "/"
local id = decode_id(idp)
local isanon = 1
if author and pasteas ~= "Anonymous" then
isanon = 0
end
stmnt_comment_insert:bind_names{
postid=id,
authorid = author and authorid or -1,
isanon = isanon,
comment_text = comment_text,
}
local err = do_sql(stmnt_comment_insert)
stmnt_comment_insert:reset()
if err ~= sql.DONE then
http_response(req,500,"Internal error, failed to post comment. Go back and try again.")
else
dirty_cache(string.format("%s%s?comments=1",host,path))
local redir = string.format("https://%s%s?comments=1", domain, path)
http_response_header(req,"Location",redir)
http_response(req,303,"")
end
else
text = read_story(host,path,idp)
end
assert(text)
http_response(req,200,text)
end
function login(req)
@ -883,4 +967,30 @@ function teardown()
print("Finished lua teardown")
end
function download(req)
local host = http_request_get_host(req)
local path = http_request_get_path(req)
print("host:",host,"path:",path)
http_request_populate_qs(req)
local story = assert(http_argument_get_string(req,"story"))
local story_id = decode_id(story)
stmnt_download:bind_names{
postid = story_id
}
local err = do_sql(stmnt_download)
if err == sql.DONE then
--No rows, story not found
http_responose(req,404,pages.nostory{path=story})
stmnt_download:reset()
return
end
local txt_compressed, title = unpack(stmnt_download:get_values())
local text = zlib.decompress(txt_compressed)
stmnt_download:reset()
http_response_header(req,"Content-Type","application/octet-stream")
local nicetitle = title:gsub("%W","_")
http_response_header(req,"Content-Disposition","attachment; filename=\"" .. nicetitle .. ".txt\"")
http_response(req,200,text)
end
print("Done with init.lua")

View File

@ -61,10 +61,10 @@ local grammar = P{
heading = wrap("==",[[<h2>%s</h2>]]),
strike = wrap("~~",[[<s>%s</s>]]),
code = tag("code",[[<pre><code>%s</code></pre>]]),
greentext = P"> " * Cs((V"marked" + word)^0) / function(a)
greentext = P">" * (B"\n>" + B">") * Cs((V"marked" + word)^0) / function(a)
return string.format([[<span class="greentext">&gt;%s</span>]],a)
end,
pinktext = P"< " * Cs((V"marked" + word)^0) / function(a)
pinktext = P"<" * (B"\n<" + B"<") * Cs((V"marked" + word)^0) / function(a)
return string.format([[<span class="pinktext">&lt;%s</span>]],a)
end,
marked = V"spoiler" + V"bold" + V"italic" + V"underline" + V"heading" + V"strike" + V"spoiler2" + V"code",
@ -76,36 +76,39 @@ local grammar = P{
chunk = V"line"^0 * V"plainline" * V"ending"
}
--local text = [[
--this is **a big** test with ''italics''!
--we need to > sanitize < things that could be tags
--like really <b> badly </b>
--words can include any'single item without=penalty
--Can you use '''one tag ==within== another tag'''?
--let's see if [spoiler]spoiler tags work[/spoiler]
--things might even __go over
--multiple lines__ blah
--Let's test out those [code]
--code tag,s and see how well
--they work
--here's ome
--preformated <with injection>
--text
--[/code]
--> Or have blank lines
--[=[
local text = [[
<pinktext on the first line
this is **a big** test with ''italics''!
we need to > sanitize < things that could be tags
like really <b> badly </b>
words can include any'single item without=penalty
Can you use '''one tag ==within== another tag'''?
let's see if [spoiler]spoiler tags work[/spoiler]
things might even __go over
multiple lines__ blah
Let's test out those [code]
code tag,s and see how well
they work
here's ome
preformated <with injection>
text
[/code]
>Or have blank lines
--one important thing is that greentext > should not start in the middle of a line
--> this next line is a green text, what if I include **markup** inside it?
--< and after '''it is''' a pinktext
--> because of some of these restrictions **bold text
--cannot go over multiple lines** in a green text
--__and finally__ there might be some text with '''
--incomplete syntax <b> with injection</b> !!!!
--]]
one important thing is that greentext > should not start in the middle of a line
>this next line is a green text, what if I include **markup** inside it?
<and after '''it is''' a pinktext
>because of some of these restrictions **bold text
cannot go over multiple lines** in a green text
>greentext on the last line
<pinktext on the last line
]]
]=]
return function(text)
return table.concat({grammar:match(text .. "\n")}," ")
end
--for k,v in pairs({grammar:match(text)}) do
--print(k,":",v)
-- print(k,":",v)
--end

View File

@ -11,24 +11,67 @@
<nav>
<a href="https://<%= domain %>"><%= domain %></a>/<a href="https://<%= domain %>/<%= idp %>"><%= idp %></a>
</nav>
<% if owner then %>
<% if owner then -%>
<form action="https://<%= domain %>/_edit" method="get"><fieldset>
<input type="hidden" name="story" value="<%= idp %>"/>
<input type="submit" value="edit" class="button"/>
</fieldset></form>
<% end %>
<% end -%>
<article>
<h2 class="title">
<%- title %>
</h2>
<h3>
<% if isanon then %>
<% if isanon then -%>
By Anonymous
<% else %>
<% else -%>
By <a href="https://<%= author %>.<%= domain %>"><%= author %></a>
<% end %>
<% end -%>
</h3>
<%- text %>
</article>
<form action="https://<%= domain %>/_download" method="get">
<input type="hidden" name="story" value="<%= idp %>"/>
<input type="submit" value="Download TXT" class="button"/>
</form>
<% if not show_comments then -%>
<form action="https://<%= domain %>/<%= idp %>"><fieldset>
<input type="hidden" name="comments" value="1">
<input type="submit" value="load comments" class="button">
</fieldset></form>
<% else %>
<form action="https://<%= domain %>/<%= idp %>" method="POST">
<textarea name="text" cols=60 rows=10 class="column"></textarea>
</div><% if iam then %>
<select id="postas" name="postas">
<option value="Anonymous">Anonymous</option>
<option value="<%= iam %>"><%= iam %></option>
</select>
<input type="submit" value="post" class="button">
<% else %>
<input type="hidden" name="postas" value="Anonymous">
<input type="submit" value="post" class="button">
<% end %>
</form>
<% if comments and #comments == 0 then %>
<p><i>No comments yet</i></p>
<% else %>
<section>
<% for _,comment in pairs(comments) do %>
<article>
<% if comment.isanon then %>
<p><b>Anonymous</b></p>
<% else %>
<p><b><%= comment.author %></b></p>
<% end %>
<p><%= comment.text %></p>
</article>
<% end %>
</section>
<% end %>
<% end %>
</main></body>
</html>

View File

@ -20,6 +20,7 @@ int edit_bio(struct http_request *);
int read_story(struct http_request *);
int login(struct http_request *);
int claim(struct http_request *);
int download(struct http_request *);
int style(struct http_request *);
int miligram(struct http_request *);
int do_lua(struct http_request *req, const char *name);
@ -107,6 +108,12 @@ claim(struct http_request *req){
return do_lua(req,"claim");
}
int
download(struct http_request *req){
printf("We want to do download!\n");
return do_lua(req,"download");
}
int
home(struct http_request *req){
return do_lua(req,"home");

View File

@ -0,0 +1,15 @@
INSERT INTO comments(
postid,
author,
isanon,
comment_text,
hashedip,
post_time
) VALUES (
:postid,
:authorid,
:isanon,
:comment_text,
'',
strftime('%s','now')
);

View File

@ -0,0 +1,11 @@
SELECT
authors.name,
comments.isanon,
comments.comment_text
FROM
comments,
authors
WHERE
comments.author = authors.id AND
comments.postid = :id
ORDER BY post_time DESC;

View File

@ -0,0 +1,7 @@
SELECT
raw_text.post_text, posts.post_title
FROM
raw_text, posts
WHERE
raw_text.id = posts.id AND
raw_text.id = :postid