-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
regalia(acc: &lib.str.acc, rank: uint16)
switch rank do -- TODO customizability
case [uint16](1) then acc:lpush('π') end
case [uint16](2) then acc:lpush('π±') end
case [uint16](3) then acc:lpush('βοΈ') end
case [uint16](4) then acc:lpush('π‘') end
case [uint16](5) then acc:lpush('π') end
else acc:lpush('π΄')
end
end
local splitwords = macro(function(str)
local words = {}
for w in str:asvalue():gmatch('(%g+)') do words[#words + 1] = w end
return `arrayof(pstr, [words])
end)
local rnd = lib.crypt.random
local terra
suggest_handle(a: &lib.str.acc)
var start = a.sz
var puncts = array('.','_','-')
var xXx = rnd(uint8, 0, 30) == 0
var leet = rnd(uint8, 0, 8) == 0
var caps = rnd(uint8, 0, 10)
var punct: rawstring = nil
var useadj = rnd(uint8, 0, 4) == 0
if rnd(uint8, 0, 4) == 0 then
punct = puncts[rnd(intptr,0,[puncts.type.N])]
end
var nouns = splitwords [[
thunder bride blaze doom squad gun lord blaster
fuck hell hound piss shit killa terror horror
fear slaughter murder general commander commissar
terrorist infinity slut cunt whore bitch bastard
cock prince princess pimp gay cop slayer vampire
vampyre blood pain brute wolf sword star sun moon
killer murderer thief arson fire ice frost hack
hacker god master mistress slave rage freeze flayer
pirate ninja shadow fog mist misery glory bear
king queen empress emperor majesty space martian
winter fall monk katana 420 warrior banana demon
devil ghost wraith cuck legend hero heroine goblin
gremlin troll dragon evil overlord radiance slop
operator rage hog bog roach wizard steel madness
reign
]]
var adjs = splitwords [[
dark super supreme ultra ultimate total infinite
omnipotent crazy final deathless immortal elite
leet 1337 bloody fearless headless screaming insane
brutal legendary space frozen flaming burning lazy
mighty flayed hidden secret lost mystery glorious
nude naked bare first radiant martian fallen bog
wandering dank demonic satanic invisible based woke
deadly lethal heroic evil majestic luminous ethereal
perfect first fantastic special great steel insane
royal imperial celestial cosmic mystic sublime
]]
if xXx then a:lpush('xXx_') end
if useadj then
var len = rnd(uint8,1,3)
for i = 0, len do
var sz = a.sz
a:ppush(adjs[rnd(intptr,0,[adjs.type.N])])
if punct ~= nil then a:push(punct, 1) end
if caps == 1 then
a.buf[sz] = lib.str.cupcase(a.buf[sz])
end
end
end
var nounct = rnd(uint8,1,3)
for i = 0, nounct do
var sz = a.sz
a:ppush(nouns[rnd(intptr,0,[nouns.type.N])])
if punct ~= nil and i+1 ~= nounct then a:push(punct, 1) end
if caps == 1 then
a.buf[sz] = lib.str.cupcase(a.buf[sz])
end
end
if leet or caps == 9 then for i=start, a.sz do
if caps == 9 and rnd(uint8,0,5)==0 then
a.buf[i] = lib.str.cupcase(a.buf[i])
end
if leet then
switch lib.str.cdowncase(a.buf[i]) do
case [uint8]([string.byte('e')]) then a.buf[i] = @'3' end
case [uint8]([string.byte('i')]) then a.buf[i] = @'1' end
case [uint8]([string.byte('l')]) then a.buf[i] = @'1' end
case [uint8]([string.byte('t')]) then a.buf[i] = @'7' end
case [uint8]([string.byte('s')]) then a.buf[i] = @'5' end
case [uint8]([string.byte('o')]) then a.buf[i] = @'0' end
case [uint8]([string.byte('b')]) then a.buf[i] = @'6' end
end
end
end end
if (nounct == 1 and not useadj) or rnd(uint8, 0, 5) == 0 then
if punct ~= nil then a:push(punct, 1) end
a:ipush(rnd(uint32,0,lib.math.pow(10,rnd(uint8,1,4))))
end
if xXx then a:lpush('_xXx') end
end
local terra
suggest_domain(a: &lib.str.acc)
var words = splitwords [[
flop slop hop wiggle wriggle bug snoot boop jorts horse rad
witch witches cum code spank grump grumps slap spoop spoopy
spook wobble flip jock nerd dope dork slab drug funk gay
hex node snack weed pot slug worm fur fuzz fuzzy game gamer
rock smack drank wack wild sexy hot sin cock fuck piss man
wank fae weird woke slurp spine skull fail elf elves mom
dad dog cat kitten snake troll top bottom chungus dong wang
420 hog lover lovers best worst love hate big bigger tiny
little teeny spunky jazz wrack rump kink kinky crack meth
whore cam live over under turbo pizza rat rats crotch crank
chunky funky butt grab grabber grabbers thief steal slave
slaves hug hugs hag hags hogs wimp thieves wizard wizards
pussy pansy dark doom stank spunk dumb rage worship orb
terror fear blood slime slab warp waggle tit boob bird derp
birb goat horde masto mastodon social global tweet post
house home prison jail box pit hole haven town trump putin
truth liberty zone land ranch butt butts sex pimp cop mail
slut goblin goblins no good bad only gtfo electro electric
dragon space mars earth venus neptune pluto saturn star
moon lunar catastrophe catastro cuck honk war lap cuddle
planet
]]
var tlds = splitwords [[
tld club town space xxx house land ranch horse com io online
shop site vip ltd win men lgbt cat adult army analytics art
associates bar bible biz black blog broker cam camp careers
catering church city coop dad date dating direct diy dog
duck dot enterprises esq estate expert express fail farm foo
forsale fun fund forum foundation gay global golf gop guru
group hangout hot industries international info investments
jobs land law life limited live lol mom network now party
porn productions pub rehab rocks school sex sexy singles
social software solutions spot store sucks supplies cuck
uwu systems university vacations ventures wang website work
wow wtf world xyz soy live gym park worship orb zone mail
war honk derp planet
]]
var sub = rnd(uint8,0,10) == 0
if sub then a:ppush(words[rnd(intptr,0,[words.type.N])]):lpush('.') end
a:ppush(words[rnd(intptr,0,[words.type.N])])
if rnd(uint8,0,3) == 0 or not sub then
a:ppush(words[rnd(intptr,0,[words.type.N])])
end
a:lpush('.'):ppush(tlds[rnd(intptr,0,[tlds.type.N])])
end
local push_num_field = macro(function(acc,name,lbl,min,max,value,disable)
name = name:asvalue()
lbl = lbl:asvalue()
local start = '<div class="elem small">'
local enabled = start .. string.format('<label for="%s">%s</label><input type="number" id="%s" name="%s" min="', name, lbl, name, name)
local disabled = start .. string.format('<label>%s</label><div class="txtbox">', lbl)
return quote
var decbuf: int8[21]
if disable then
acc:lpush([disabled])
:push(lib.math.decstr(value, &decbuf[20]),0):lpush('</div></div>')
else
acc:lpush([enabled]):push(lib.math.decstr(min, &decbuf[20]),0)
:lpush('" max="'):push(lib.math.decstr(max, &decbuf[20]),0)
:lpush('" value="'):push(lib.math.decstr(value, &decbuf[20]),0):lpush('"></div>')
end
end
end)
local input_pusher = function(kind,wrap,uniq)
local fn = terra(acc: &lib.str.acc, name: pstr, val: pstr, lbl: pstr, on: bool, enabled: bool, class: pstr)
if wrap then acc:lpush('<label>') end
acc:lpush(['<input type="'..kind..'" name="']):ppush(name)
if not wrap then
acc:lpush('" id="'):ppush(name)
if uniq then acc:lpush('-'):ppush(val) end
end
if val:ref() then acc:lpush('" value="'):ppush(val) end
if class:ref() then acc:lpush('" class="'):ppush(class) end
acc:lpush('"')
if on then acc:lpush(' checked') end
if not enabled then acc:lpush(' disabled') end
acc:lpush('>')
if not wrap then acc:lpush('<label for="'):ppush(name)
if uniq then acc:lpush('-'):ppush(val) end
acc:lpush('">')
else acc:lpush(' ') end
acc:ppush(lbl):lpush('</label>')
end
fn.name = string.format('push-input-element<%q>',kind)
return fn
end
local push_checkbox = input_pusher('checkbox',true,false)
local push_pickbox = input_pusher('checkbox',false,false)
local push_radio = input_pusher('radio',false,true)
local mode_local, mode_remote, mode_staff, mode_peers, mode_peons, mode_all = 0,1,2,3,4,5
local terra
render_conf_users(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
if path.ct >= 3 then
var uid, ok = lib.math.shorthand.parse(path(2).ptr,path(2).ct)
if not ok then goto e404 end
var user = co.srv:actor_fetch_uid(uid)
-- FIXME allow xids as well, for manual queries
if not user then goto e404 end
defer user:free()
if not co.who:overpowers(user.ptr) then goto e403 end
if path.ct == 4 then
if path(3):cmp(lib.str.lit'cred') then
var pg = co:stra(1024)
pg:lpush('<div class="context">editing credentials for user <a href="/conf/users/'):rpush(path(2)):lpush('">'):push(user(0).xid,0):lpush('</a></div>')
var credmgr = lib.render.conf.sec(co, uid)
pg:ppush(credmgr)
--credmgr:free()
return pg:finalize()
else goto e404 end
elseif path.ct == 3 then
var cinp = co:stra(256)
cinp:lpush('<div class="elem-group">')
if user.ptr.rights.rank > 0 and (co.who.rights.powers.elevate() or co.who.rights.powers.demote()) then
var max = co.who.rights.rank
if not co.who.rights.powers.elevate() then max = user.ptr.rights.rank end
var min = co.srv.cfg.nranks
if not co.who.rights.powers.demote() then min = user.ptr.rights.rank end
push_num_field(cinp, 'rank', 'rank', max, min, user.ptr.rights.rank, user.ptr.id == co.who.id)
end
if co.who.rights.powers.herald() then
var sanitized: pstr
if user.ptr.epithet == nil
then sanitized = pstr {ptr='', ct=0}
else sanitized = lib.html.sanitize(&co.srv.pool,cs(user.ptr.epithet),true)
end
cinp:lpush('<div class="elem"><label for="epithet">epithet</label><input type="text" id="epithet" name="epithet" value="'):ppush(sanitized):lpush('"></div>')
--if user.ptr.epithet ~= nil then sanitized:free() end
end
if co.who.rights.powers.invite() or co.who.rights.powers.discipline() then
var min: uint32 = 0
if not (co.who.rights.powers.discipline() or
co.who.rights.powers.demote() and co.who.rights.powers.invite())
then min = user.ptr.rights.invites end
var max: uint32 = co.srv.cfg.maxinvites
if not co.who.rights.powers.invite() then max = user.ptr.rights.invites end
push_num_field(cinp, 'invites', 'invites', min, max, user.ptr.rights.invites, false)
end
if co.who.rights.powers.elevate() or co.who.rights.powers.demote() then
var max: uint32 = 5000
if not co.who.rights.powers.elevate() then max = user.ptr.rights.quota end
var min: uint32 = 0
if not co.who.rights.powers.demote() then min = user.ptr.rights.quota end
push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
end
cinp:lpush('</div><div class="elem"><div class="check-panel">')
if user.ptr.id ~= co.who.id and
((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
(user.ptr.rights.rank > 0 and co.who.rights.powers.demote())) then
push_checkbox(&cinp, 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
end
cinp:lpush('</div></div>')
if (co.who.rights.powers.elevate() or
co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
var map = array([lib.store.powmap])
cinp:lpush('<details><summary>powers</summary><div class="pick-list">')
for i=0, [map.type.N] do
if (co.who.rights.powers and map[i].val):sz() > 0 then
var on = (user.ptr.rights.powers and map[i].val):sz() > 0
var enabled = ( on and co.who.rights.powers.demote() ) or
((not on) and co.who.rights.powers.elevate())
var namea: lib.str.acc namea:pcompose(&co.srv.pool,'power-', map[i].name)
var name = namea:finalize()
push_pickbox(&cinp, name, pstr.null(), map[i].name, on, enabled, pstr.null())
--name:free()
end
end
cinp:lpush('</div></details>')
end
if co.who.id ~= uid and co.who.rights.powers.purge() then
var purgeconf = co:stra(48)
var purgestrs = array(
'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'eta', 'nu', 'kappa',
'emerald', 'carnelian', 'sapphire', 'ruby', 'amethyst', 'glory',
'hope', 'grace', 'pearl', 'carnation', 'rose', 'peony', 'poppy'
)
for i=0,3 do
purgeconf:push(purgestrs[lib.crypt.random(intptr,0,[purgestrs.type.N])],0)
if i ~= 2 then purgeconf:lpush('-') end
end
cinp:lpush('<details><summary>purge account</summary><p>you have the authority to destroy this account and all its associated content irreversibly and irretrievably. if you really wish to apply such an extreme sanction, enter the confirmation string <strong style="user-select:none">'):push(purgeconf.buf,purgeconf.sz):lpush('</strong> below and press the βalterβ button to begin the process.</p><div class="elem"><label for="purge">purge confirmation string</label><input type="text" id="purge" name="purgekey"></div><input type="hidden" name="purgestr" value="'):push(purgeconf.buf,purgeconf.sz):lpush('"></details>')
--purgeconf:free()
end
-- TODO black mark system? e.g. resolution option for badthink reports
-- adds a black mark to the offending user; they can be automatically banned
-- or brought up for review after a certain number of offenses; possibly lower
-- set of default privs for marked users
var cinpp = cinp:finalize() --defer cinpp:free()
var unym = co:stra(64)
unym:lpush('<a href="/')
if user(0).origin ~= 0 then unym:lpush('@') end
do var sanxid = lib.html.sanitize(&co.srv.pool,user(0).xid, true)
unym:ppush(sanxid)
--sanxid:free()
end
unym:lpush('" class="id">')
lib.render.nym(user.ptr,0,&unym,false)
unym:lpush('</a>')
var ctlbox = data.view.conf_user_ctl {
name = unym:finalize();
inputcontent = cinpp;
btns = pstr{'',0};
}
if co.who.id ~= uid and co.who.rights.powers.cred() then
ctlbox.btns = lib.str.acc{}:pcompose(&co.srv.pool,'<a class="button" href="/conf/users/',path(2),'/cred">security & credentials</a>'):finalize()
end
var pg = co:stra(512)
ctlbox:append(&pg)
--ctlbox.name:free()
--if ctlbox.btns.ct > 0 then ctlbox.btns:free() end
return pg:finalize()
end
else
var modes = arrayof(pstr,'local', 'remote', 'staff', 'titled', 'peons', 'all')
var idbuf: int8[lib.math.shorthand.maxlen]
var ulst = co:stra(256)
var mode: uint8 = mode_local
var modestr = co:pgetv('show')
ulst:lpush('<div style="text-align: right"><em>showing ')
for i=0,[modes.type.N] do
if modestr:ref() and modes[i]:cmp(modestr) then mode = i end
end
for i=0,[modes.type.N] do
if i > 0 then ulst:lpush(' Β· ') end
if mode == i then
ulst:lpush('<strong>'):ppush(modes[i]):lpush('</strong>')
else
ulst:lpush('<a href="?show='):ppush(modes[i]):lpush('">')
:ppush(modes[i]):lpush('</a>')
end
end
var users: lib.mem.lstptr(lib.store.actor)
if mode == mode_local then
users = co.srv:actor_enum_local()
else
users = co.srv:actor_enum()
end
ulst:lpush('</em></div>')
ulst:lpush('<ul class="user-list">')
for i=0,users.ct do var usr = users(i).ptr
if mode == mode_staff and usr.rights.rank == 0 then goto skip
elseif mode == mode_peons and usr.rights.rank ~= 0 then goto skip
elseif mode == mode_remote and usr.origin == 0 then goto skip
elseif mode == mode_peers and usr.epithet == nil then goto skip end
var idlen = lib.math.shorthand.gen(usr.id, &idbuf[0])
ulst:lpush('<li>')
if usr.rights.rank ~= 0 then
ulst:lpush('<span class="regalia">')
regalia(&ulst, usr.rights.rank)
ulst:lpush('</span>')
end
if co.who:overpowers(usr) then
ulst:lpush('<a class="id" href="users/'):push(&idbuf[0],idlen):lpush('">')
lib.render.nym(usr, 0, &ulst, false)
ulst:lpush('</a></li>')
else
ulst:lpush('<span class="id">')
lib.render.nym(usr, 0, &ulst, false)
ulst:lpush('</span></li>')
end
::skip::end
ulst:lpush('</ul>')
if co.who.rights.powers.invite() or co.who.rights.invites > 0 then
ulst:lpush('<details><summary>create new user</summary><form method="post"><div class="elem"><label for="handle">handle</label><input type="text" name="handle" id="handle" placeholder="')
suggest_handle(&ulst)
ulst:lpush('"></div><button name="act" value="create">create</button></form></details>')
end
ulst:lpush('<details><summary>instantiate remote actor</summary><form method="post"><div class="elem"><label for="xid">xid</label><input type="text" name="xid" id="xid" placeholder="')
suggest_handle(&ulst) ulst:lpush('@') suggest_domain(&ulst)
ulst:lpush('"></div><button name="act" value="inst">instantiate</button></form></details>')
return ulst:finalize()
end
do return pstr.null() end
::e404:: co:complain(404, 'not found', 'there is no user or resource by that identifier on this server') goto quit
::e403:: co:complain(403, 'forbidden', 'you do not have sufficient authority to control that resource')
::quit:: return pstr.null()
end
return render_conf_users