parsav  Check-in [8398fcda5a]

Overview
Comment:add avatar panel
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 8398fcda5a90bf34c663de5c55cd783dd281b0ce08baaa1297d5d59a9ff330c8
User & Date: lexi on 2021-01-10 16:44:33
Other Links: manifest | tags
Context
2021-01-11
01:53
add auth docs and rsa auth check-in: 0d10a378e9 user: lexi tags: trunk
2021-01-10
16:44
add avatar panel check-in: 8398fcda5a user: lexi tags: trunk
14:26
get some user admin shit working, general cleanups check-in: e1ff4f301e user: lexi tags: trunk
Changes

Modified math.t from [60110bc615] to [12858be43d].

     1      1   -- vim: ft=terra
     2      2   local m = {
     3         -	shorthand = {maxlen = 14}
            3  +	shorthand = {maxlen = 14};
            4  +	ll = {
            5  +		ctpop_u8 = terralib.intrinsic('llvm.ctpop.i8', uint8 -> uint8);
            6  +	};
     4      7   }
     5      8   
     6      9   local pstring = lib.mem.ptr(int8)
     7     10   
     8     11   -- swap in place -- faster on little endian
     9     12   m.netswap_ip = macro(function(ty, src, dest)
    10     13   	if ty:astype().type ~= 'integer' then error('bad type') end
................................................................................
    52     55   	elseif ch == 0x3a then ch = 37
    53     56   	elseif ch >= 0x61 and ch <= 0x7a then
    54     57   		ch = 38 + (ch - 0x61)
    55     58   	else return 0, false end
    56     59   
    57     60   	return ch, true 
    58     61   end
           62  +
           63  +terra m.pow(n: intptr, fac: intptr): intptr
           64  +	var o = n
           65  +	for i=0,fac do n = n * o end
           66  +	return n
           67  +end
    59     68   
    60     69   terra m.shorthand.gen(val: uint64, dest: rawstring): ptrdiff
    61     70   	var lst = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz"
    62     71   	var buf: int8[m.shorthand.maxlen]
    63     72   	var ptr = [&int8](buf)
    64     73   	while val ~= 0 do
    65     74   		var v = val % 64

Modified mgtool.t from [b40be7a821] to [600c3c1067].

   115    115   		elseif tmppw[i] >= 10 then
   116    116   			tmppw[i] = tmppw[i] + (0x41 - 10)
   117    117   		else tmppw[i] = tmppw[i] + 0x30 end
   118    118   	end
   119    119   	lib.dbg('assigning temporary password')
   120    120   	dlg:auth_attach_pw(uid, reset,
   121    121   		pstr { ptr = [rawstring](tmppw), ct = 32 },
   122         -		lib.str.plit 'temporary password');
          122  +		'temporary password');
   123    123   end
   124    124   
   125    125   local terra ipc_report(acks: lib.mem.ptr(lib.ipc.ack), rep: rawstring)
   126    126   	var decbuf: int8[21]
   127    127   	for i=0,acks.ct do
   128    128   		var num = lib.math.decstr(acks(i).clid, &decbuf[20])
   129    129   		if acks(i).success then
................................................................................
   379    379   				if cfmode.help then
   380    380   					[ lib.emit(false, 1, 'usage: ', `argv[0], ' mkroot ', cfmode.type.helptxt.flags, ' <handle>', cfmode.type.helptxt.opts) ]
   381    381   					return 1
   382    382   				end
   383    383   				if cfmode.arglist.ct == 1 then
   384    384   					var am = dlg:conf_get('credential-store')
   385    385   					var mg: bool
   386         -					if (not am) or am:cmp(lib.str.plit 'managed') then
          386  +					if (not am) or am:cmp('managed') then
   387    387   						mg = true
   388         -					elseif am:cmp(lib.str.plit 'unmanaged') then
          388  +					elseif am:cmp('unmanaged') then
   389    389   						lib.warn('credential store is unmanaged; you will need to create credentials for the new root user manually!')
   390    390   						mg = false
   391    391   					else lib.bail('unknown credential store mode "',{am.ptr,am.ct},'"; should be either "managed" or "unmanaged"') end
   392    392   					var kbuf: uint8[lib.crypt.const.maxdersz]
   393    393   					var root = lib.store.actor.mk(&kbuf[0])
   394    394   					root.handle = cfmode.arglist(0)
   395    395   					var epithets = array(

Modified parsav.t from [2fffe11917] to [04e1a3fbb9].

   272    272   	set.name = string.format('set<%s>', table.concat(tbl, '|'))
   273    273   	set.metamethods.__entrymissing = macro(function(val, obj)
   274    274   		if o[val] == nil then error('value ' .. val .. ' not in set') end
   275    275   		return `bit { _v=[o[val] - 1], _set = &(obj) }
   276    276   	end)
   277    277   	terra set:sz()
   278    278   		var ct: intptr = 0
          279  +		--for i = 0, [math.floor(#tbl/8)] do
          280  +		--	ct = ct + lib.math.ll.ctpop_u8(self._store[i])
          281  +		--end
          282  +		--[(function()
          283  +		--	if #tbl % 8 ~= 0 then
          284  +		--		local last = #tbl-1
          285  +		--		local msk = (2 ^ (#tbl % 8)) - 1
          286  +		--		return quote ct = ct + lib.math.ll.ctpop_u8(self._store[last] and [uint8](msk)) end
          287  +		--	else return {} end
          288  +		--end)()]
   279    289   		for i = 0, [#tbl] do
   280    290   			if (self._store[i/8] and (1 << i % 8)) ~= 0 then ct = ct + 1 end
   281    291   		end
   282    292   		return ct
   283    293   	end
   284    294   	set.methods.dump = macro(function(self)
   285    295   		local q = quote lib.io.say('dumping set:\n') end
................................................................................
   451    461   	'render:media-gallery';
   452    462   
   453    463   	'render:docpage';
   454    464   
   455    465   	'render:conf:profile';
   456    466   	'render:conf:sec';
   457    467   	'render:conf:users';
          468  +	'render:conf:avi';
   458    469   	'render:conf';
   459    470   	'route';
   460    471   }
   461    472   
   462    473   do
   463    474   	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
   464    475   	terra version() lib.io.send(1, p, [#p]) end

Modified render/compose.t from [95dc7dcbc1] to [4959831ced].

    15     15   		form.acl = edit.acl
    16     16   	end
    17     17   	if acc ~= nil then form:append(acc) return end 
    18     18   
    19     19   	var cotxt = form:poolstr(&co.srv.pool) -- defer cotxt:free()
    20     20   
    21     21   	var doc = [lib.srv.convo.page] {
    22         -		title = lib.str.plit 'compose';
           22  +		title = 'compose';
    23     23   		body = cotxt;
    24         -		class = lib.str.plit 'compose';
           24  +		class = 'compose';
    25     25   		cache = true;
    26     26   	}
    27     27   
    28     28   	co:stdpage(doc)
    29     29   end
    30     30   
    31     31   return render_compose

Modified render/conf.t from [cd79efba6f] to [a292c2030a].

     1      1   -- vim: ft=terra
     2      2   local pstr = lib.mem.ptr(int8)
     3      3   local pref = lib.mem.ref(int8)
     4      4   
     5      5   local mappings = {
     6      6   	{url = 'profile', title = 'account profile', render = 'profile'};
     7         -	{url = 'avi', title = 'avatar', render = 'avatar'};
            7  +	{url = 'avi', title = 'avatar', render = 'avi'};
     8      8   	{url = 'ui', title = 'user interface', render = 'ui'};
     9      9   	{url = 'sec', title = 'security', render = 'sec_overlay'};
    10     10   	{url = 'rel', title = 'relationships', render = 'rel'};
    11     11   	{url = 'qnt', title = 'quarantine', render = 'quarantine'};
    12     12   	{url = 'acl', title = 'access control shortcuts', render = 'acl'};
    13     13   	{url = 'rooms', title = 'chatrooms', render = 'rooms'};
    14     14   	{url = 'circles', title = 'circles', render = 'circles'};
................................................................................
    83     83   		pg:append(&fnpg)
    84     84   		pgt = fnpg:finalize()
    85     85   	else pgt = pg:poolstr(&co.srv.pool) end
    86     86   	--defer pgt:free()
    87     87   
    88     88   	co:stdpage([lib.srv.convo.page] {
    89     89   		title = 'configure'; body = pgt;
    90         -		class = lib.str.plit 'conf';
           90  +		class = 'conf';
    91     91   		cache = false;
    92     92   	})
    93     93   
    94     94   	--if panel.ct ~= 0 then panel:free() end
    95     95   end
    96     96   
    97     97   return render_conf

Added render/conf/avi.t version [154a292a80].

            1  +-- vim: ft=terra
            2  +local pstr = lib.mem.ptr(int8)
            3  +local pref = lib.mem.ref(int8)
            4  +
            5  +local terra 
            6  +render_conf_avi(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
            7  +	var a = co:stra(128)
            8  +	a:lpush '<form method="post"><p>this is your current avatar. if you want to change it, you can reset it to the default, or pick a new image in your <a href="/media">media library</a> to represent yourself with.</p><img class="avatar big" src="/avi/':push(co.who.handle,0):lpush '">'
            9  +	if co.who.avatarid ~= 0 then
           10  +		a:lpush '<menu class="vertical choice"><button name="act" value="clear">use default avatar</button><a class="button" href="/media/a/':shpush(co.who.avatarid):lpush '">open image</a></menu>'
           11  +	end
           12  +	return a:finalize()
           13  +end
           14  +
           15  +return render_conf_avi

Modified render/conf/sec.t from [d16c1b9a13] to [157639932a].

    35     35   					end
    36     36   				end
    37     37   				credmgr.credlist = cl:finalize()
    38     38   			end
    39     39   			credmgr:append(&a)
    40     40   			--if credmgr.credlist.ct > 0 then credmgr.credlist:free() end
    41     41   		else
    42         -			if new:cmp(lib.str.plit'pw') then
           42  +			if new:cmp('pw') then
    43     43   				var d: data.view.conf_sec_pwnew
    44     44   				var time = lib.osclock.time(nil)
    45     45   				var timestr: int8[26] lib.osclock.ctime_r(&time, &timestr[0])
    46     46   				var cmt = co:stra(48)
    47     47   				cmt:lpush('enrolled over http on '):push(&timestr[0],0)
    48     48   				d.comment = cmt:finalize()
    49     49   
    50     50   				var st = d:poolstr(&co.srv.pool)
    51     51   				--d.comment:free()
    52     52   				return st
    53         -			elseif new:cmp(lib.str.plit'challenge') then
           53  +			elseif new:cmp('challenge') then
    54     54   			-- we're going to break the rules a bit and do database munging from
    55     55   			-- the rendering code, because doing otherwise in this case would be
    56     56   			-- genuinely nightmarish
    57         -			elseif new:cmp(lib.str.plit'otp') then
    58         -			elseif new:cmp(lib.str.plit'api') then
           57  +			elseif new:cmp('otp') then
           58  +			elseif new:cmp('api') then
    59     59   			else return pstr.null() end
    60     60   		end
    61     61   	else body:append(&a) end
    62     62   
    63     63   	return a:finalize()
    64     64   end
    65     65   

Modified render/conf/users.t from [7c5c858095] to [88e528c638].

     1      1   -- vim: ft=terra
     2      2   local pstr = lib.mem.ptr(int8)
     3      3   local pref = lib.mem.ref(int8)
     4         -local P = lib.str.plit
     5      4   
     6      5   local terra cs(s: rawstring)
     7      6   	return pstr { ptr = s, ct = lib.str.sz(s) }
     8      7   end
     9      8   
    10      9   local terra 
    11     10   regalia(acc: &lib.str.acc, rank: uint16)
................................................................................
    14     13   		case [uint16](2) then acc:lpush('🔱') end
    15     14   		case [uint16](3) then acc:lpush('⚜️') end
    16     15   		case [uint16](4) then acc:lpush('🗡') end
    17     16   		case [uint16](5) then acc:lpush('🗝') end
    18     17   		else acc:lpush('🕴')
    19     18   	end
    20     19   end
           20  +
           21  +local splitwords = macro(function(str)
           22  +	local words = {}
           23  +	for w in str:asvalue():gmatch('(%g+)') do words[#words + 1] = w end
           24  +	return `arrayof(pstr, [words])
           25  +end)
    21     26   
    22     27   local rnd = lib.crypt.random
    23     28   local terra 
    24     29   suggest_handle(a: &lib.str.acc)
    25     30   	var start = a.sz
    26     31   	var puncts = array('.','_','-')
    27     32   	var xXx = rnd(uint8, 0, 9) == 0
    28     33   	var leet = rnd(uint8, 0, 8) == 0
    29         -	var caps = rnd(uint8, 0, 5)
           34  +	var caps = rnd(uint8, 0, 10)
    30     35   	var punct: rawstring = nil
    31     36   	var useadj = rnd(uint8, 0, 4) == 0
    32     37   	if rnd(uint8, 0, 4) == 0 then
    33     38   		punct = puncts[rnd(intptr,0,[puncts.type.N])]
    34     39   	end
    35     40   
    36         -	var nouns = array(
    37         -		'thunder','bride','blaze','doom','squad','gun','lord','blaster',
    38         -		'fuck','hell','hound','piss','shit','killa','terror', 'horror',
    39         -		'fear', 'slaughter','murder','general','commander', 'commissar',
    40         -		'terrorist','infinity','slut','cunt','whore','bitch', 'bastard',
    41         -		'cock','prince','princess','pimp','gay','cop','slayer', 'vampire',
    42         -		'vampyre','blood','pain','brute','wolf','sword','star','sun','moon',
    43         -		'killer','murderer','thief','arson','fire','ice','frost','hack',
    44         -		'hacker','god','master','mistress','slave','rage','freeze','flayer',
    45         -		'pirate','ninja','shadow','fog','mist','misery','glory','bear',
    46         -		'king','queen','empress','emperor','majesty','space','martian',
    47         -		'winter','fall','monk','katana','420','warrior','banana','demon',
    48         -		'devil','ghost','wraith','cuck','legend','hero','heroine','goblin',
    49         -		'gremlin','troll','dragon','evil','overlord','radiance'
    50         -	)
    51         -	var adjs = array(
    52         -		'dark','super','supreme','ultra','ultimate','total','infinite',
    53         -		'omnipotent','crazy','final','deathless','immortal', 'elite',
    54         -		'leet','1337','bloody','fearless','headless','screaming','insane',
    55         -		'brutal','legendary','space','frozen','flaming','burning',
    56         -		'mighty','flayed','hidden','secret','lost','mystery','glorious',
    57         -		'nude','naked','bare','first','radiant','martian','fallen',
    58         -		'wandering','dank','demonic','satanic','invisible','based','woke',
    59         -		'deadly','lethal','heroic','evil','majestic','luminous'
    60         -	)
           41  +	var nouns = splitwords [[
           42  +		thunder bride blaze doom squad gun lord blaster
           43  +		fuck hell hound piss shit killa terror horror
           44  +		fear slaughter murder general commander commissar
           45  +		terrorist infinity slut cunt whore bitch bastard
           46  +		cock prince princess pimp gay cop slayer vampire
           47  +		vampyre blood pain brute wolf sword star sun moon
           48  +		killer murderer thief arson fire ice frost hack
           49  +		hacker god master mistress slave rage freeze flayer
           50  +		pirate ninja shadow fog mist misery glory bear
           51  +		king queen empress emperor majesty space martian
           52  +		winter fall monk katana 420 warrior banana demon
           53  +		devil ghost wraith cuck legend hero heroine goblin
           54  +		gremlin troll dragon evil overlord radiance slop
           55  +		operator rage hog bog roach wizard
           56  +	]]
           57  +	var adjs = splitwords [[
           58  +		dark super supreme ultra ultimate total infinite
           59  +		omnipotent crazy final deathless immortal elite
           60  +		leet 1337 bloody fearless headless screaming insane
           61  +		brutal legendary space frozen flaming burning
           62  +		mighty flayed hidden secret lost mystery glorious
           63  +		nude naked bare first radiant martian fallen bog
           64  +		wandering dank demonic satanic invisible based woke
           65  +		deadly lethal heroic evil majestic luminous ethereal
           66  +	]]
    61     67   
    62     68   	if xXx then a:lpush('xXx_') end
    63     69   
    64     70   	if useadj then
    65     71   		var len = rnd(uint8,1,3) 
    66     72   		for i = 0, len do
    67     73   			var sz = a.sz
    68         -			a:push(adjs[rnd(intptr,0,[adjs.type.N])], 0)
           74  +			a:ppush(adjs[rnd(intptr,0,[adjs.type.N])])
    69     75   			if punct ~= nil then a:push(punct, 1) end
    70     76   			if caps == 1 then
    71     77   				a.buf[sz] = lib.str.cupcase(a.buf[sz])
    72     78   			end
    73     79   		end
    74     80   	end
    75     81   	var nounct = rnd(uint8,1,3) 
    76     82   	for i = 0, nounct do
    77     83   		var sz = a.sz
    78         -		a:push(nouns[rnd(intptr,0,[nouns.type.N])], 0)
           84  +		a:ppush(nouns[rnd(intptr,0,[nouns.type.N])])
    79     85   		if punct ~= nil and i+1 ~= nounct then a:push(punct, 1) end
    80     86   		if caps == 1 then
    81     87   			a.buf[sz] = lib.str.cupcase(a.buf[sz])
    82     88   		end
    83     89   	end
    84     90   
    85         -	if leet or caps == 2 then for i=start, a.sz do
    86         -		if caps == 2 and rnd(uint8,0,5)==0 then
           91  +	if leet or caps == 9 then for i=start, a.sz do
           92  +		if caps == 9 and rnd(uint8,0,5)==0 then
    87     93   			a.buf[i] = lib.str.cupcase(a.buf[i])
    88     94   		end
    89     95   		if leet then 
    90     96   			switch lib.str.cdowncase(a.buf[i]) do
    91     97   				case [uint8]([string.byte('e')]) then a.buf[i] = @'3' end
    92     98   				case [uint8]([string.byte('i')]) then a.buf[i] = @'1' end
    93     99   				case [uint8]([string.byte('l')]) then a.buf[i] = @'1' end
................................................................................
    97    103   				case [uint8]([string.byte('b')]) then a.buf[i] = @'6' end
    98    104   			end
    99    105   		end
   100    106   	end end
   101    107   
   102    108   	if (nounct == 1 and not useadj) or rnd(uint8, 0, 5) == 0 then
   103    109   		if punct ~= nil then a:push(punct, 1) end
   104         -		a:ipush(rnd(uint16,0,65535))
          110  +		a:ipush(rnd(uint32,0,lib.math.pow(10,rnd(uint8,1,4))))
   105    111   	end
   106    112   
   107    113   	if xXx then a:lpush('_xXx') end
   108    114   
   109    115   end
   110    116   
   111    117   local terra 
   112    118   suggest_domain(a: &lib.str.acc)
   113         -	var tlds = array('tld','club','town','space','xxx')
          119  +	var words = splitwords [[
          120  +		flop slop hop wiggle wriggle bug snoot boop jorts horse rad
          121  +		witch witches cum code spank grump grumps slap spoop spoopy
          122  +		spook wobble flip jock nerd dope dork slab drug funk gay
          123  +		hex node snack weed pot slug worm fur fuzz fuzzy game gamer
          124  +		rock smack drank wack wild sexy hot sin cock fuck piss man
          125  +		wank fae weird woke slurp spine skull fail elf elves mom
          126  +		dad dog cat kitten snake troll top bottom chungus dong wang
          127  +		420 hog lover lovers best worst love hate big bigger tiny
          128  +		little teeny spunky jazz wrack rump kink kinky crack meth
          129  +		whore cam live over under turbo pizza rat rats crotch crank
          130  +		chunky funky butt grab grabber grabbers thief steal slave
          131  +		slaves hug hugs hag hags hogs wimp thieves wizard wizards
          132  +		pussy pansy dark doom stank spunk dumb rage
          133  +	]]
          134  +	var tlds = splitwords [[
          135  +		tld club town space xxx house land ranch horse com io online
          136  +		shop site vip ltd win men lgbt cat adult army analytics art
          137  +		associates bar bible biz black blog broker cam camp careers
          138  +		catering church city coop dad date dating direct diy dog
          139  +		duck dot enterprises esq estate expert express fail farm foo
          140  +		forsale fun fund forum foundation gay global golf gop guru
          141  +		group hangout hot industries international info investments
          142  +		jobs land law life limited live lol mom network now party
          143  +		porn productions pub rehab rocks school sex sexy singles
          144  +		social software solutions space spot store sucks supplies
          145  +		systems university vacations ventures wang website work
          146  +		wow wtf world xyz soy live gym park 
          147  +	]]
          148  +	var sub = rnd(uint8,0,10) == 0
          149  +	if sub then a:ppush(words[rnd(intptr,0,[words.type.N])]):lpush('.') end
          150  +	a:ppush(words[rnd(intptr,0,[words.type.N])])
          151  +	if rnd(uint8,0,3) == 0 or not sub then
          152  +		a:ppush(words[rnd(intptr,0,[words.type.N])])
          153  +	end
          154  +	a:lpush('.'):ppush(tlds[rnd(intptr,0,[tlds.type.N])])
   114    155   end
   115    156   
   116    157   local push_num_field = macro(function(acc,name,lbl,min,max,value,disable)
   117    158   	name = name:asvalue()
   118    159   	lbl = lbl:asvalue()
   119    160   	local start = '<div class="elem small">'
   120    161   	local enabled = start .. string.format('<label for="%s">%s</label><input type="number" id="%s" name="%s" min="', name, lbl, name, name)
................................................................................
   290    331   			ctlbox:append(&pg)
   291    332   			--ctlbox.name:free()
   292    333   			--if ctlbox.btns.ct > 0 then ctlbox.btns:free() end
   293    334   
   294    335   			return pg:finalize()
   295    336   		end
   296    337   	else
   297         -		var modes = array(P'local', P'remote', P'staff', P'titled', P'peons', P'all')
          338  +		var modes = arrayof(pstr,'local', 'remote', 'staff', 'titled', 'peons', 'all')
   298    339   		var idbuf: int8[lib.math.shorthand.maxlen]
   299    340   		var ulst = co:stra(256)
   300    341   		var mode: uint8 = mode_local
   301    342   		var modestr = co:pgetv('show')
   302    343   		ulst:lpush('<div style="text-align: right"><em>showing ')
   303    344   		for i=0,[modes.type.N] do
   304    345   			if modestr:ref() and modes[i]:cmp(modestr) then mode = i end
................................................................................
   345    386   		ulst:lpush('</ul>')
   346    387   
   347    388   		if co.who.rights.powers.invite() or co.who.rights.invites > 0 then
   348    389   			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="')
   349    390   			suggest_handle(&ulst)
   350    391   			ulst:lpush('"></div><button name="act" value="create">create</button></form></details>')
   351    392   		end
   352         -		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="tweetlord@website.tld"></div><button name="act" value="inst">instantiate</button></form></details>')
          393  +		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="')
          394  +		suggest_handle(&ulst) ulst:lpush('@') suggest_domain(&ulst)
          395  +		ulst:lpush('"></div><button name="act" value="inst">instantiate</button></form></details>')
   353    396   
   354    397   		return ulst:finalize()
   355    398   	end
   356    399   	do return pstr.null() end
   357    400   	::e404:: co:complain(404, 'not found', 'there is no user or resource by that identifier on this server') goto quit
   358    401   	::e403:: co:complain(403, 'forbidden', 'you do not have sufficient authority to control that resource')
   359    402   
   360    403   	::quit:: return pstr.null()
   361    404   end
   362    405   
   363    406   return render_conf_users

Modified render/docpage.t from [9f2f406de7] to [704653b9d0].

     1      1   -- vim: ft=terra
     2      2   local page = lib.srv.convo.page
     3      3   local pstr = lib.mem.ptr(int8)
     4      4   local pref = lib.mem.ref(int8)
     5         -local P = lib.str.plit
     6         -local R = lib.str.lit
     7      5   
     8      6   local topics = lib.util.keys(data.doc)
     9      7   local topicidxt = {}
    10      8   table.sort(topics) -- because deterministic builds are good
    11      9   local branches = {}
    12     10   for i,k in pairs(topics) do
    13     11   	topicidxt[k] = i
................................................................................
    36     34   	if t.meta.priv then
    37     35   		if type(t.meta.priv) ~= 'table' then t.meta.priv = {t.meta.priv} end
    38     36   		for _,v in pairs(t.meta.priv) do
    39     37   			setbits = quote [setbits]; (restrict.[v] << true) end
    40     38   		end
    41     39   	end
    42     40   	allpages[i] = quote var [restrict]; [setbits] in pgpair {
    43         -		name = R(v);
           41  +		name = [v];
    44     42   		parent = par;
    45     43   		priv = restrict;
    46         -		title = R(t.meta.title);
           44  +		title = [t.meta.title];
    47     45   		content = page {
    48     46   			title = ['documentation :: ' .. t.meta.title];
    49     47   			body = [ t.text ];
    50         -			class = P'doc article';
           48  +			class = 'doc article';
    51     49   			cache = true;
    52     50   		};
    53     51   	} end
    54     52   end
    55     53   
    56     54   local terra 
    57     55   showpage(co: &lib.srv.convo, id: pref)
................................................................................
   107    105   			end
   108    106   		end
   109    107   		list:lpush('</ul>')
   110    108   
   111    109   		co:stdpage(page {
   112    110   			title = 'documentation';
   113    111   			body = list:finalize();
   114         -			class = P'doc listing';
          112  +			class = 'doc listing';
   115    113   			cache = false;
   116    114   		})
   117    115   	else showpage(co, pg) end
   118    116   end
   119    117   
   120    118   return render_docpage

Modified render/login.t from [7ea4ccf2b4] to [434636bebc].

     1      1   -- vim: ft=terra
     2      2   local pstr = lib.mem.ptr(int8)
     3         -local P = lib.str.plit
     4      3   local terra 
     5      4   login_form(co: &lib.srv.convo, user: &lib.store.actor, creds: &lib.store.credset, msg: pstr)
     6      5   	var doc = [lib.srv.convo.page] {
     7         -		title = lib.str.plit 'instance logon';
     8         -		class = lib.str.plit 'login';
            6  +		title = 'instance logon';
            7  +		class = 'login';
     9      8   		cache = false;
    10      9   	}
    11     10   
    12     11   	if user == nil then
    13     12   		var form = data.view.login_username {
    14     13   			loginmsg = msg;
    15     14   		}
    16     15   		if form.loginmsg.ptr == nil then
    17         -			form.loginmsg = lib.str.plit 'identify yourself for access to this instance.'
           16  +			form.loginmsg = 'identify yourself for access to this instance.'
    18     17   		end
    19     18   		doc.body = form:tostr()
    20     19   	elseif creds:sz() == 0 then
    21     20   		co:complain(403,'access denied','your host is not eligible to authenticate as this user')
    22     21   		return
    23     22   	elseif creds:sz() == 1 then
    24     23   		if creds.trust() then
................................................................................
    27     26   		end
    28     27   
    29     28   		var ch = data.view.login_challenge {
    30     29   			handle = user.handle;
    31     30   			name = lib.coalesce(user.nym, user.handle);
    32     31   		}
    33     32   		if creds.pw() then
    34         -			ch.challenge = P'enter the password associated with your account'
    35         -			ch.label = P'password'
    36         -			ch.method = P'pw'
    37         -			ch.auto = P'current-password';
           33  +			ch.challenge = 'enter the password associated with your account'
           34  +			ch.label = 'password'
           35  +			ch.method = 'pw'
           36  +			ch.auto = 'current-password';
    38     37   		elseif creds.otp() then
    39         -			ch.challenge = P'enter a valid one-time password for your account'
    40         -			ch.label = P'OTP code'
    41         -			ch.method = P'otp'
    42         -			ch.auto = P'one-time-code';
           38  +			ch.challenge = 'enter a valid one-time password for your account'
           39  +			ch.label = 'OTP code'
           40  +			ch.method = 'otp'
           41  +			ch.auto = 'one-time-code';
    43     42   		elseif creds.challenge() then
    44         -			ch.challenge = P'sign the challenge token: <code>...</code>'
    45         -			ch.label = P'digest'
    46         -			ch.method = P'challenge'
    47         -			ch.auto = P'one-time-code';
           43  +			ch.challenge = 'sign the challenge token: <code>...</code>'
           44  +			ch.label = 'digest'
           45  +			ch.method = 'challenge'
           46  +			ch.auto = 'one-time-code';
    48     47   		else
    49     48   			co:complain(500,'login failure','unknown login method')
    50     49   			return
    51     50   		end
    52     51   
    53     52   		doc.body = ch:tostr()
    54     53   	else

Modified render/media-gallery.t from [18e4a71c1d] to [1f18c83945].

     1      1   -- vim: ft=terra
     2      2   local pstr = lib.str.t 
     3         -local P = lib.str.plit
     4      3   local terra cs(s: rawstring)
     5      4   	return pstr { ptr = s, ct = lib.str.sz(s) }
     6      5   end
     7      6   
     8      7   local show_all,show_new,show_unfiled,show_files,show_vid,show_img=1,2,3,4,5,6
     9      8   
    10      9   local terra 
................................................................................
    21     20   			var pa = co:stra(32)
    22     21   			pa:lpush('/')
    23     22   			if ou(0).origin ~= 0 then pa:lpush('@') end
    24     23   			pa:push(ou(0).xid,0)
    25     24   			pfx = pa:finalize()
    26     25   		end
    27     26   
    28         -		if path.ct >= 3 and path(1):cmp(lib.str.lit'a') then
           27  +		if path.ct >= 3 and path(1):cmp('a') then
    29     28   			var id, idok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
    30     29   			if not idok then goto e404 end
    31     30   			var art = co.srv:artifact_fetch(uid, id)
    32     31   			if not art then goto e404 end
    33     32   			if path.ct == 3 then
    34     33   			-- sniff out the artifact type and display the appropriate viewer
    35     34   				var artid = cs(art(0).url)
................................................................................
    45     44   					pfx = pfx, desc = desc;
    46     45   					id = artid; btns = btntxt;
    47     46   				}
    48     47   				if lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then
    49     48   					var view = data.view.media_image(viewerprops)
    50     49   					var pg = view:poolstr(&co.srv.pool)
    51     50   					co:stdpage([lib.srv.convo.page] {
    52         -						title = lib.str.plit'media :: image';
    53         -						class = lib.str.plit'media viewer img';
           51  +						title = 'media :: image';
           52  +						class = 'media viewer img';
    54     53   						cache = false, body = pg;
    55     54   					})
    56     55   					--pg:free()
    57     56   				elseif lib.str.cmp(art(0).mime, 'text/markdown') == 0 then
    58     57   					var view = data.view.media_text(viewerprops)
    59     58   					var text, mime = co.srv:artifact_load(id) mime:free()
    60     59   					view.text = lib.smackdown.html(&co.srv.pool, pstr{[rawstring](text.ptr),text.ct}, false)
    61     60   					text:free()
    62     61   					var pg = view:poolstr(&co.srv.pool)
    63     62   					--view.text:free()
    64     63   					co:stdpage([lib.srv.convo.page] {
    65         -						title = lib.str.plit'media :: text';
    66         -						class = lib.str.plit'media viewer text';
           64  +						title = 'media :: text';
           65  +						class = 'media viewer text';
    67     66   						cache = false, body = pg;
    68     67   					})
    69     68   					--pg:free()
    70     69   				elseif
    71     70   					lib.str.ncmp(art(0).mime, 'text/', 5) == 0          or
    72     71   					lib.str.cmp(art(0).mime, 'application/x-perl') == 0 or
    73     72   					lib.str.cmp(art(0).mime, 'application/sql') == 0
................................................................................
    78     77   					var san = lib.html.sanitize(&co.srv.pool,pstr{[rawstring](text.ptr),text.ct}, false)
    79     78   					text:free()
    80     79   					view.text = co:qstr('<pre>',san,'</pre>')
    81     80   					--san:free()
    82     81   					var pg = view:poolstr(&co.srv.pool)
    83     82   					--view.text:free()
    84     83   					co:stdpage([lib.srv.convo.page] {
    85         -						title = lib.str.plit'media :: text';
    86         -						class = lib.str.plit'media viewer text';
           84  +						title = 'media :: text';
           85  +						class = 'media viewer text';
    87     86   						cache = false, body = pg;
    88     87   					})
    89     88   					--pg:free()
    90     89   				else co:complain(500,'bad file type','this file type is not supported') end
    91     90   			elseif path.ct == 4 then
    92     91   				var act = path(3)
    93     92   				var curl = co:qstr('/media/a/', path(2))
    94     93   				-- defer curl:free()
    95         -				if act:cmp(lib.str.lit'avi') and lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then
           94  +				if act:cmp('avi') and lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then
    96     95   					co:confirm('set avatar', 'are you sure you want this image to be your new avatar?',curl)
    97         -				elseif act:cmp(lib.str.lit'del') then
           96  +				elseif act:cmp('del') then
    98     97   					co:confirm('delete', 'are you sure you want to permanently delete this artifact?',curl)
    99     98   				else goto e404 end
   100     99   			end
   101    100   		else
   102    101   			var mode: uint8 = show_new
   103    102   			var folder: pstr
   104    103   			if path.ct == 2 then
   105         -				if path(1):cmp(lib.str.lit'unfiled') then
          104  +				if path(1):cmp('unfiled') then
   106    105   					mode=show_unfiled
   107         -				elseif path(1):cmp(lib.str.lit'all') then
          106  +				elseif path(1):cmp('all') then
   108    107   					mode=show_all
   109    108   				else goto e404 end
   110         -			elseif path.ct == 3 and path(1):cmp(lib.str.lit'kind') then
          109  +			elseif path.ct == 3 and path(1):cmp('kind') then
   111    110   			end
   112    111   
   113    112   			var folders = co.srv:artifact_folder_enum(uid)
   114    113   
   115    114   			if mode == show_new then
   116         -				folder = lib.str.plit''
          115  +				folder = ''
   117    116   			elseif mode == show_all or mode == show_unfiled then
   118    117   				folder = pstr.null()
   119    118   			end
   120    119   
   121    120   			var view = data.view.media_gallery {
   122    121   				menu = pstr{'',0};
   123    122   				folders = pstr{'',0};
................................................................................
   142    141   				end
   143    142   				fa:lpush('<hr>')
   144    143   				view.folders = fa:finalize()
   145    144   				folders:free()
   146    145   			end
   147    146   
   148    147   			if owner then
   149         -				view.menu = P'<a class="pos" href="/media/upload">upload</a><hr>'
          148  +				view.menu = '<a class="pos" href="/media/upload">upload</a><hr>'
   150    149   			end
   151    150   
   152    151   			var md = co.srv:artifact_enum_uid(uid, folder)
   153    152   			var gallery: lib.str.acc gallery:pool(&co.srv.pool,256)
   154    153   			var files: lib.str.acc files:pool(&co.srv.pool,256) 
   155    154   			for i=0,md.ct do
   156    155   				var desc = lib.smackdown.html(&co.srv.pool,pstr{md(i)(0).desc,0}, true) --defer desc:free()
................................................................................
   173    172   			view.directory = files:finalize()
   174    173   
   175    174   			if acc ~= nil then
   176    175   				view:append(acc)
   177    176   			else
   178    177   				var pg = view:poolstr(&co.srv.pool) -- defer pg:free()
   179    178   				co:stdpage([lib.srv.convo.page] {
   180         -					title = P'media';
   181         -					class = P'media manager';
          179  +					title = 'media';
          180  +					class = 'media manager';
   182    181   					cache = false;
   183    182   					body = pg;
   184    183   				})
   185    184   			end
   186    185   
   187    186   			--view.images:free()
   188    187   			--view.directory:free()

Modified render/notices.t from [1e81acf0af] to [75a5e28d82].

     1      1   -- vim: ft=terra
     2      2   local pstr = lib.mem.ptr(int8)
     3         -local P = lib.str.plit
     4      3   local terra cs(s: rawstring)
     5      4   	return pstr { ptr = s, ct = lib.str.sz(s) }
     6      5   end
     7      6   
     8      7   local terra 
     9      8   render_notices(
    10      9   	co: &lib.srv.convo
................................................................................
    33     32   			avatar = cs(who(0).avatar);
    34     33   			nym = lib.render.nym(who.ptr,0,nil,true);
    35     34   			pflink = pstr{ptr = pflink.buf; ct = pflink.sz};
    36     35   		}
    37     36   		var notweet, nopost = true, false
    38     37   		switch notes(i).kind do
    39     38   			case lib.store.noticetype.rt then
    40         -				n.kind = P'rt'
    41         -				n.act = P'retweeted your post'
           39  +				n.kind = 'rt'
           40  +				n.act = 'retweeted your post'
    42     41   			end
    43     42   			case lib.store.noticetype.like then
    44         -				n.kind = P'like'
    45         -				n.act = P'likes your post'
           43  +				n.kind = 'like'
           44  +				n.act = 'likes your post'
    46     45   			end
    47     46   			case lib.store.noticetype.reply then
    48         -				n.kind = P'reply'
    49         -				n.act = P'replied to your post'
           47  +				n.kind = 'reply'
           48  +				n.act = 'replied to your post'
    50     49   				notweet = false
    51     50   			end
    52     51   			case lib.store.noticetype.follow then
    53         -				n.kind = P'follow'
    54         -				n.act = P'followed you!'
           52  +				n.kind = 'follow'
           53  +				n.act = 'followed you!'
    55     54   				nopost = true
    56     55   			end
    57     56   		else goto skip end
    58     57   		if not nopost then
    59     58   			var what = co.srv:post_fetch(notes(i).what) defer what:free()
    60     59   			var b = lib.smackdown.html(&co.srv.pool, pstr {ptr=what(0).body,ct=0},true) --defer b:free()
    61     60   			body:lpush(' <a class="quote" href="/post/'):shpush(notes(i).what):lpush('">'):ppush(b):lpush('</a>')
................................................................................
    71     70   		::skip:: n.nym:free()
    72     71   		         pflink:reset()
    73     72   				 body:reset()
    74     73   	end
    75     74   	--pflink:free()
    76     75   	pg:lpush('<form method="post"><button name="act" value="clear">clear all notices</button></form>')
    77     76   	co:livepage([lib.srv.convo.page] {
    78         -		title = P'notices', class = P'notices';
           77  +		title = 'notices', class = 'notices';
    79     78   		body = pstr {ptr = pg.buf, ct = pg.sz};
    80     79   		cache = false;
    81     80   	}, latest)
    82     81   end
    83     82   
    84     83   return render_notices

Modified render/profile.t from [849244d87f] to [f79f7a1ea9].

    32     32   
    33     33   	var strfbuf: int8[28*4]
    34     34   	var stats = co.srv:actor_stats(actor.id)
    35     35   		var sn_posts     = cs(lib.math.decstr_friendly(stats.posts, &strfbuf[ [strfbuf.type.N - 1] ]))
    36     36   		var sn_follows   = cs(lib.math.decstr_friendly(stats.follows, sn_posts.ptr - 1))
    37     37   		var sn_followers = cs(lib.math.decstr_friendly(stats.followers, sn_follows.ptr - 1))
    38     38   		var sn_mutuals   = cs(lib.math.decstr_friendly(stats.mutuals, sn_followers.ptr - 1))
    39         -	var bio = lib.str.plit '<em style="opacity:0.6">tall, dark, and mysterious</em>'
           39  +	var bio = pstr '<em style="opacity:0.6">tall, dark, and mysterious</em>'
    40     40   	if actor.bio ~= nil then
    41     41   		bio = lib.smackdown.html(&co.srv.pool,cs(actor.bio),false)
    42     42   	end
    43     43   	var fullname = lib.render.nym(actor,0,nil,false) defer fullname:free()
    44     44   	var comments = co:stra(64)
    45     45   
    46     46   	if co.srv.cfg.master == actor.id then
    47         -		var foundertxt = lib.str.plit 'founder'
           47  +		var foundertxt = pstr 'founder'
    48     48   		if co.srv.cfg.ui_cue_founder:ref() then
    49     49   			if co.srv.cfg.ui_cue_founder.ct == 0 -- empty string, suppress field
    50     50   				then foundertxt = pstr.null()
    51     51   				else foundertxt = co.srv.cfg.ui_cue_founder
    52     52   			end
    53     53   		end
    54     54   
    55     55   		if foundertxt:ref() then
    56     56   			comments:lpush('<li style="--co:-70">'):ppush(foundertxt):lpush('</li>')
    57     57   		end
    58     58   	end
    59     59   	if co.aid ~= 0 and actor.rights.rank ~= 0 then
    60         -		var stafftxt = lib.str.plit 'site staff'
           60  +		var stafftxt = pstr 'site staff'
    61     61   		if co.srv.cfg.ui_cue_staff:ref() then
    62     62   			if co.srv.cfg.ui_cue_staff.ct == 0 -- empty string, suppress field
    63     63   				then stafftxt = pstr.null()
    64     64   				else stafftxt = co.srv.cfg.ui_cue_staff
    65     65   			end
    66     66   		end
    67     67   
................................................................................
    88     88   		bio = bio;
    89     89   		xid = cs(actor.xid);
    90     90   		avatar = cs(actor.avatar);
    91     91   
    92     92   		nposts = sn_posts, nfollows = sn_follows;
    93     93   		nfollowers = sn_followers, nmutuals = sn_mutuals;
    94     94   		tweetday = cs(timestr);
    95         -		timephrase = lib.trn(actor.origin == 0, lib.str.plit'joined', lib.str.plit'known since');
           95  +		timephrase = lib.trn(actor.origin == 0, pstr 'joined', pstr 'known since');
    96     96   
    97     97   		remarks = '';
    98     98   
    99     99   		auxbtn = auxp;
   100    100   	}
   101    101   	if comments.sz > 0 then profile.remarks = comments:finalize() end
   102    102   

Modified render/timeline.t from [7375f87c90] to [9997258dca].

    22     22   			from_time = stoptime;
    23     23   			to_idx = 64;
    24     24   		})
    25     25   	elseif mode == modes.fediglobal then
    26     26   	elseif mode == modes.circle then
    27     27   	end
    28     28   
    29         -	var acc: lib.str.acc acc:pool(&co.srv.pool,1024)
           29  +	var acc = co:stra(1024)
    30     30   	acc:lpush('<div id="tl" data-live="10">')
    31     31   	var newest: lib.store.timepoint = 0
    32     32   	for i = 0, posts.sz do
    33     33   		lib.render.tweet(co, posts(i).ptr, &acc)
    34     34   		var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited)
    35     35   		if t > newest then newest = t end
    36     36   		posts(i):free()
    37     37   	end
    38     38   	posts:free()
    39     39   	acc:lpush('</div>')
    40     40   
    41     41   	var doc = [lib.srv.convo.page] {
    42         -		title = lib.str.plit'timeline';
           42  +		title = 'timeline';
    43     43   		body = acc:finalize();
    44         -		class = lib.str.plit'timeline';
           44  +		class = 'timeline';
    45     45   		cache = false;
    46     46   	}
    47     47   	co:livepage(doc,newest)
    48     48   	--doc.body:free()
    49     49   end
    50     50   return render_timeline

Modified render/tweet-page.t from [304c6c3aff] to [6d8fb63eed].

    60     60   
    61     61   	if co.aid ~= 0 and co.who.rights.powers.post() then
    62     62   		lib.render.compose(co, nil, &pg)
    63     63   	end
    64     64   
    65     65   	var ppg = pg:finalize() --defer ppg:free()
    66     66   	co:livepage([lib.srv.convo.page] {
    67         -		title = lib.str.plit 'post'; cache = false;
    68         -		class = lib.str.plit 'post'; body = ppg;
           67  +		title = 'post'; cache = false;
           68  +		class = 'post'; body = ppg;
    69     69   	}, livetime)
    70     70   
    71     71   	-- TODO display conversation
    72     72   	-- perhaps display descendant nodes here, and have a link to the top of the whole tree?
    73     73   end
    74     74   
    75     75   return render_tweet_page

Modified render/user-page.t from [be37fdb666] to [d4b538d774].

    34     34   	end
    35     35   	posts:free()
    36     36   	acc:lpush('</div>')
    37     37   
    38     38   	var bdf = acc:finalize()
    39     39   	co:livepage([lib.srv.convo.page] {
    40     40   		title = tiptr; body = bdf;
    41         -		class = lib.str.plit 'profile';
           41  +		class = 'profile';
    42     42   		cache = false;
    43     43   	}, newest)
    44     44   
    45     45   	tiptr:free()
    46     46   	--bdf:free()
    47     47   end
    48     48   
    49     49   return render_userpage

Modified route.t from [efbc5e4a8e] to [6caa1366ba].

   451    451   			end
   452    452   
   453    453   			msg = 'profile changes saved'
   454    454   			--user_refresh = true -- not really necessary here, actually
   455    455   
   456    456   		elseif path(1):cmp('sec') then
   457    457   			credsec_for_uid(co, co.who.id)
          458  +		elseif path(1):cmp('avi') then
          459  +			var act = co:ppostv('act')
          460  +			if act:ref() and act:cmp('clear') then
          461  +				co.who.avatarid = 0
          462  +				co.who.source:actor_save(co.who)
          463  +				msg = 'avatar reset to default'
          464  +			else goto badop end
   458    465   		elseif path(1):cmp('users') then
   459    466   			if path.ct >= 3 then
   460    467   				var userid, ok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
   461    468   				if ok then
   462    469   					var usr = co.srv:actor_fetch_uid(userid)
   463    470   					if usr:ref() then --defer usr:free()
   464    471   						if not co.who:overpowers(usr.ptr) then

Modified srv.t from [baac561cfe] to [2ff93305a1].

   237    237   terra convo:stdpage(pg: convo.page) self:statpage(200, pg) end
   238    238   
   239    239   terra convo:bytestream(mime: pstring, data: lib.mem.ptr(uint8))
   240    240   	-- TODO this is not a satisfactory solution; it's a bandaid on a gaping
   241    241   	-- chest wound. ultimately we need to compile a whitelist of safe mime
   242    242   	-- types as part of mimelib, but that is no small task. for now, this
   243    243   	-- will keep the patient from immediately bleeding out
   244         -	if mime:cmp(lib.str.plit'text/html') or
   245         -		mime:cmp(lib.str.plit'text/xml') or
   246         -		mime:cmp(lib.str.plit'application/xhtml+xml') or
   247         -		mime:cmp(lib.str.plit'application/vnd.wap.xhtml+xml')
          244  +	if mime:cmp('text/html') or
          245  +		mime:cmp('text/xml') or
          246  +		mime:cmp('application/xhtml+xml') or
          247  +		mime:cmp('application/vnd.wap.xhtml+xml')
   248    248   	then -- danger will robinson
   249         -		mime = lib.str.plit'text/plain'
   250         -	elseif mime:cmp(lib.str.plit'application/x-shockwave-flash') then
   251         -		mime = lib.str.plit'application/octet-stream'
          249  +		mime = 'text/plain'
          250  +	elseif mime:cmp('application/x-shockwave-flash') then
          251  +		mime = 'application/octet-stream'
   252    252   	end
   253    253   	lib.net.mg_printf(self.con, "HTTP/1.1 200 OK\r\nContent-Type: %.*s\r\nContent-Length: %llu\r\nContent-Security-Policy: sandbox; default-src 'none'; form-action 'none'; navigate-to 'none';\r\nX-Content-Options: nosniff\r\n\r\n", mime.ct, mime.ptr, data.ct + 2)
   254    254   	lib.net.mg_send(self.con, data.ptr, data.ct)
   255    255   	lib.net.mg_send(self.con, '\r\n', 2)
   256    256   end
   257    257   
   258    258   terra convo:reroute_cookie(dest: rawstring, cookie: rawstring)
................................................................................
   293    293   	if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end
   294    294   
   295    295   	var ti: lib.str.acc ti:compose('error :: ', title)
   296    296   	var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>')
   297    297   	var body = [convo.page] {
   298    298   		title = ti:finalize();
   299    299   		body = bo:finalize();
   300         -		class = lib.str.plit 'error';
          300  +		class = 'error';
   301    301   		cache = false;
   302    302   	}
   303    303   
   304    304   	self:statpage(code, body)
   305    305   
   306    306   	body.title:free()
   307    307   	body.body:free()
................................................................................
   313    313   		query = msg;
   314    314   		cancel = cancel;
   315    315   	}
   316    316   	var ti: lib.str.acc ti:pcompose(&self.srv.pool,'confirm :: ', title)
   317    317   	var body = conf:poolstr(&self.srv.pool) -- defer body:free()
   318    318   	var cf = [convo.page] {
   319    319   		title = ti:finalize();
   320         -		class = lib.str.plit 'query';
          320  +		class = 'query';
   321    321   		body = body; cache = false;
   322    322   	}
   323    323   	self:stdpage(cf)
   324    324   	--cf.title:free()
   325    325   end
   326    326   
   327    327   terra convo:stra(sz: intptr) -- convenience function
................................................................................
   629    629   								var halt = lib.str.find(@lsent, lsr)
   630    630   								if halt:ref() then
   631    631   									lsent.ct = halt.ptr - lsent.ptr
   632    632   								end
   633    633   								lsr:free() end
   634    634   
   635    635   							for i=0,upmap.ct do
   636         -								var hdrbrk = lib.str.find(upmap(i), lib.str.plit'\r\n\r\n')
          636  +								var hdrbrk = lib.str.find(upmap(i), '\r\n\r\n')
   637    637   								if hdrbrk:ref() then
   638    638   									var hdrtxt = pstring {upmap(i).ptr,upmap(i).ct - hdrbrk.ct}
   639    639   									var hdrs = lib.str.splitmap(hdrtxt, '\r\n',6)
   640    640   									var ctt = pstring.null()
   641    641   									var ctd = pstring.null()
   642    642   									for j=0, hdrs.ct do
   643         -										var brk = lib.str.find(hdrs(j),lib.str.plit':')
          643  +										var brk = lib.str.find(hdrs(j),':')
   644    644   										if brk:ref() then
   645    645   											var hdr = pstring{hdrs(j).ptr,hdrs(j).ct - brk.ct}
   646    646   											var val = pstring{brk.ptr+1, brk.ct-1}:ffw()
   647         -											if hdr:cmp(lib.str.plit'Content-Type') then
          647  +											if hdr:cmp('Content-Type') then
   648    648   												ctt = val
   649         -											elseif hdr:cmp(lib.str.plit'Content-Disposition') then
          649  +											elseif hdr:cmp('Content-Disposition') then
   650    650   												ctd = val
   651    651   											end
   652    652   										end
   653    653   									end
   654    654   									if ctd:ref() then
   655    655   										var ctdvals = lib.str.splitmap(ctd, ';', 4) defer ctdvals:free()
   656         -										if ctdvals(0):cmp(lib.str.plit'form-data') and ctdvals.ct > 1 then
          656  +										if ctdvals(0):cmp('form-data') and ctdvals.ct > 1 then
   657    657   											var fld = pstring.null()
   658    658   											var file = pstring.null()
   659    659   											for j=1, ctdvals.ct do var v = ctdvals(j):ffw()
   660         -												var x = lib.str.find(v,lib.str.plit'=')
          660  +												var x = lib.str.find(v,'=')
   661    661   												if x:ref() then
   662    662   													var key = pstring{v.ptr, v.ct - x.ct}
   663    663   													var val = pstring{x.ptr + 1, x.ct - 1}
   664    664   													var decval, ofs, sp = lib.str.toknext(val,@';',true)
   665         -													if key:cmp(lib.str.plit'name') then
          665  +													if key:cmp('name') then
   666    666   														fld = decval
   667         -													elseif key:cmp(lib.str.plit'filename') then
          667  +													elseif key:cmp('filename') then
   668    668   														file = decval
   669    669   													else decval:free() end
   670    670   												end
   671    671   											end
   672    672   											if fld:ref() then
   673    673   												var nextup = co.uploads:new()
   674    674   												if ctt:ref() then
................................................................................
   937    937   	end
   938    938   	return default
   939    939   end
   940    940   
   941    941   terra cfgcache:cfbool(name: rawstring, default: bool)
   942    942   	var str = self.overlord:conf_get(name)
   943    943   	if str.ptr ~= nil then
   944         -		if str:cmp(lib.str.plit 'true') or str:cmp(lib.str.plit 'on') or
   945         -		   str:cmp(lib.str.plit 'yes')  or str:cmp(lib.str.plit '1') then
          944  +		if str:cmp('true') or str:cmp('on') or
          945  +		   str:cmp('yes')  or str:cmp('1') then
   946    946   			default = true
   947         -		elseif str:cmp(lib.str.plit 'false') or str:cmp(lib.str.plit 'off') or
   948         -		       str:cmp(lib.str.plit 'no')    or str:cmp(lib.str.plit '0') then
          947  +		elseif str:cmp('false') or str:cmp('off') or
          948  +		       str:cmp('no')    or str:cmp('0') then
   949    949   			default = false
   950    950   		else
   951    951   			lib.warn('invalid configuration setting ',name,'="',{str.ptr,str.ct},'", expected boolean; using default value instead')
   952    952   		end
   953    953   		str:free()
   954    954   	end
   955    955   	return default

Modified static/style.scss from [08446e37b3] to [e23a7affc6].

   287    287   		grid-row: 1 / 2;
   288    288   		display: grid;
   289    289   		grid-template-columns: 1.1in 1fr;
   290    290   		grid-template-rows: max-content 1fr;
   291    291   		> .avatar {
   292    292   			display: block;
   293    293   			width: 1in; height: 1in;
   294         -			object-fit: contain;
          294  +			object-fit: cover;
   295    295   			grid-column: 1 / 2;
   296    296   			grid-row: 1 / 3;
   297    297   			border: 1px solid black;
   298    298   		}
   299    299   		> .id {
   300    300   			grid-column: 2 / 3;
   301    301   			grid-row: 1 / 2;
................................................................................
   526    526   	transition: 0.2s ease-out;
   527    527   	>.avatar {
   528    528   		grid-column: 1/2; grid-row: 1/2;
   529    529   		background: linear-gradient(to bottom, tone(-53%), tone(-57%));
   530    530   		img {
   531    531   			display: block; width: 1in; height: 1in; margin:0;
   532    532   			border-right: 1px solid tone(-65%);
          533  +			object-fit: cover;
   533    534   		}
   534    535   	}
   535    536   	>a[href].username {
   536    537   		display: block;
   537    538   		grid-column: 1/3;
   538    539   		grid-row: 2/3;
   539    540   		text-align: left;
................................................................................
   665    666   			padding-top: 0.12in;
   666    667   			background: linear-gradient(to right, tone(-50%), tone(-50%,-0.7));
   667    668   			border: 1px solid tone(-55%);
   668    669   			border-left: none;
   669    670   			text-shadow: 1px 1px 0 black;
   670    671   		}
   671    672   	}
   672         -
          673  +	img.avatar.big {
          674  +		display:block;
          675  +		width: 70%;
          676  +		height: auto;
          677  +		margin: 0.2in auto;
          678  +		border: 1px solid tone(-45%);
          679  +		border-top-color: tone(-40%);
          680  +		border-bottom: 2px solid tone(-60%);
          681  +		object-fit: cover;
          682  +		border-radius: 3px;
          683  +	}
   673    684   }
   674    685   
   675    686   hr {
   676    687   	border: none;
   677    688   	border-top: 1px solid tone(-30%);
   678    689   	border-bottom: 1px solid tone(-55%);
   679    690   }

Modified store.t from [8b07464dc2] to [9b6251fcb3].

    59     59   local function setmap(set)
    60     60   	local map = {}
    61     61   	local struct pt { name:lib.mem.ptr(int8), val:set }
    62     62   	for k,v in pairs(set.members) do
    63     63   		map[#map + 1] = quote
    64     64   			var ps: set ps:clear()
    65     65   			(ps.[v] << true)
    66         -		in pt {name = lib.str.plit(v), val = ps} end
           66  +		in pt {name = [v], val = ps} end
    67     67   	end
    68     68   	return map
    69     69   end
    70     70   m.powmap = setmap(m.powerset)
    71     71   m.privmap = setmap(m.privset)
    72     72   
    73     73   terra m.powerset:affect_users()

Modified str.t from [c8b5d8bfdc] to [c25d641c64].

    36     36   do local strptr = (lib.mem.ptr(int8))
    37     37   	local strref = (lib.mem.ref(int8))
    38     38   	local byteptr = (lib.mem.ptr(uint8))
    39     39   	local function install_funcs(ty)
    40     40   		ty.metamethods.__cast = function(from,to,e)
    41     41   			local v = e:asvalue()
    42     42   			if type(v) == 'string' then
    43         -				print('hard-coding pstr',v,#v)
    44     43   				return `ty {ptr = v, ct = [#v]}
    45     44   			elseif from == &int8 then
    46     45   				return `ty {ptr = e, ct = m.sz(e)}
    47     46   			elseif to == &int8 then
    48     47   				return e.ptr
    49     48   			end
    50     49   		end