#!/usr/bin/env lua -- this software is licensed under the terms of the GNU affero public license -- v3 or later. view LICENSE.txt for more information. -- if you host your own instance, please change the email address in the about -- page and clearly distinguish your instance from https://zzcxz.citrons.xyz. local env = os.getenv local f = io.open("/dev/urandom", 'r') local e = f and f:read(1) if f then f:close() end math.randomseed(os.time() + string.byte(e)) local function url_encode(str) return (str:gsub("([^A-Za-z0-9%_%.%-%~])", function(v) return string.upper(string.format("%%%02x", string.byte(v))) end)) end local esc_sequences = { ["<"] = "<", [">"] = ">", ['"'] = """ } local function html_encode(x) local escaped = tostring(x) escaped = escaped:gsub("&", "&") for char,esc in pairs(esc_sequences) do escaped = string.gsub(escaped, char, esc) end return escaped end local function parse_qs(str) local function decode(str, path) local str = str if not path then str = str:gsub('+', ' ') end return (str:gsub("%%(%x%x)", function(c) return string.char(tonumber(c, 16)) end)) end local values = {} for key,val in str:gmatch(string.format('([^%q=]+)(=*[^%q=]*)', '&', '&')) do local key = decode(key) local keys = {} key = key:gsub('%[([^%]]*)%]', function(v) -- extract keys between balanced brackets if string.find(v, "^-?%d+$") then v = tonumber(v) else v = decode(v) end table.insert(keys, v) return "=" end) key = key:gsub('=+.*$', "") key = key:gsub('%s', "_") -- remove spaces in parameter name val = val:gsub('^=+', "") if not values[key] then values[key] = {} end if #keys > 0 and type(values[key]) ~= 'table' then values[key] = {} elseif #keys == 0 and type(values[key]) == 'table' then values[key] = decode(val) end local t = values[key] for i,k in ipairs(keys) do if type(t) ~= 'table' then t = {} end if k == "" then k = #t+1 end if not t[k] then t[k] = {} end if i == #keys then t[k] = decode(val) end t = t[k] end end return values end local function redirect(to) return "", { status = '303 see other', headers = { location = to }, } end local function template(str) return function (t) return (str:gsub("$([A-Za-z][A-Za-z0-9]*)", function(v) return t[v] or "" end)) end end local base = template [[
')
code_block = true
else
line = line:gsub("%[(.-)%]",
function(s)
return ('%s'):format(s)
end
)
table.insert(result, ('%s
'):format(line))
end
end
if code_block then
if line:sub(1,1) == ' ' then
table.insert(result, line .. '\n')
else
table.insert(result, '')
code_block = false
end
end
::continue::
end
if code_block then
table.insert(result, '')
code_block = false
end
return table.concat(result), directives
end
local function parse_page(s)
local page = {}
page.title = s:match "^(.-)\n"
page.actions = {}
local content = {}
for line in (s..'\n'):gmatch "(.-\n)" do
if line:sub(1,1) == '\t' then
table.insert(content, line:sub(2))
else
local target, action = line:match "^(%w%w%w%w%w):(.-)\n$"
if action then
table.insert(page.actions, {action = action, target = target})
end
end
end
page.content = table.concat(content)
return page
end
local function load_page(p, raw)
if not p:match("^%w%w%w%w%w$") then return nil end
local f, bee = io.open('content/'..p)
if not f then return nil end
local s = f:read "a"
f:close()
if not s then return nil end
if raw then return s end
return parse_page(s)
end
local function new_action(page, action, result)
local new_name = {}
for i=1,5 do
table.insert(new_name, string.char(string.byte 'a' + math.random(0,25)))
end
new_name = table.concat(new_name)
assert(not io.open('content/'..new_name, 'r'), "page already exists!")
local new = assert(io.open('content/'..new_name, 'w'))
local old = assert(io.open('content/'..page, 'a'))
action = action:gsub('\n', ' ')
assert(new:write(action..'\n'))
for line in (result..'\n'):gmatch "(.-\n)" do
assert(new:write('\t' .. line))
end
assert(old:write(('%s:%s\n'):format(new_name, action)))
local _, directives = convert_markup(result)
if directives.backlinks then
for _,d in ipairs(directives.backlinks) do
assert(new:write(('%s:%s\n'):format(d.page, d.action)))
end
end
new:close()
old:close()
return new_name
end
local map = {}
local page_template = template [[