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/ mirror=http://dl-cdn.alpinelinux.org/alpine/
arch=aarch64 arch=aarch64
version=2.10.5-r0 version=2.10.5-r0
#certbot_email=--register-unsafely-without-email certbot_email=--register-unsafely-without-email
certbot_email=-m you@cock.li #certbot_email=-m you@cock.li
domain=test.monster domain=test.monster
#Probably don't change stuff past here #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) echo $(built_files)
kodev run kodev run
apk-tools-static-$(version).apk: Makefile apk-tools-static-$(version).apk:
wget -q $(mirror)latest-stable/main/$(arch)/apk-tools-static-$(version).apk # wget -q $(mirror)latest-stable/main/$(arch)/apk-tools-static-$(version).apk
clean: clean:
kodev 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)
mkdir -p $(chroot_dir)/pages mkdir -p $(chroot_dir)/pages
mkdir -p $(chroot_dir)/sql 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 #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/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 #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 -t proc none $(chroot_dir)/proc
#mount -o bind /sys $(chroot_dir)/sys #mount -o bind /sys $(chroot_dir)/sys
#cp /etc/resolv.conf $(chroot_dir)/etc/resolv.conf #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 #cp /etc/apk/repositories $(chroot_dir)/etc/apk/repositories
#mkdir $(chroot_dir)/var/sm #mkdir $(chroot_dir)/var/sm
## Things to build lua libraries ## 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) 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 etlua
#chroot $(chroot_dir) luarocks-5.1 install lsqlite3 #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 ## 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 #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. ## 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) code : $(built_files)
$(built_files): $(chroot_dir)%.lua : src/lua/%.lua $(built_files): $(chroot_dir)%.lua : src/lua/%.lua
echo built files: $(built_files)
cp $^ $@ cp $^ $@
$(built_pages): $(chroot_dir)pages/%.etlua : src/pages/%.etlua $(built_pages): $(chroot_dir)pages/%.etlua : src/pages/%.etlua

View File

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

View File

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

View File

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

View File

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

View File

@ -11,24 +11,67 @@
<nav> <nav>
<a href="https://<%= domain %>"><%= domain %></a>/<a href="https://<%= domain %>/<%= idp %>"><%= idp %></a> <a href="https://<%= domain %>"><%= domain %></a>/<a href="https://<%= domain %>/<%= idp %>"><%= idp %></a>
</nav> </nav>
<% if owner then %> <% if owner then -%>
<form action="https://<%= domain %>/_edit" method="get"><fieldset> <form action="https://<%= domain %>/_edit" method="get"><fieldset>
<input type="hidden" name="story" value="<%= idp %>"/> <input type="hidden" name="story" value="<%= idp %>"/>
<input type="submit" value="edit" class="button"/> <input type="submit" value="edit" class="button"/>
</fieldset></form> </fieldset></form>
<% end %> <% end -%>
<article>
<h2 class="title"> <h2 class="title">
<%- title %> <%- title %>
</h2> </h2>
<h3> <h3>
<% if isanon then %> <% if isanon then -%>
By Anonymous By Anonymous
<% else %> <% else -%>
By <a href="https://<%= author %>.<%= domain %>"><%= author %></a> By <a href="https://<%= author %>.<%= domain %>"><%= author %></a>
<% end %> <% end -%>
</h3> </h3>
<%- text %> <%- text %>
</article> </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> </main></body>
</html> </html>

View File

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