Add comments
Add comments for each paste, Also add a "Download TXT" button.
This commit is contained in:
parent
0ba9e22f70
commit
bce0788b62
19
Makefile
19
Makefile
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
230
src/lua/init.lua
230
src/lua/init.lua
|
@ -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")
|
||||||
|
|
|
@ -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">>%s</span>]],a)
|
return string.format([[<span class="greentext">>%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"><%s</span>]],a)
|
return string.format([[<span class="pinktext"><%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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
INSERT INTO comments(
|
||||||
|
postid,
|
||||||
|
author,
|
||||||
|
isanon,
|
||||||
|
comment_text,
|
||||||
|
hashedip,
|
||||||
|
post_time
|
||||||
|
) VALUES (
|
||||||
|
:postid,
|
||||||
|
:authorid,
|
||||||
|
:isanon,
|
||||||
|
:comment_text,
|
||||||
|
'',
|
||||||
|
strftime('%s','now')
|
||||||
|
);
|
|
@ -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;
|
|
@ -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
|
Loading…
Reference in New Issue