#!/usr/bin/env lua5.3 -- 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
if directives.redirect then
local r = directives.redirect
return (
'this action will link to ' .. '%s.
' ):format(r, r), directives 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 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 _, directives = convert_markup(result) local old = assert(io.open('content/'..page, 'a')) if directives.redirect then assert(old:write(('%s:%s\n'):format(directives.redirect, action))) old:close() return directives.redirect end ::generate_name:: 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) local exists = io.open('content/'..new_name, 'r') if exists then exists:close() goto generate_name end local new = assert(io.open('content/'..new_name, 'w')) action = action:gsub('\n', ' ') assert(new:write((directives.title or 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))) 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 [[