Inital commit

This commit is contained in:
Linux User 2020-05-15 19:10:11 -04:00
commit 48b4173a7a
31 changed files with 2300 additions and 0 deletions

1
Makefile Normal file
View File

@ -0,0 +1 @@
The slash.monster server code

6
README.md Normal file
View File

@ -0,0 +1,6 @@
# Misc notes.
We need to enable STATX in kore instead of STAT. One of the places seems to have
ifdef garuds around it, just added it to the other place.
We need to comment out the restrictions on mprotect around line 99 of src/seccomp.c in order for luajit to be able to do it's thing

BIN
assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

11
assets/milligram.css Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14
assets/style.css Normal file
View File

@ -0,0 +1,14 @@
body{
margin:40px auto;
max-width:80%;
line-height:1.6;
font-size:18px;
color:#444 !important;
padding:0 10px
}
h1,h2,h3{line-height:1.2}
a,a:visited{color:#f99}
.spoiler,.spoiler2{background:#444}
.spoiler:hover,.spoiler2:hover{color:#FFF}
.greentext{color:#282}
.pinktext{color:#928}

38
conf/build.conf Normal file
View File

@ -0,0 +1,38 @@
# smr build config
# You can switch flavors using: kodev flavor [newflavor]
# Set to yes if you wish to produce a single binary instead
# of a dynamic library. If you set this to yes you must also
# set kore_source together with kore_flavor.
#single_binary=no
#kore_source=/home/joris/src/kore
#kore_flavor=
# The flags below are shared between flavors
cflags=-Wall -Wmissing-declarations -Wshadow
cflags=-Wstrict-prototypes -Wmissing-prototypes
cflags=-Wpointer-arith -Wcast-qual -Wsign-compare
cxxflags=-Wall -Wmissing-declarations -Wshadow
cxxflags=-Wpointer-arith -Wcast-qual -Wsign-compare
# Mime types for assets served via the builtin asset_serve_*
mime_add=txt:text/plain; charset=utf-8
mime_add=png:image/png
mime_add=html:text/html; charset=utf-8
mime_add=css:text/css
dev {
# These flags are added to the shared ones when
# you build the "dev" flavor.
ldflags=-llua
cflags=-g
cxxflags=-g
}
prod {
cflags=-D BUILD_PROD
cflags=-I/usr/include/luajit-2.1
cflags=-lluajit-5.1
ldflags=-lluajit-5.1
}

73
conf/smr.conf Normal file
View File

@ -0,0 +1,73 @@
# smr configuration
server tls {
bind 0.0.0.0 8888
}
seccomp_tracing yes
load ./smr.so
root kore_chroot
runas root
#keymgr_runas demo
#keymgr_root ./
workers 1
http_body_max 8388608
tls_dhparam dh2048.pem
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)
domain * {
attach tls
certfile server.pem
certkey key.pem
#I run kore behind a lighttpd reverse proxy, so this is a bit useless to me
#accesslog /dev/null
accesslog kore_access.log
route / home
route /_css/style.css asset_serve_style_css
route /_css/milligram.css asset_serve_milligram_css
route /_css/milligram.min.css.map asset_serve_milligram_min_css_map
route /favicon.ico asset_serve_favicon_ico
route /_paste post_story
route /_edit edit_story
route /_bio edit_bio
route /_login login
route /_claim claim
# 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 post /_edit {
validate title v_any
validate story v_storyid
validate text v_any
validate pasteas v_subdomain
validate markup v_markup
}
params post /_paste {
validate title v_any
validate text v_any
validate pasteas v_subdomain
validate markup v_markup
}
#params get /[^_].* {
#validate story v_storyid
#}
params post /_login {
validate user v_subdomain
validate pass v_any
}
params post /_claim {
validate user v_any
}
}

8
dh2048.pem Normal file
View File

@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEAn4f4Qn5SudFjEYPWTbUaOTLUH85YWmmPFW1+b5bRa9ygr+1wfamv
VKVT7jO8c4msSNikUf6eEfoH0H4VTCaj+Habwu+Sj+I416r3mliMD4SjNsUJrBrY
Y0QV3ZUgZz4A8ARk/WwQcRl8+ZXJz34IaLwAcpyNhoV46iHVxW0ty8ND0U4DIku/
PNayKimu4BXWXk4RfwNVP59t8DQKqjshZ4fDnbotskmSZ+e+FHrd+Kvrq/WButvV
Bzy9fYgnUlJ82g/bziCI83R2xAdtH014fR63MpElkqdNeChb94pPbEdFlNUvYIBN
xx2vTUQMqRbB4UdG2zuzzr5j98HDdblQ+wIBAg==
-----END DH PARAMETERS-----

36
src/keccak.c Normal file
View File

@ -0,0 +1,36 @@
#define FOR(i,n) for(i=0; i<n; ++i)
#include "keccak.h"
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen);
void FIPS202_SHAKE128(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1344, 256, in, inLen, 0x1F, out, outLen); }
void FIPS202_SHAKE256(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1088, 512, in, inLen, 0x1F, out, outLen); }
void FIPS202_SHA3_224(const u8 *in, u64 inLen, u8 *out) { Keccak(1152, 448, in, inLen, 0x06, out, 28); }
void FIPS202_SHA3_256(const u8 *in, u64 inLen, u8 *out) { Keccak(1088, 512, in, inLen, 0x06, out, 32); }
void FIPS202_SHA3_384(const u8 *in, u64 inLen, u8 *out) { Keccak(832, 768, in, inLen, 0x06, out, 48); }
void FIPS202_SHA3_512(const u8 *in, u64 inLen, u8 *out) { Keccak(576, 1024, in, inLen, 0x06, out, 64); }
int LFSR86540(u8 *R) { (*R)=((*R)<<1)^(((*R)&0x80)?0x71:0); return ((*R)&2)>>1; }
#define ROL(a,o) ((((u64)a)<<o)^(((u64)a)>>(64-o)))
static u64 load64(const u8 *x) { ui i; u64 u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; }
static void store64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]=u; u>>=8; } }
static void xor64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]^=u; u>>=8; } }
#define rL(x,y) load64((u8*)s+8*(x+5*y))
#define wL(x,y,l) store64((u8*)s+8*(x+5*y),l)
#define XL(x,y,l) xor64((u8*)s+8*(x+5*y),l)
void KeccakF1600(void *s)
{
ui r,x,y,i,j,Y; u8 R=0x01; u64 C[5],D;
for(i=0; i<24; i++) {
/*θ*/ FOR(x,5) C[x]=rL(x,0)^rL(x,1)^rL(x,2)^rL(x,3)^rL(x,4); FOR(x,5) { D=C[(x+4)%5]^ROL(C[(x+1)%5],1); FOR(y,5) XL(x,y,D); }
/*ρπ*/ x=1; y=r=0; D=rL(x,y); FOR(j,24) { r+=j+1; Y=(2*x+3*y)%5; x=y; y=Y; C[0]=rL(x,y); wL(x,y,ROL(D,r%64)); D=C[0]; }
/*χ*/ FOR(y,5) { FOR(x,5) C[x]=rL(x,y); FOR(x,5) wL(x,y,C[x]^((~C[(x+1)%5])&C[(x+2)%5])); }
/*ι*/ FOR(j,7) if (LFSR86540(&R)) XL(0,0,(u64)1<<((1<<j)-1));
}
}
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen)
{
/*initialize*/ u8 s[200]; ui R=r/8; ui i,b=0; FOR(i,200) s[i]=0;
/*absorb*/ while(inLen>0) { b=(inLen<R)?inLen:R; FOR(i,b) s[i]^=in[i]; in+=b; inLen-=b; if (b==R) { KeccakF1600(s); b=0; } }
/*pad*/ s[b]^=sfx; if((sfx&0x80)&&(b==(R-1))) KeccakF1600(s); s[R-1]^=0x80; KeccakF1600(s);
/*squeeze*/ while(outLen>0) { b=(outLen<R)?outLen:R; FOR(i,b) out[i]=s[i]; out+=b; outLen-=b; if(outLen>0) KeccakF1600(s); }
}

4
src/keccak.h Normal file
View File

@ -0,0 +1,4 @@
typedef unsigned char u8;
typedef unsigned long long int u64;
typedef unsigned int ui;
void FIPS202_SHA3_512(const u8 *in, u64 inLen, u8 *out);

64
src/libcrypto.c Normal file
View File

@ -0,0 +1,64 @@
/*
borrowed sha3 implementation from keccak.team
*/
#ifdef BUILD_PROD
#include <luajit.h>
#endif
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include "libcrypto.h"
#include "keccak.h"
/*
sha3(data::string)::string
*/
int
lsha3(lua_State *L){
size_t len;
char out[64];
const char *data = luaL_checklstring(L,-1,&len);
lua_pop(L,1);
printf("All data gotten, about to hash\n");
FIPS202_SHA3_512(data, len, out);
printf("Finished hashing\n");
lua_pushlstring(L,out,64);
printf("Finished pushing string to lua\n");
return 1;
}
/*
string xor
sxor(a::string, b::string)::string
*/
int
lsxor(lua_State *L){
size_t la,lb;
const char *a = luaL_checklstring(L,-1,&la);
const char *b = luaL_checklstring(L,-2,&lb);
size_t outsize = la > lb ? la : lb;
size_t loopsize = la > lb ? lb : la;
const char *shorter = la > lb ? b : a;
const char *longer = la > lb ? a : b;
char out[outsize];
int i;
for(i = 0; i < loopsize; i++)
out[i] = shorter[i] ^ longer[i];
for(;i < outsize; i++)
out[i] = longer[i];
lua_pushlstring(L,out,outsize);
return 1;
}
static const luaL_Reg crypto_funcs[] = {
{"sha3", lsha3},
{"sxor", lsxor},
{NULL,NULL}
};
void
load_crypto_libs(lua_State *L){
lua_getglobal(L,"_G");
luaL_register(L,NULL,crypto_funcs);
lua_pop(L,1);
}

4
src/libcrypto.h Normal file
View File

@ -0,0 +1,4 @@
int lsha3(lua_State *L);
int lsxor(lua_State *L);
void load_crypto_libs(lua_State *L);

292
src/libkore.c Normal file
View File

@ -0,0 +1,292 @@
/*Export kore's functions to lua*/
#include <kore/kore.h>
#include <kore/http.h>
#ifdef BUILD_PROD
#include <luajit.h>
#endif
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
//#include <inet/in.h>//linux only I guess
#include "libkore.h"
struct http_request*
luaL_checkrequest(lua_State *L, int pos){
if(!lua_isuserdata(L,pos)){
lua_pushstring(L,"Bad argument, expected userdata, got ");
lua_pushstring(L,lua_typename(L,lua_type(L,pos)));
lua_concat(L,2);
lua_error(L);
}
return lua_touserdata(L,pos);
}
/*
http_response(request::userdata, errcode::number, data::string)
*/
int
lhttp_response(lua_State *L){
size_t size;
const char *data = luaL_checklstring(L,-1,&size);
int httpcode = luaL_checkint(L,-2);
struct http_request *req = luaL_checkrequest(L,-3);
http_response(req,httpcode,data,size);
lua_pop(L,3);
return 0;
}
/*
http_method_text(request::userdata)::string
*/
int
lhttp_method_text(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
const char *method = http_method_text(req->method);
lua_pushstring(L,method);
return 1;
}
/*
http_request_get_path(request::userdata)::string
*/
int
lhttp_request_get_path(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
const char *path = req->path;
lua_pushstring(L,path);
return 1;
}
/*
http_request_get_host(request::userdata)::string
*/
int
lhttp_request_get_host(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
const char *host = req->host;
lua_pushstring(L,host);
return 1;
}
/*
http_request_populate_post(request::userdata)
*/
int
lhttp_request_populate_post(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
http_populate_post(req);
return 0;
}
/*
http_request_populate_qs(request::userdata)
*/
int
lhttp_request_populate_qs(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
http_populate_qs(req);
return 0;
}
/*
http_response_header(request::userdata, header::string, value::string)
*/
int
lhttp_response_header(lua_State *L){
const char *value = luaL_checkstring(L,-1);
const char *header = luaL_checkstring(L,-2);
struct http_request *req = luaL_checkrequest(L,-3);
lua_pop(L,3);
http_response_header(req, header, value);
return 0;
}
/*
http_response_cookie(req::userdata, name::string, value::string, path::string, expires::number, maxage::number)
*/
int
lhttp_response_cookie(lua_State *L){
//int flags = luaL_checkint(L,-1);//TODO: flags
int maxage = luaL_checkint(L,-1);
int expires = luaL_checkint(L,-2);
const char *path = luaL_checkstring(L,-3);
const char *value = luaL_checkstring(L,-4);
const char *name = luaL_checkstring(L,-5);
struct http_request *req = luaL_checkrequest(L,-6);
lua_pop(L,6);
http_response_cookie(req,name,value,path,expires,maxage,NULL);
return 0;
}
/*
http_request_cookie(req::userdata, name::string)::string | nil
*/
int
lhttp_request_cookie(lua_State *L){
char *value;
const char *name = luaL_checkstring(L,-1);
struct http_request *req = luaL_checkrequest(L,-2);
lua_pop(L,2);
if(http_request_cookie(req,name,&value)){
lua_pushstring(L,value);
}else{
lua_pushnil(L);
}
return 1;
}
/*
This method may fail, if it does, it will return false plus an error message
http_argument_get_string(request::userdata, name::string)::(string || false, string)
*/
int
lhttp_argument_get_string(lua_State *L){
const char *name = luaL_checkstring(L,-1);
struct http_request *req = luaL_checkrequest(L,-2);
lua_pop(L,2);
const char *value;
int err = http_argument_get_string(req,name,&value);
if(err == KORE_RESULT_OK){
lua_pushstring(L,value);
return 1;
}else{
lua_pushboolean(L,0);
lua_pushstring(L,"Failed to find argument: ");
lua_pushstring(L,name);
lua_concat(L,2);
return 2;
}
}
/*
Get the ip of this connection, ipv6 supported
http_request_get_ip(request::userdata)::string
*/
int
lhttp_request_get_ip(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
char addr[INET6_ADDRSTRLEN];
printf("AF_INET:%d\n",AF_INET);
printf("AF_INET6:%d\n",AF_INET6);
printf("AF_UNIX:%d\n",AF_UNIX);
switch(req->owner->family){
case AF_INET:
inet_ntop(
req->owner->family,
&(req->owner->addr.ipv4.sin_addr),
addr,
sizeof(addr)
);
break;
case AF_INET6:
inet_ntop(
req->owner->family,
&(req->owner->addr.ipv6.sin6_addr),
addr,
sizeof(addr)
);
break;
case AF_UNIX:
default:
lua_pushstring(L,"Tried to get IP from unknown socket family:");
lua_getglobal(L,"tostring");
lua_pushnumber(L,req->owner->family);
lua_call(L,1,1);
lua_concat(L,2);
lua_error(L);
break;
}
lua_pushstring(L,addr);
return 1;
}
/*
http_populate_cookies(request::userdata)
*/
int
lhttp_populate_cookies(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
http_populate_cookies(req);
return 0;
}
/*
http_populate_multipart_form(request::userdata)
*/
int
lhttp_populate_multipart_form(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
http_populate_multipart_form(req);
return 0;
}
/*
http_file_get(request::userdata, name::string)::string
*/
int
lhttp_file_get(lua_State *L){
const char *name = luaL_checkstring(L,-1);
struct http_request *req = luaL_checkrequest(L,-2);
lua_pop(L,2);
struct http_file *f = http_file_lookup(req, name);
if(f == NULL){
lua_pushstring(L,"No such file:");
lua_pushstring(L,name);
lua_concat(L,2);
lua_error(L);
}
printf("file length: %d\n", f->length);
char s[f->length + 1];
size_t read = http_file_read(f,s,f->length);
if(read < f->length){
lua_pushstring(L,"Failed to read contents of:");
lua_pushstring(L,name);
lua_concat(L,2);
lua_error(L);
}
s[f->length] = '\0';
lua_pushstring(L,s);
return 1;
}
static const luaL_Reg kore_funcs[] = {
{"http_response", lhttp_response},
{"http_response_header", lhttp_response_header},
{"http_method_text",lhttp_method_text},
{"http_request_get_path",lhttp_request_get_path},
{"http_request_get_host",lhttp_request_get_host},
{"http_request_populate_post",lhttp_request_populate_post},
{"http_request_populate_qs",lhttp_request_populate_qs},
{"http_request_cookie",lhttp_request_cookie},
{"http_response_cookie",lhttp_response_cookie},
{"http_argument_get_string",lhttp_argument_get_string},
{"http_request_get_ip",lhttp_request_get_ip},
{"http_populate_cookies",lhttp_populate_cookies},
{"http_populate_multipart_form",lhttp_populate_multipart_form},
{"http_file_get",lhttp_file_get},
{NULL,NULL}
};
static const luaL_Reg http_request_meta[] = {
{NULL,NULL}
};
void
load_kore_libs(lua_State *L){
luaL_newmetatable(L,"http_request");//{m_http_request}
lua_newtable(L);//{m_http_request},{}
luaL_register(L,NULL,http_request_meta);//{m_http_request},{meta}
lua_setfield(L,-2,"__index");//{m_http_request}
lua_pop(L,1);
lua_getglobal(L,"_G");
luaL_register(L,NULL,kore_funcs);
lua_pop(L,1);
}

16
src/libkore.h Normal file
View File

@ -0,0 +1,16 @@
int lhttp_response(lua_State *L);
int lhttp_response_header(lua_State *L);
int lhttp_method_text(lua_State *L);
int lhttp_request_get_path(lua_State *L);
int lhttp_request_get_host(lua_State *L);
int lhttp_request_populate_post(lua_State *L);
int lhttp_response_cookie(lua_State *L);
int lhttp_request_cookie(lua_State *L);
int lhttp_argument_get_string(lua_State *L);
int lhttp_request_get_ip(lua_State *L);
int lhttp_populate_cookies(lua_State *L);
int lhttp_file_get(lua_State *L);
int lhttp_populate_multipart_form(lua_State *L);
void load_kore_libs(lua_State *L);
struct http_request* luaL_checkrequest(lua_State *L, int pos);

1027
src/lua/init.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,111 @@
local lpeg = require("lpeg")
lpeg.locale(lpeg)
local V,P,C,S,B,Cs = lpeg.V,lpeg.P,lpeg.C,lpeg.S,lpeg.B,lpeg.Cs
--Characters to escape in the body text
local escapes = {
["&"] = "&amp;",
["<"] = "&lt;",
[">"] = "&gt;",
}
local esctbl = {}
for char,_ in pairs(escapes) do
table.insert(esctbl,char)
end
local escapematch = string.format("([%s])",table.concat(esctbl))
local function sanitize_item(capture)
return escapes[capture] or capture
end
local function sanitize(text)
local ret,_ = string.gsub(text,escapematch,sanitize_item)
return ret
end
--Grammer
local space = S" \t\r"^0
local special = P{
P"**" + P"''" + P"'''" +
P"__" + P"==" + P"~~" +
P"\n>" + P"\n<" + P"\n" +
P"[code]" + P"[spoiler]"
}
local word = Cs((1 - special)^1) * space / sanitize
--Generates a pattern that formats text inside matching 'seq' tags with format
--ex wrap("^^",[[<sup>%s</sup>]])
--will wrapp text "5^^3^^" as "5<sup>3</sup>"
local function wrap(seq,format)
return P(seq) * Cs(((1 - P(seq)) * space)^1) * P(seq) * space / function(a)
return string.format(format,sanitize(a))
end
end
--Generates a pattern that formats text inside openinig and closing "name" tags
--with a format, BB forum style
local function tag(name,format)
local start_tag = P(string.format("[%s]",name))
local end_tag = P(string.format("[/%s]",name))
return start_tag * Cs(((1 - end_tag) * space)^1) * end_tag * space / function(a)
return string.format(format,sanitize(a))
end
end
local grammer = P{
"chunk";
--regular
spoiler = wrap("**",[[<span class="spoiler">%s</span>]]),
spoiler2 = tag("spoiler",[[<span class="spoiler2">%s</span>]]),
italic = wrap("''",[[<i>%s</i>]]),
bold = wrap("'''",[[<b>%s</b>]]),
underline = wrap("__",[[<u>%s</u>]]),
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)
return string.format([[<span class="greentext">&gt;%s</span>]],a)
end,
pinktext = P"< " * 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",
plainline = (V"marked" + word)^0,
line = Cs(V"greentext" + V"pinktext" + V"plainline" + P"") * P"\n" / function(a)
return string.format("%s<br/>",a)
end,
ending = C(P(1)^0) / sanitize,
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
--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> !!!!
--]]
return function(text)
return table.concat({grammer:match(text .. "\n")}," ")
end
--for k,v in pairs({grammer:match(text)}) do
--print(k,":",v)
--end

22
src/lua/parser_plain.lua Normal file
View File

@ -0,0 +1,22 @@
--Characters to escape in the body text
local escapes = {
["&"] = "&amp;",
["<"] = "&lt;",
[">"] = "&gt;",
['"'] = "&quot;",
["'"] = "&#39;",
--Kinda hacky
["\n"] = "<br/>",
}
local esctbl = {}
for char,_ in pairs(escapes) do
table.insert(esctbl,char)
end
local escapematch = string.format("([%s])",table.concat(esctbl))
local function sanitize(capture)
return escapes[capture] or capture
end
return function(text)
return string.gsub(text,escapematch,sanitize)
end

View File

@ -0,0 +1,42 @@
<% assert(author,"No author specified") %>
<% assert(bio,"No bio included") %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#x1f351;</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body class="container">
<h1 class="title">
<a href="https://<%= author %>.<%= domain %>"><%= author %></a>.<a href="https://<%= domain %>"><%= domain %></a>
</h1>
<div class="content">
<form action="https://<%= author %>.<%= domain %>/" method="post" class="container">
<textarea name="" cols=80 rows=24 class="column">
<%= bio %>
</textarea><br/>
<input type="submit">
</form>
</div>
<div class="content">
<% if #stories == 0 then %>
This author has not made any pastes yet.
<% else %>
<table>
<% for k,v in pairs(stories) do %>
<tr><td><%= v[1] %></td><td><%= v[2] %></td></tr>
<% end %>
</table>
<% end %>
</div>
<footer class="footer">
</footer>
</body>
<body>

View File

@ -0,0 +1,45 @@
<% assert(author,"No author specified") %>
<% assert(bio,"No bio included") %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#x1f351;</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body class="container">
<h1 class="title">
<a href="https://<%= author %>.<%= domain %>"><%= author %></a>.<a href="https://<%= domain %>"><%= domain %></a>
</h1>
<div class="content">
<%= bio %>
</div>
<div class="content">
<% if #stories == 0 then %>
This author has not made any pastes yet.
<% else %>
<table>
<% for k,v in pairs(stories) do %>
<tr><td>
<a href="<%= v.url %>">
<%- v.title %>
</a>
</td><td>
By <a href="https://<%= author %>.<%= domain %>"><%= author %></a>
</td><td>
<%= v.posted %>
</td></tr>
<% end %>
</table>
<% end %>
</div>
<footer class="footer">
</footer>
</body>
<body>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#x1f351;</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body>
<main class="wrapper">
<h1 class="title">
Paste
</h1>
<% if err then %><em class="error"><%= err %></em><% end %>
<form action="https://<%= user %>.<%= domain %>/_paste" method="post" class="container">
<fieldset>
<div class="row">
<input type="text" name="title" placeholder="Title" class="column column-80"></input>
<select id="pasteas" name="pasteas" class="column column-10">
<option value="<%= user %>"><%= user %></option>
<option value="anonymous">Anonymous</option>
</select>
<select id="markup" name="markup" class="column column-10">
<option value="plain">Plain</option>
<option value="imageboard">Imageboard</option>
</select>
</div>
<div class="row">
<textarea name="text" cols=80 rows=24 class="column"><%= text %></textarea><br/>
</div>
<input type="submit">
</fieldset>
</form>
<footer class="footer">
</footer>
</main>
</body>
<body>

35
src/pages/claim.etlua Normal file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#x1f351;</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body class="container">
<main class="wrapper">
<h1 class="title">
Register
</h1>
Once you press submit, you will be prompted to download a file.<br/>
slash.monster uses this file in place of a password, keep it safe.<br/>
Consider keeping a copy on a USB in case your hard drive fails.<br/>
The admin cannot recover your passfile, and will not reset accounts.<br/>
Names may be up to 30 characters, alphanumeric, all lower case.<br/>
<% if err then %><em class="error"><%= err %></em><% end %>
<form action="/_claim" method="post">
<fieldset>
<label for="user">Name:</label>
<input type="text" name="user" id="user" placeholder="name">
<input type="submit">
</fieldset>
</form>
Once you have your file, you can <a href="/_login">log in</a>
<footer class="footer">
</footer>
</main>
</body>
<body>

41
src/pages/edit.etlua Normal file
View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#x1f351;</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body>
<main class="wrapper">
<h1 class="title">
Paste
</h1>
<% if err then %><em class="error"><%= err %></em><% end %>
<form action="https://<%= user %>.<%= domain %>/_paste" method="post" class="container">
<fieldset>
<div class="row">
<input type="text" name="title" placeholder="Title" class="column column-80"></input>
<select id="pasteas" class="column column-10">
<option value="<%= user %>"><%= user %></option>
<option value="anonymous">anonymous</option>
</select>
<select id="markup" class="column column-10">
<option value="plain">Plain</option>
</select>
</div>
<div class="row">
<textarea name="text" cols=80 rows=24 class="column"><%= text %></textarea><br/>
</div>
<input type="submit">
</fieldset>
</form>
<footer class="footer">
</footer>
</main>
</body>
<body>

49
src/pages/index.etlua Normal file
View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#x1f351;</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body class="container">
<main class="wrapper">
<h1 class="title">
<a href="https://<%= domain %>">
<%= domain %>
</a>
</h1>
<div class="container">
<p>
Welcome to slash.monster, stories of fiction and fantasy<br/>
Not safe for work<br/>
18+
</p>
</div>
<div class="content">
<% if #stories == 0 then %>
No stories available.
<% else %>
<table>
<% for k,v in pairs(stories) do %>
<tr><td>
<a href="<%= v.url %>">
<%- v.title %>
</a>
</td><td>
<% if v.isanon then %>
By Anonymous
<% else %>
By <a href="https://<%= v.author %>.<%= domain %>"><%= v.author %></a>
<% end %>
</td><td>
<%= v.posted %>
</td></tr>
<% end %>
</table>
<% end %>
</div>
</main>
</body>
<body>

31
src/pages/login.etlua Normal file
View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#x1f351;</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body class="container">
<main class="wrapper">
<h1 class="title">
Login
</h1>
<% if err then %><em class="error"><%= err %></em><% end %>
<form action="/_login" method="post" enctype="multipart/form-data">
<fieldset>
<label for="user">Name:</label>
<input type="text" name="user" id="user" placeholder="name">
<label for="pass">Passfile:</label>
<input type="file" name="pass" id="pass">
<input type="submit">
</fieldset>
</form>
<footer class="footer">
</footer>
</main>
</body>
<body>

22
src/pages/noauthor.etlua Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#128577</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body class="container">
<main class="wrapper">
<h1 class="title">
&#128577;
</h1>
<div class="container">
<p>
No author found: <%= author %>
</p>
</div>
</main>
</body>
<body>

22
src/pages/nostory.etlua Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#128577</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body class="container">
<main class="wrapper">
<h1 class="title">
&#128577;
</h1>
<div class="container">
<p>
No story found: <%= path %>
</p>
</div>
</main>
</body>
<body>

35
src/pages/paste.etlua Normal file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>&#x1f351;</title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body>
<main class="wrapper">
<h1 class="title">
Paste
</h1>
<% if err then %><em class="error"><%= err %></em><% end %>
<form action="https://<%= domain %>/_paste" method="post" class="container"><fieldset>
<div class="row">
<input type="text" name="title" placeholder="Title" class="column column-80"></input>
<select id="markup" name="markup" class="column column-20">
<option value="plain">Plain</option>
<option value="imageboard">Imageboard</option>
</select>
</div>
<div class="row">
<textarea name="text" cols=80 rows=24 class="column"></textarea><br/>
</div>
<input type="submit">
</fieldset></form>
<footer class="footer">
</footer>
</main>
</body>
<body>

34
src/pages/read.etlua Normal file
View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><%= title %></title>
<link href="/_css/style.css" rel="stylesheet">
<link href="/_css/milligram.css" rel="stylesheet">
</head>
<body class="container">
<main class="wrapper">
<h1>
<a href="https://<%= domain %>"><%= domain %></a>/<a href="https://<%= domain %>/<%= idp %>"><%= idp %></a>
</h1>
<% 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 %>
<h1 class="title">
<%- title %>
</h1>
<% if isanon then %>
By Anonymous
<% else %>
By <a href="https://<%= author %>.<%= domain %>"><%= author %></a>
<% end %>
<article class="container content">
<%- text %>
</article>
</main>
</body>
</html>

173
src/smr.c Normal file
View File

@ -0,0 +1,173 @@
#include <kore/kore.h>
#include <kore/http.h>
#ifdef BUILD_PROD
#include <luajit.h>
#else
#define LUA_OK 0
#endif
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include "smr.h"
#include "libkore.h"
#include "libcrypto.h"
#include <dirent.h>
int home(struct http_request *);
int post_story(struct http_request *);
int edit_story(struct http_request *);
int edit_bio(struct http_request *);
int read_story(struct http_request *);
int login(struct http_request *);
int claim(struct http_request *);
int style(struct http_request *);
int miligram(struct http_request *);
int do_lua(struct http_request *req, const char *name);
int errhandeler(lua_State *);
lua_State *L;
/*
static / index
static / _post post
static / _edit edit
dynamic / .* read
static / _login login
static / _claim claim
*/
int
errhandeler(lua_State *L){
printf("Error: %s\n",lua_tostring(L,1));
lua_getglobal(L,"debug");
lua_getglobal(L,"print");
lua_getfield(L,-2,"traceback");
lua_call(L,0,1);
lua_call(L,1,0);
lua_pop(L,1);
return 1;
}
int
do_lua(struct http_request *req, const char *name){
printf("About to do lua %s\n",name);
lua_pushcfunction(L,errhandeler);
printf("Pushed errhandler function\n");
lua_getglobal(L,name);//err(),name()
if(!lua_isfunction(L,-1)){
printf("Could not find a method named %s\n",name);
lua_pop(L,2);//
return (KORE_RESULT_OK);
}
printf("About to push light userdata: %p\n",(void*)req);
lua_pushlightuserdata(L,req);//err,name(),ud_req
printf("About to pcall\n");
int err = lua_pcall(L,1,0,-3);
if(err != LUA_OK){
printf("Failed to run %s: %s\n",name,lua_tostring(L,-1));
http_response(req, 500, NULL, 0);
lua_pop(L,lua_gettop(L));
return (KORE_RESULT_OK);
}
return KORE_RESULT_OK;
}
int
post_story(struct http_request *req){
printf("We want to post!\n");
return do_lua(req,"paste");
}
int
edit_story(struct http_request *req){
printf("We want to edit!\n");
return do_lua(req,"edit");
}
int
edit_bio(struct http_request *req){
printf("We want to edit bio!\n");
return do_lua(req,"edit_bio");
}
int
read_story(struct http_request *req){
printf("We want to read!\n");
return do_lua(req,"read");
}
int
login(struct http_request *req){
printf("We want to login!\n");
return do_lua(req,"login");
}
int
claim(struct http_request *req){
printf("We want to claim!\n");
return do_lua(req,"claim");
}
int
home(struct http_request *req){
return do_lua(req,"home");
}
void
kore_worker_configure(void){
printf("Configuring worker...\n");
int err;
/*DIR *dp;*/
/*struct dirent *ep;*/
/*dp = opendir("./");*/
/*if(dp != NULL){*/
/*while(ep = readdir(dp)){*/
/*printf("%s\n",ep->d_name);*/
/*}*/
/*closedir(dp);*/
/*}*/
L = luaL_newstate();
luaL_openlibs(L);
load_kore_libs(L);
load_crypto_libs(L);
lua_pushcfunction(L,errhandeler);
printf("About to run loadfile...\n");
luaL_loadfile(L,SM_INIT);
printf("Done running loadfile...\n");
if(!lua_isfunction(L,-1)){//failed to loadfile()
printf("Failed to load %s: %s\n",SM_INIT,lua_tostring(L,-1));
lua_pop(L,1);
return;
}
printf("About to pcall\n");
err = lua_pcall(L,0,LUA_MULTRET,-2);
printf("Done pcalling\n");
//err = luaL_dofile(L,"init.lua");
if(err){
printf("Failed to run %s\n",SM_INIT);
return;
}
lua_pushcfunction(L,errhandeler);
lua_getglobal(L,"configure");
err = lua_pcall(L,0,0,-2);
if(err != LUA_OK){
printf("Failed to run configure(): %s\n",lua_tostring(L,-1));
lua_pop(L,2);
return;
}
lua_pop(L,lua_gettop(L));
printf("Finished configuring worker\n");
}
void
kore_worker_teardown(void){
int err;
lua_pushcfunction(L,errhandeler);
lua_getglobal(L,"teardown");
err = lua_pcall(L,0,0,1);
if(err != LUA_OK){
printf("Failed to run configure(): %s\n",lua_tostring(L,-1));
lua_pop(L,2);
return;
}
lua_close(L);
}

1
src/smr.h Normal file
View File

@ -0,0 +1 @@
#define SM_INIT "init.lua"