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
2
3



4
5
6
7
8
9
10
..
52
53
54
55
56
57
58






59
60
61
62
63
64
65
-- vim: ft=terra
local m = {
	shorthand = {maxlen = 14}



}

local pstring = lib.mem.ptr(int8)

-- swap in place -- faster on little endian
m.netswap_ip = macro(function(ty, src, dest)
	if ty:astype().type ~= 'integer' then error('bad type') end
................................................................................
	elseif ch == 0x3a then ch = 37
	elseif ch >= 0x61 and ch <= 0x7a then
		ch = 38 + (ch - 0x61)
	else return 0, false end

	return ch, true 
end







terra m.shorthand.gen(val: uint64, dest: rawstring): ptrdiff
	var lst = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz"
	var buf: int8[m.shorthand.maxlen]
	var ptr = [&int8](buf)
	while val ~= 0 do
		var v = val % 64


|
>
>
>







 







>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
..
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
-- vim: ft=terra
local m = {
	shorthand = {maxlen = 14};
	ll = {
		ctpop_u8 = terralib.intrinsic('llvm.ctpop.i8', uint8 -> uint8);
	};
}

local pstring = lib.mem.ptr(int8)

-- swap in place -- faster on little endian
m.netswap_ip = macro(function(ty, src, dest)
	if ty:astype().type ~= 'integer' then error('bad type') end
................................................................................
	elseif ch == 0x3a then ch = 37
	elseif ch >= 0x61 and ch <= 0x7a then
		ch = 38 + (ch - 0x61)
	else return 0, false end

	return ch, true 
end

terra m.pow(n: intptr, fac: intptr): intptr
	var o = n
	for i=0,fac do n = n * o end
	return n
end

terra m.shorthand.gen(val: uint64, dest: rawstring): ptrdiff
	var lst = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz"
	var buf: int8[m.shorthand.maxlen]
	var ptr = [&int8](buf)
	while val ~= 0 do
		var v = val % 64

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

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
		elseif tmppw[i] >= 10 then
			tmppw[i] = tmppw[i] + (0x41 - 10)
		else tmppw[i] = tmppw[i] + 0x30 end
	end
	lib.dbg('assigning temporary password')
	dlg:auth_attach_pw(uid, reset,
		pstr { ptr = [rawstring](tmppw), ct = 32 },
		lib.str.plit 'temporary password');
end

local terra ipc_report(acks: lib.mem.ptr(lib.ipc.ack), rep: rawstring)
	var decbuf: int8[21]
	for i=0,acks.ct do
		var num = lib.math.decstr(acks(i).clid, &decbuf[20])
		if acks(i).success then
................................................................................
				if cfmode.help then
					[ lib.emit(false, 1, 'usage: ', `argv[0], ' mkroot ', cfmode.type.helptxt.flags, ' <handle>', cfmode.type.helptxt.opts) ]
					return 1
				end
				if cfmode.arglist.ct == 1 then
					var am = dlg:conf_get('credential-store')
					var mg: bool
					if (not am) or am:cmp(lib.str.plit 'managed') then
						mg = true
					elseif am:cmp(lib.str.plit 'unmanaged') then
						lib.warn('credential store is unmanaged; you will need to create credentials for the new root user manually!')
						mg = false
					else lib.bail('unknown credential store mode "',{am.ptr,am.ct},'"; should be either "managed" or "unmanaged"') end
					var kbuf: uint8[lib.crypt.const.maxdersz]
					var root = lib.store.actor.mk(&kbuf[0])
					root.handle = cfmode.arglist(0)
					var epithets = array(







|







 







|

|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
		elseif tmppw[i] >= 10 then
			tmppw[i] = tmppw[i] + (0x41 - 10)
		else tmppw[i] = tmppw[i] + 0x30 end
	end
	lib.dbg('assigning temporary password')
	dlg:auth_attach_pw(uid, reset,
		pstr { ptr = [rawstring](tmppw), ct = 32 },
		'temporary password');
end

local terra ipc_report(acks: lib.mem.ptr(lib.ipc.ack), rep: rawstring)
	var decbuf: int8[21]
	for i=0,acks.ct do
		var num = lib.math.decstr(acks(i).clid, &decbuf[20])
		if acks(i).success then
................................................................................
				if cfmode.help then
					[ lib.emit(false, 1, 'usage: ', `argv[0], ' mkroot ', cfmode.type.helptxt.flags, ' <handle>', cfmode.type.helptxt.opts) ]
					return 1
				end
				if cfmode.arglist.ct == 1 then
					var am = dlg:conf_get('credential-store')
					var mg: bool
					if (not am) or am:cmp('managed') then
						mg = true
					elseif am:cmp('unmanaged') then
						lib.warn('credential store is unmanaged; you will need to create credentials for the new root user manually!')
						mg = false
					else lib.bail('unknown credential store mode "',{am.ptr,am.ct},'"; should be either "managed" or "unmanaged"') end
					var kbuf: uint8[lib.crypt.const.maxdersz]
					var root = lib.store.actor.mk(&kbuf[0])
					root.handle = cfmode.arglist(0)
					var epithets = array(

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

272
273
274
275
276
277
278










279
280
281
282
283
284
285
...
451
452
453
454
455
456
457

458
459
460
461
462
463
464
	set.name = string.format('set<%s>', table.concat(tbl, '|'))
	set.metamethods.__entrymissing = macro(function(val, obj)
		if o[val] == nil then error('value ' .. val .. ' not in set') end
		return `bit { _v=[o[val] - 1], _set = &(obj) }
	end)
	terra set:sz()
		var ct: intptr = 0










		for i = 0, [#tbl] do
			if (self._store[i/8] and (1 << i % 8)) ~= 0 then ct = ct + 1 end
		end
		return ct
	end
	set.methods.dump = macro(function(self)
		local q = quote lib.io.say('dumping set:\n') end
................................................................................
	'render:media-gallery';

	'render:docpage';

	'render:conf:profile';
	'render:conf:sec';
	'render:conf:users';

	'render:conf';
	'route';
}

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end







>
>
>
>
>
>
>
>
>
>







 







>







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

	'render:docpage';

	'render:conf:profile';
	'render:conf:sec';
	'render:conf:users';
	'render:conf:avi';
	'render:conf';
	'route';
}

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end

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

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
		form.acl = edit.acl
	end
	if acc ~= nil then form:append(acc) return end 

	var cotxt = form:poolstr(&co.srv.pool) -- defer cotxt:free()

	var doc = [lib.srv.convo.page] {
		title = lib.str.plit 'compose';
		body = cotxt;
		class = lib.str.plit 'compose';
		cache = true;
	}

	co:stdpage(doc)
end

return render_compose







|

|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
		form.acl = edit.acl
	end
	if acc ~= nil then form:append(acc) return end 

	var cotxt = form:poolstr(&co.srv.pool) -- defer cotxt:free()

	var doc = [lib.srv.convo.page] {
		title = 'compose';
		body = cotxt;
		class = 'compose';
		cache = true;
	}

	co:stdpage(doc)
end

return render_compose

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)

local mappings = {
	{url = 'profile', title = 'account profile', render = 'profile'};
	{url = 'avi', title = 'avatar', render = 'avatar'};
	{url = 'ui', title = 'user interface', render = 'ui'};
	{url = 'sec', title = 'security', render = 'sec_overlay'};
	{url = 'rel', title = 'relationships', render = 'rel'};
	{url = 'qnt', title = 'quarantine', render = 'quarantine'};
	{url = 'acl', title = 'access control shortcuts', render = 'acl'};
	{url = 'rooms', title = 'chatrooms', render = 'rooms'};
	{url = 'circles', title = 'circles', render = 'circles'};
................................................................................
		pg:append(&fnpg)
		pgt = fnpg:finalize()
	else pgt = pg:poolstr(&co.srv.pool) end
	--defer pgt:free()

	co:stdpage([lib.srv.convo.page] {
		title = 'configure'; body = pgt;
		class = lib.str.plit 'conf';
		cache = false;
	})

	--if panel.ct ~= 0 then panel:free() end
end

return render_conf






|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)

local mappings = {
	{url = 'profile', title = 'account profile', render = 'profile'};
	{url = 'avi', title = 'avatar', render = 'avi'};
	{url = 'ui', title = 'user interface', render = 'ui'};
	{url = 'sec', title = 'security', render = 'sec_overlay'};
	{url = 'rel', title = 'relationships', render = 'rel'};
	{url = 'qnt', title = 'quarantine', render = 'quarantine'};
	{url = 'acl', title = 'access control shortcuts', render = 'acl'};
	{url = 'rooms', title = 'chatrooms', render = 'rooms'};
	{url = 'circles', title = 'circles', render = 'circles'};
................................................................................
		pg:append(&fnpg)
		pgt = fnpg:finalize()
	else pgt = pg:poolstr(&co.srv.pool) end
	--defer pgt:free()

	co:stdpage([lib.srv.convo.page] {
		title = 'configure'; body = pgt;
		class = 'conf';
		cache = false;
	})

	--if panel.ct ~= 0 then panel:free() end
end

return render_conf

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































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)

local terra 
render_conf_avi(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
	var a = co:stra(128)
	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 '">'
	if co.who.avatarid ~= 0 then
		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>'
	end
	return a:finalize()
end

return render_conf_avi

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

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
					end
				end
				credmgr.credlist = cl:finalize()
			end
			credmgr:append(&a)
			--if credmgr.credlist.ct > 0 then credmgr.credlist:free() end
		else
			if new:cmp(lib.str.plit'pw') then
				var d: data.view.conf_sec_pwnew
				var time = lib.osclock.time(nil)
				var timestr: int8[26] lib.osclock.ctime_r(&time, &timestr[0])
				var cmt = co:stra(48)
				cmt:lpush('enrolled over http on '):push(&timestr[0],0)
				d.comment = cmt:finalize()

				var st = d:poolstr(&co.srv.pool)
				--d.comment:free()
				return st
			elseif new:cmp(lib.str.plit'challenge') then
			-- we're going to break the rules a bit and do database munging from
			-- the rendering code, because doing otherwise in this case would be
			-- genuinely nightmarish
			elseif new:cmp(lib.str.plit'otp') then
			elseif new:cmp(lib.str.plit'api') then
			else return pstr.null() end
		end
	else body:append(&a) end

	return a:finalize()
end








|










|



|
|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
					end
				end
				credmgr.credlist = cl:finalize()
			end
			credmgr:append(&a)
			--if credmgr.credlist.ct > 0 then credmgr.credlist:free() end
		else
			if new:cmp('pw') then
				var d: data.view.conf_sec_pwnew
				var time = lib.osclock.time(nil)
				var timestr: int8[26] lib.osclock.ctime_r(&time, &timestr[0])
				var cmt = co:stra(48)
				cmt:lpush('enrolled over http on '):push(&timestr[0],0)
				d.comment = cmt:finalize()

				var st = d:poolstr(&co.srv.pool)
				--d.comment:free()
				return st
			elseif new:cmp('challenge') then
			-- we're going to break the rules a bit and do database munging from
			-- the rendering code, because doing otherwise in this case would be
			-- genuinely nightmarish
			elseif new:cmp('otp') then
			elseif new:cmp('api') then
			else return pstr.null() end
		end
	else body:append(&a) end

	return a:finalize()
end

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

1
2
3
4
5
6
7
8
9
10
11
..
14
15
16
17
18
19
20






21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50


51
52
53
54
55
56
57
58
59
60


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113



































114
115
116
117
118
119
120
...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
...
345
346
347
348
349
350
351
352


353
354
355
356
357
358
359
360
361
362
363
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local P = lib.str.plit

local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local terra 
regalia(acc: &lib.str.acc, rank: uint16)
................................................................................
		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 rnd = lib.crypt.random
local terra 
suggest_handle(a: &lib.str.acc)
	var start = a.sz
	var puncts = array('.','_','-')
	var xXx = rnd(uint8, 0, 9) == 0
	var leet = rnd(uint8, 0, 8) == 0
	var caps = rnd(uint8, 0, 5)
	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 = array(
		'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'
	)


	var adjs = array(
		'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',
		'mighty','flayed','hidden','secret','lost','mystery','glorious',
		'nude','naked','bare','first','radiant','martian','fallen',
		'wandering','dank','demonic','satanic','invisible','based','woke',
		'deadly','lethal','heroic','evil','majestic','luminous'
	)



	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:push(adjs[rnd(intptr,0,[adjs.type.N])], 0)
			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:push(nouns[rnd(intptr,0,[nouns.type.N])], 0)
		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 == 2 then for i=start, a.sz do
		if caps == 2 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('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(uint16,0,65535))
	end

	if xXx then a:lpush('_xXx') end

end

local terra 
suggest_domain(a: &lib.str.acc)
	var tlds = array('tld','club','town','space','xxx')



































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)
................................................................................
			ctlbox:append(&pg)
			--ctlbox.name:free()
			--if ctlbox.btns.ct > 0 then ctlbox.btns:free() end

			return pg:finalize()
		end
	else
		var modes = array(P'local', P'remote', P'staff', P'titled', P'peons', P'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
................................................................................
		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="tweetlord@website.tld"></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



<







 







>
>
>
>
>
>








|






|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
>
>
|
<
|
|
|
|
|
|
|
<
>
>







|









|






|
|







 







|








|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|
>
>











1
2
3

4
5
6
7
8
9
10
..
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57

58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
-- 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)
................................................................................
		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, 9) == 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
	]]
	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
		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
	]]

	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('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
	]]
	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 space spot store sucks supplies
		systems university vacations ventures wang website work
		wow wtf world xyz soy live gym park 
	]]
	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)
................................................................................
			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
................................................................................
		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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
-- vim: ft=terra
local page = lib.srv.convo.page
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local P = lib.str.plit
local R = lib.str.lit

local topics = lib.util.keys(data.doc)
local topicidxt = {}
table.sort(topics) -- because deterministic builds are good
local branches = {}
for i,k in pairs(topics) do
	topicidxt[k] = i
................................................................................
	if t.meta.priv then
		if type(t.meta.priv) ~= 'table' then t.meta.priv = {t.meta.priv} end
		for _,v in pairs(t.meta.priv) do
			setbits = quote [setbits]; (restrict.[v] << true) end
		end
	end
	allpages[i] = quote var [restrict]; [setbits] in pgpair {
		name = R(v);
		parent = par;
		priv = restrict;
		title = R(t.meta.title);
		content = page {
			title = ['documentation :: ' .. t.meta.title];
			body = [ t.text ];
			class = P'doc article';
			cache = true;
		};
	} end
end

local terra 
showpage(co: &lib.srv.convo, id: pref)
................................................................................
			end
		end
		list:lpush('</ul>')

		co:stdpage(page {
			title = 'documentation';
			body = list:finalize();
			class = P'doc listing';
			cache = false;
		})
	else showpage(co, pg) end
end

return render_docpage




<
<







 







|


|



|







 







|






1
2
3
4


5
6
7
8
9
10
11
..
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
-- vim: ft=terra
local page = lib.srv.convo.page
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)



local topics = lib.util.keys(data.doc)
local topicidxt = {}
table.sort(topics) -- because deterministic builds are good
local branches = {}
for i,k in pairs(topics) do
	topicidxt[k] = i
................................................................................
	if t.meta.priv then
		if type(t.meta.priv) ~= 'table' then t.meta.priv = {t.meta.priv} end
		for _,v in pairs(t.meta.priv) do
			setbits = quote [setbits]; (restrict.[v] << true) end
		end
	end
	allpages[i] = quote var [restrict]; [setbits] in pgpair {
		name = [v];
		parent = par;
		priv = restrict;
		title = [t.meta.title];
		content = page {
			title = ['documentation :: ' .. t.meta.title];
			body = [ t.text ];
			class = 'doc article';
			cache = true;
		};
	} end
end

local terra 
showpage(co: &lib.srv.convo, id: pref)
................................................................................
			end
		end
		list:lpush('</ul>')

		co:stdpage(page {
			title = 'documentation';
			body = list:finalize();
			class = 'doc listing';
			cache = false;
		})
	else showpage(co, pg) end
end

return render_docpage

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local P = lib.str.plit
local terra 
login_form(co: &lib.srv.convo, user: &lib.store.actor, creds: &lib.store.credset, msg: pstr)
	var doc = [lib.srv.convo.page] {
		title = lib.str.plit 'instance logon';
		class = lib.str.plit 'login';
		cache = false;
	}

	if user == nil then
		var form = data.view.login_username {
			loginmsg = msg;
		}
		if form.loginmsg.ptr == nil then
			form.loginmsg = lib.str.plit 'identify yourself for access to this instance.'
		end
		doc.body = form:tostr()
	elseif creds:sz() == 0 then
		co:complain(403,'access denied','your host is not eligible to authenticate as this user')
		return
	elseif creds:sz() == 1 then
		if creds.trust() then
................................................................................
		end

		var ch = data.view.login_challenge {
			handle = user.handle;
			name = lib.coalesce(user.nym, user.handle);
		}
		if creds.pw() then
			ch.challenge = P'enter the password associated with your account'
			ch.label = P'password'
			ch.method = P'pw'
			ch.auto = P'current-password';
		elseif creds.otp() then
			ch.challenge = P'enter a valid one-time password for your account'
			ch.label = P'OTP code'
			ch.method = P'otp'
			ch.auto = P'one-time-code';
		elseif creds.challenge() then
			ch.challenge = P'sign the challenge token: <code>...</code>'
			ch.label = P'digest'
			ch.method = P'challenge'
			ch.auto = P'one-time-code';
		else
			co:complain(500,'login failure','unknown login method')
			return
		end

		doc.body = ch:tostr()
	else


<



|
|








|







 







|
|
|
|

|
|
|
|

|
|
|
|







1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)

local terra 
login_form(co: &lib.srv.convo, user: &lib.store.actor, creds: &lib.store.credset, msg: pstr)
	var doc = [lib.srv.convo.page] {
		title = 'instance logon';
		class = 'login';
		cache = false;
	}

	if user == nil then
		var form = data.view.login_username {
			loginmsg = msg;
		}
		if form.loginmsg.ptr == nil then
			form.loginmsg = 'identify yourself for access to this instance.'
		end
		doc.body = form:tostr()
	elseif creds:sz() == 0 then
		co:complain(403,'access denied','your host is not eligible to authenticate as this user')
		return
	elseif creds:sz() == 1 then
		if creds.trust() then
................................................................................
		end

		var ch = data.view.login_challenge {
			handle = user.handle;
			name = lib.coalesce(user.nym, user.handle);
		}
		if creds.pw() then
			ch.challenge = 'enter the password associated with your account'
			ch.label = 'password'
			ch.method = 'pw'
			ch.auto = 'current-password';
		elseif creds.otp() then
			ch.challenge = 'enter a valid one-time password for your account'
			ch.label = 'OTP code'
			ch.method = 'otp'
			ch.auto = 'one-time-code';
		elseif creds.challenge() then
			ch.challenge = 'sign the challenge token: <code>...</code>'
			ch.label = 'digest'
			ch.method = 'challenge'
			ch.auto = 'one-time-code';
		else
			co:complain(500,'login failure','unknown login method')
			return
		end

		doc.body = ch:tostr()
	else

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

1
2
3
4
5
6
7
8
9
10
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
-- vim: ft=terra
local pstr = lib.str.t 
local P = lib.str.plit
local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local show_all,show_new,show_unfiled,show_files,show_vid,show_img=1,2,3,4,5,6

local terra 
................................................................................
			var pa = co:stra(32)
			pa:lpush('/')
			if ou(0).origin ~= 0 then pa:lpush('@') end
			pa:push(ou(0).xid,0)
			pfx = pa:finalize()
		end

		if path.ct >= 3 and path(1):cmp(lib.str.lit'a') then
			var id, idok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
			if not idok then goto e404 end
			var art = co.srv:artifact_fetch(uid, id)
			if not art then goto e404 end
			if path.ct == 3 then
			-- sniff out the artifact type and display the appropriate viewer
				var artid = cs(art(0).url)
................................................................................
					pfx = pfx, desc = desc;
					id = artid; btns = btntxt;
				}
				if lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then
					var view = data.view.media_image(viewerprops)
					var pg = view:poolstr(&co.srv.pool)
					co:stdpage([lib.srv.convo.page] {
						title = lib.str.plit'media :: image';
						class = lib.str.plit'media viewer img';
						cache = false, body = pg;
					})
					--pg:free()
				elseif lib.str.cmp(art(0).mime, 'text/markdown') == 0 then
					var view = data.view.media_text(viewerprops)
					var text, mime = co.srv:artifact_load(id) mime:free()
					view.text = lib.smackdown.html(&co.srv.pool, pstr{[rawstring](text.ptr),text.ct}, false)
					text:free()
					var pg = view:poolstr(&co.srv.pool)
					--view.text:free()
					co:stdpage([lib.srv.convo.page] {
						title = lib.str.plit'media :: text';
						class = lib.str.plit'media viewer text';
						cache = false, body = pg;
					})
					--pg:free()
				elseif
					lib.str.ncmp(art(0).mime, 'text/', 5) == 0          or
					lib.str.cmp(art(0).mime, 'application/x-perl') == 0 or
					lib.str.cmp(art(0).mime, 'application/sql') == 0
................................................................................
					var san = lib.html.sanitize(&co.srv.pool,pstr{[rawstring](text.ptr),text.ct}, false)
					text:free()
					view.text = co:qstr('<pre>',san,'</pre>')
					--san:free()
					var pg = view:poolstr(&co.srv.pool)
					--view.text:free()
					co:stdpage([lib.srv.convo.page] {
						title = lib.str.plit'media :: text';
						class = lib.str.plit'media viewer text';
						cache = false, body = pg;
					})
					--pg:free()
				else co:complain(500,'bad file type','this file type is not supported') end
			elseif path.ct == 4 then
				var act = path(3)
				var curl = co:qstr('/media/a/', path(2))
				-- defer curl:free()
				if act:cmp(lib.str.lit'avi') and lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then
					co:confirm('set avatar', 'are you sure you want this image to be your new avatar?',curl)
				elseif act:cmp(lib.str.lit'del') then
					co:confirm('delete', 'are you sure you want to permanently delete this artifact?',curl)
				else goto e404 end
			end
		else
			var mode: uint8 = show_new
			var folder: pstr
			if path.ct == 2 then
				if path(1):cmp(lib.str.lit'unfiled') then
					mode=show_unfiled
				elseif path(1):cmp(lib.str.lit'all') then
					mode=show_all
				else goto e404 end
			elseif path.ct == 3 and path(1):cmp(lib.str.lit'kind') then
			end

			var folders = co.srv:artifact_folder_enum(uid)

			if mode == show_new then
				folder = lib.str.plit''
			elseif mode == show_all or mode == show_unfiled then
				folder = pstr.null()
			end

			var view = data.view.media_gallery {
				menu = pstr{'',0};
				folders = pstr{'',0};
................................................................................
				end
				fa:lpush('<hr>')
				view.folders = fa:finalize()
				folders:free()
			end

			if owner then
				view.menu = P'<a class="pos" href="/media/upload">upload</a><hr>'
			end

			var md = co.srv:artifact_enum_uid(uid, folder)
			var gallery: lib.str.acc gallery:pool(&co.srv.pool,256)
			var files: lib.str.acc files:pool(&co.srv.pool,256) 
			for i=0,md.ct do
				var desc = lib.smackdown.html(&co.srv.pool,pstr{md(i)(0).desc,0}, true) --defer desc:free()
................................................................................
			view.directory = files:finalize()

			if acc ~= nil then
				view:append(acc)
			else
				var pg = view:poolstr(&co.srv.pool) -- defer pg:free()
				co:stdpage([lib.srv.convo.page] {
					title = P'media';
					class = P'media manager';
					cache = false;
					body = pg;
				})
			end

			--view.images:free()
			--view.directory:free()


<







 







|







 







|
|











|
|







 







|
|








|

|







|

|


|





|







 







|







 







|
|







1
2

3
4
5
6
7
8
9
..
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
..
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
-- vim: ft=terra
local pstr = lib.str.t 

local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local show_all,show_new,show_unfiled,show_files,show_vid,show_img=1,2,3,4,5,6

local terra 
................................................................................
			var pa = co:stra(32)
			pa:lpush('/')
			if ou(0).origin ~= 0 then pa:lpush('@') end
			pa:push(ou(0).xid,0)
			pfx = pa:finalize()
		end

		if path.ct >= 3 and path(1):cmp('a') then
			var id, idok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
			if not idok then goto e404 end
			var art = co.srv:artifact_fetch(uid, id)
			if not art then goto e404 end
			if path.ct == 3 then
			-- sniff out the artifact type and display the appropriate viewer
				var artid = cs(art(0).url)
................................................................................
					pfx = pfx, desc = desc;
					id = artid; btns = btntxt;
				}
				if lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then
					var view = data.view.media_image(viewerprops)
					var pg = view:poolstr(&co.srv.pool)
					co:stdpage([lib.srv.convo.page] {
						title = 'media :: image';
						class = 'media viewer img';
						cache = false, body = pg;
					})
					--pg:free()
				elseif lib.str.cmp(art(0).mime, 'text/markdown') == 0 then
					var view = data.view.media_text(viewerprops)
					var text, mime = co.srv:artifact_load(id) mime:free()
					view.text = lib.smackdown.html(&co.srv.pool, pstr{[rawstring](text.ptr),text.ct}, false)
					text:free()
					var pg = view:poolstr(&co.srv.pool)
					--view.text:free()
					co:stdpage([lib.srv.convo.page] {
						title = 'media :: text';
						class = 'media viewer text';
						cache = false, body = pg;
					})
					--pg:free()
				elseif
					lib.str.ncmp(art(0).mime, 'text/', 5) == 0          or
					lib.str.cmp(art(0).mime, 'application/x-perl') == 0 or
					lib.str.cmp(art(0).mime, 'application/sql') == 0
................................................................................
					var san = lib.html.sanitize(&co.srv.pool,pstr{[rawstring](text.ptr),text.ct}, false)
					text:free()
					view.text = co:qstr('<pre>',san,'</pre>')
					--san:free()
					var pg = view:poolstr(&co.srv.pool)
					--view.text:free()
					co:stdpage([lib.srv.convo.page] {
						title = 'media :: text';
						class = 'media viewer text';
						cache = false, body = pg;
					})
					--pg:free()
				else co:complain(500,'bad file type','this file type is not supported') end
			elseif path.ct == 4 then
				var act = path(3)
				var curl = co:qstr('/media/a/', path(2))
				-- defer curl:free()
				if act:cmp('avi') and lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then
					co:confirm('set avatar', 'are you sure you want this image to be your new avatar?',curl)
				elseif act:cmp('del') then
					co:confirm('delete', 'are you sure you want to permanently delete this artifact?',curl)
				else goto e404 end
			end
		else
			var mode: uint8 = show_new
			var folder: pstr
			if path.ct == 2 then
				if path(1):cmp('unfiled') then
					mode=show_unfiled
				elseif path(1):cmp('all') then
					mode=show_all
				else goto e404 end
			elseif path.ct == 3 and path(1):cmp('kind') then
			end

			var folders = co.srv:artifact_folder_enum(uid)

			if mode == show_new then
				folder = ''
			elseif mode == show_all or mode == show_unfiled then
				folder = pstr.null()
			end

			var view = data.view.media_gallery {
				menu = pstr{'',0};
				folders = pstr{'',0};
................................................................................
				end
				fa:lpush('<hr>')
				view.folders = fa:finalize()
				folders:free()
			end

			if owner then
				view.menu = '<a class="pos" href="/media/upload">upload</a><hr>'
			end

			var md = co.srv:artifact_enum_uid(uid, folder)
			var gallery: lib.str.acc gallery:pool(&co.srv.pool,256)
			var files: lib.str.acc files:pool(&co.srv.pool,256) 
			for i=0,md.ct do
				var desc = lib.smackdown.html(&co.srv.pool,pstr{md(i)(0).desc,0}, true) --defer desc:free()
................................................................................
			view.directory = files:finalize()

			if acc ~= nil then
				view:append(acc)
			else
				var pg = view:poolstr(&co.srv.pool) -- defer pg:free()
				co:stdpage([lib.srv.convo.page] {
					title = 'media';
					class = 'media manager';
					cache = false;
					body = pg;
				})
			end

			--view.images:free()
			--view.directory:free()

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

1
2
3
4
5
6
7
8
9
10
..
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
..
71
72
73
74
75
76
77
78
79
80
81
82
83
84
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local P = lib.str.plit
local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local terra 
render_notices(
	co: &lib.srv.convo
................................................................................
			avatar = cs(who(0).avatar);
			nym = lib.render.nym(who.ptr,0,nil,true);
			pflink = pstr{ptr = pflink.buf; ct = pflink.sz};
		}
		var notweet, nopost = true, false
		switch notes(i).kind do
			case lib.store.noticetype.rt then
				n.kind = P'rt'
				n.act = P'retweeted your post'
			end
			case lib.store.noticetype.like then
				n.kind = P'like'
				n.act = P'likes your post'
			end
			case lib.store.noticetype.reply then
				n.kind = P'reply'
				n.act = P'replied to your post'
				notweet = false
			end
			case lib.store.noticetype.follow then
				n.kind = P'follow'
				n.act = P'followed you!'
				nopost = true
			end
		else goto skip end
		if not nopost then
			var what = co.srv:post_fetch(notes(i).what) defer what:free()
			var b = lib.smackdown.html(&co.srv.pool, pstr {ptr=what(0).body,ct=0},true) --defer b:free()
			body:lpush(' <a class="quote" href="/post/'):shpush(notes(i).what):lpush('">'):ppush(b):lpush('</a>')
................................................................................
		::skip:: n.nym:free()
		         pflink:reset()
				 body:reset()
	end
	--pflink:free()
	pg:lpush('<form method="post"><button name="act" value="clear">clear all notices</button></form>')
	co:livepage([lib.srv.convo.page] {
		title = P'notices', class = P'notices';
		body = pstr {ptr = pg.buf, ct = pg.sz};
		cache = false;
	}, latest)
end

return render_notices


<







 







|
|


|
|


|
|



|
|







 







|






1
2

3
4
5
6
7
8
9
..
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)

local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local terra 
render_notices(
	co: &lib.srv.convo
................................................................................
			avatar = cs(who(0).avatar);
			nym = lib.render.nym(who.ptr,0,nil,true);
			pflink = pstr{ptr = pflink.buf; ct = pflink.sz};
		}
		var notweet, nopost = true, false
		switch notes(i).kind do
			case lib.store.noticetype.rt then
				n.kind = 'rt'
				n.act = 'retweeted your post'
			end
			case lib.store.noticetype.like then
				n.kind = 'like'
				n.act = 'likes your post'
			end
			case lib.store.noticetype.reply then
				n.kind = 'reply'
				n.act = 'replied to your post'
				notweet = false
			end
			case lib.store.noticetype.follow then
				n.kind = 'follow'
				n.act = 'followed you!'
				nopost = true
			end
		else goto skip end
		if not nopost then
			var what = co.srv:post_fetch(notes(i).what) defer what:free()
			var b = lib.smackdown.html(&co.srv.pool, pstr {ptr=what(0).body,ct=0},true) --defer b:free()
			body:lpush(' <a class="quote" href="/post/'):shpush(notes(i).what):lpush('">'):ppush(b):lpush('</a>')
................................................................................
		::skip:: n.nym:free()
		         pflink:reset()
				 body:reset()
	end
	--pflink:free()
	pg:lpush('<form method="post"><button name="act" value="clear">clear all notices</button></form>')
	co:livepage([lib.srv.convo.page] {
		title = 'notices', class = 'notices';
		body = pstr {ptr = pg.buf, ct = pg.sz};
		cache = false;
	}, latest)
end

return render_notices

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

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

	var strfbuf: int8[28*4]
	var stats = co.srv:actor_stats(actor.id)
		var sn_posts     = cs(lib.math.decstr_friendly(stats.posts, &strfbuf[ [strfbuf.type.N - 1] ]))
		var sn_follows   = cs(lib.math.decstr_friendly(stats.follows, sn_posts.ptr - 1))
		var sn_followers = cs(lib.math.decstr_friendly(stats.followers, sn_follows.ptr - 1))
		var sn_mutuals   = cs(lib.math.decstr_friendly(stats.mutuals, sn_followers.ptr - 1))
	var bio = lib.str.plit '<em style="opacity:0.6">tall, dark, and mysterious</em>'
	if actor.bio ~= nil then
		bio = lib.smackdown.html(&co.srv.pool,cs(actor.bio),false)
	end
	var fullname = lib.render.nym(actor,0,nil,false) defer fullname:free()
	var comments = co:stra(64)

	if co.srv.cfg.master == actor.id then
		var foundertxt = lib.str.plit 'founder'
		if co.srv.cfg.ui_cue_founder:ref() then
			if co.srv.cfg.ui_cue_founder.ct == 0 -- empty string, suppress field
				then foundertxt = pstr.null()
				else foundertxt = co.srv.cfg.ui_cue_founder
			end
		end

		if foundertxt:ref() then
			comments:lpush('<li style="--co:-70">'):ppush(foundertxt):lpush('</li>')
		end
	end
	if co.aid ~= 0 and actor.rights.rank ~= 0 then
		var stafftxt = lib.str.plit 'site staff'
		if co.srv.cfg.ui_cue_staff:ref() then
			if co.srv.cfg.ui_cue_staff.ct == 0 -- empty string, suppress field
				then stafftxt = pstr.null()
				else stafftxt = co.srv.cfg.ui_cue_staff
			end
		end

................................................................................
		bio = bio;
		xid = cs(actor.xid);
		avatar = cs(actor.avatar);

		nposts = sn_posts, nfollows = sn_follows;
		nfollowers = sn_followers, nmutuals = sn_mutuals;
		tweetday = cs(timestr);
		timephrase = lib.trn(actor.origin == 0, lib.str.plit'joined', lib.str.plit'known since');

		remarks = '';

		auxbtn = auxp;
	}
	if comments.sz > 0 then profile.remarks = comments:finalize() end








|







|












|







 







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

	var strfbuf: int8[28*4]
	var stats = co.srv:actor_stats(actor.id)
		var sn_posts     = cs(lib.math.decstr_friendly(stats.posts, &strfbuf[ [strfbuf.type.N - 1] ]))
		var sn_follows   = cs(lib.math.decstr_friendly(stats.follows, sn_posts.ptr - 1))
		var sn_followers = cs(lib.math.decstr_friendly(stats.followers, sn_follows.ptr - 1))
		var sn_mutuals   = cs(lib.math.decstr_friendly(stats.mutuals, sn_followers.ptr - 1))
	var bio = pstr '<em style="opacity:0.6">tall, dark, and mysterious</em>'
	if actor.bio ~= nil then
		bio = lib.smackdown.html(&co.srv.pool,cs(actor.bio),false)
	end
	var fullname = lib.render.nym(actor,0,nil,false) defer fullname:free()
	var comments = co:stra(64)

	if co.srv.cfg.master == actor.id then
		var foundertxt = pstr 'founder'
		if co.srv.cfg.ui_cue_founder:ref() then
			if co.srv.cfg.ui_cue_founder.ct == 0 -- empty string, suppress field
				then foundertxt = pstr.null()
				else foundertxt = co.srv.cfg.ui_cue_founder
			end
		end

		if foundertxt:ref() then
			comments:lpush('<li style="--co:-70">'):ppush(foundertxt):lpush('</li>')
		end
	end
	if co.aid ~= 0 and actor.rights.rank ~= 0 then
		var stafftxt = pstr 'site staff'
		if co.srv.cfg.ui_cue_staff:ref() then
			if co.srv.cfg.ui_cue_staff.ct == 0 -- empty string, suppress field
				then stafftxt = pstr.null()
				else stafftxt = co.srv.cfg.ui_cue_staff
			end
		end

................................................................................
		bio = bio;
		xid = cs(actor.xid);
		avatar = cs(actor.avatar);

		nposts = sn_posts, nfollows = sn_follows;
		nfollowers = sn_followers, nmutuals = sn_mutuals;
		tweetday = cs(timestr);
		timephrase = lib.trn(actor.origin == 0, pstr 'joined', pstr 'known since');

		remarks = '';

		auxbtn = auxp;
	}
	if comments.sz > 0 then profile.remarks = comments:finalize() end

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

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
			from_time = stoptime;
			to_idx = 64;
		})
	elseif mode == modes.fediglobal then
	elseif mode == modes.circle then
	end

	var acc: lib.str.acc acc:pool(&co.srv.pool,1024)
	acc:lpush('<div id="tl" data-live="10">')
	var newest: lib.store.timepoint = 0
	for i = 0, posts.sz do
		lib.render.tweet(co, posts(i).ptr, &acc)
		var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited)
		if t > newest then newest = t end
		posts(i):free()
	end
	posts:free()
	acc:lpush('</div>')

	var doc = [lib.srv.convo.page] {
		title = lib.str.plit'timeline';
		body = acc:finalize();
		class = lib.str.plit'timeline';
		cache = false;
	}
	co:livepage(doc,newest)
	--doc.body:free()
end
return render_timeline







|












|

|






22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
			from_time = stoptime;
			to_idx = 64;
		})
	elseif mode == modes.fediglobal then
	elseif mode == modes.circle then
	end

	var acc = co:stra(1024)
	acc:lpush('<div id="tl" data-live="10">')
	var newest: lib.store.timepoint = 0
	for i = 0, posts.sz do
		lib.render.tweet(co, posts(i).ptr, &acc)
		var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited)
		if t > newest then newest = t end
		posts(i):free()
	end
	posts:free()
	acc:lpush('</div>')

	var doc = [lib.srv.convo.page] {
		title = 'timeline';
		body = acc:finalize();
		class = 'timeline';
		cache = false;
	}
	co:livepage(doc,newest)
	--doc.body:free()
end
return render_timeline

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

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

	if co.aid ~= 0 and co.who.rights.powers.post() then
		lib.render.compose(co, nil, &pg)
	end

	var ppg = pg:finalize() --defer ppg:free()
	co:livepage([lib.srv.convo.page] {
		title = lib.str.plit 'post'; cache = false;
		class = lib.str.plit 'post'; body = ppg;
	}, livetime)

	-- TODO display conversation
	-- perhaps display descendant nodes here, and have a link to the top of the whole tree?
end

return render_tweet_page







|
|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

	if co.aid ~= 0 and co.who.rights.powers.post() then
		lib.render.compose(co, nil, &pg)
	end

	var ppg = pg:finalize() --defer ppg:free()
	co:livepage([lib.srv.convo.page] {
		title = 'post'; cache = false;
		class = 'post'; body = ppg;
	}, livetime)

	-- TODO display conversation
	-- perhaps display descendant nodes here, and have a link to the top of the whole tree?
end

return render_tweet_page

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

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
	end
	posts:free()
	acc:lpush('</div>')

	var bdf = acc:finalize()
	co:livepage([lib.srv.convo.page] {
		title = tiptr; body = bdf;
		class = lib.str.plit 'profile';
		cache = false;
	}, newest)

	tiptr:free()
	--bdf:free()
end

return render_userpage







|








34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
	end
	posts:free()
	acc:lpush('</div>')

	var bdf = acc:finalize()
	co:livepage([lib.srv.convo.page] {
		title = tiptr; body = bdf;
		class = 'profile';
		cache = false;
	}, newest)

	tiptr:free()
	--bdf:free()
end

return render_userpage

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

451
452
453
454
455
456
457







458
459
460
461
462
463
464
			end

			msg = 'profile changes saved'
			--user_refresh = true -- not really necessary here, actually

		elseif path(1):cmp('sec') then
			credsec_for_uid(co, co.who.id)







		elseif path(1):cmp('users') then
			if path.ct >= 3 then
				var userid, ok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
				if ok then
					var usr = co.srv:actor_fetch_uid(userid)
					if usr:ref() then --defer usr:free()
						if not co.who:overpowers(usr.ptr) then







>
>
>
>
>
>
>







451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
			end

			msg = 'profile changes saved'
			--user_refresh = true -- not really necessary here, actually

		elseif path(1):cmp('sec') then
			credsec_for_uid(co, co.who.id)
		elseif path(1):cmp('avi') then
			var act = co:ppostv('act')
			if act:ref() and act:cmp('clear') then
				co.who.avatarid = 0
				co.who.source:actor_save(co.who)
				msg = 'avatar reset to default'
			else goto badop end
		elseif path(1):cmp('users') then
			if path.ct >= 3 then
				var userid, ok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
				if ok then
					var usr = co.srv:actor_fetch_uid(userid)
					if usr:ref() then --defer usr:free()
						if not co.who:overpowers(usr.ptr) then

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

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
...
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
terra convo:stdpage(pg: convo.page) self:statpage(200, pg) end

terra convo:bytestream(mime: pstring, data: lib.mem.ptr(uint8))
	-- TODO this is not a satisfactory solution; it's a bandaid on a gaping
	-- chest wound. ultimately we need to compile a whitelist of safe mime
	-- types as part of mimelib, but that is no small task. for now, this
	-- will keep the patient from immediately bleeding out
	if mime:cmp(lib.str.plit'text/html') or
		mime:cmp(lib.str.plit'text/xml') or
		mime:cmp(lib.str.plit'application/xhtml+xml') or
		mime:cmp(lib.str.plit'application/vnd.wap.xhtml+xml')
	then -- danger will robinson
		mime = lib.str.plit'text/plain'
	elseif mime:cmp(lib.str.plit'application/x-shockwave-flash') then
		mime = lib.str.plit'application/octet-stream'
	end
	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)
	lib.net.mg_send(self.con, data.ptr, data.ct)
	lib.net.mg_send(self.con, '\r\n', 2)
end

terra convo:reroute_cookie(dest: rawstring, cookie: rawstring)
................................................................................
	if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end

	var ti: lib.str.acc ti:compose('error :: ', title)
	var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>')
	var body = [convo.page] {
		title = ti:finalize();
		body = bo:finalize();
		class = lib.str.plit 'error';
		cache = false;
	}

	self:statpage(code, body)

	body.title:free()
	body.body:free()
................................................................................
		query = msg;
		cancel = cancel;
	}
	var ti: lib.str.acc ti:pcompose(&self.srv.pool,'confirm :: ', title)
	var body = conf:poolstr(&self.srv.pool) -- defer body:free()
	var cf = [convo.page] {
		title = ti:finalize();
		class = lib.str.plit 'query';
		body = body; cache = false;
	}
	self:stdpage(cf)
	--cf.title:free()
end

terra convo:stra(sz: intptr) -- convenience function
................................................................................
								var halt = lib.str.find(@lsent, lsr)
								if halt:ref() then
									lsent.ct = halt.ptr - lsent.ptr
								end
								lsr:free() end

							for i=0,upmap.ct do
								var hdrbrk = lib.str.find(upmap(i), lib.str.plit'\r\n\r\n')
								if hdrbrk:ref() then
									var hdrtxt = pstring {upmap(i).ptr,upmap(i).ct - hdrbrk.ct}
									var hdrs = lib.str.splitmap(hdrtxt, '\r\n',6)
									var ctt = pstring.null()
									var ctd = pstring.null()
									for j=0, hdrs.ct do
										var brk = lib.str.find(hdrs(j),lib.str.plit':')
										if brk:ref() then
											var hdr = pstring{hdrs(j).ptr,hdrs(j).ct - brk.ct}
											var val = pstring{brk.ptr+1, brk.ct-1}:ffw()
											if hdr:cmp(lib.str.plit'Content-Type') then
												ctt = val
											elseif hdr:cmp(lib.str.plit'Content-Disposition') then
												ctd = val
											end
										end
									end
									if ctd:ref() then
										var ctdvals = lib.str.splitmap(ctd, ';', 4) defer ctdvals:free()
										if ctdvals(0):cmp(lib.str.plit'form-data') and ctdvals.ct > 1 then
											var fld = pstring.null()
											var file = pstring.null()
											for j=1, ctdvals.ct do var v = ctdvals(j):ffw()
												var x = lib.str.find(v,lib.str.plit'=')
												if x:ref() then
													var key = pstring{v.ptr, v.ct - x.ct}
													var val = pstring{x.ptr + 1, x.ct - 1}
													var decval, ofs, sp = lib.str.toknext(val,@';',true)
													if key:cmp(lib.str.plit'name') then
														fld = decval
													elseif key:cmp(lib.str.plit'filename') then
														file = decval
													else decval:free() end
												end
											end
											if fld:ref() then
												var nextup = co.uploads:new()
												if ctt:ref() then
................................................................................
	end
	return default
end

terra cfgcache:cfbool(name: rawstring, default: bool)
	var str = self.overlord:conf_get(name)
	if str.ptr ~= nil then
		if str:cmp(lib.str.plit 'true') or str:cmp(lib.str.plit 'on') or
		   str:cmp(lib.str.plit 'yes')  or str:cmp(lib.str.plit '1') then
			default = true
		elseif str:cmp(lib.str.plit 'false') or str:cmp(lib.str.plit 'off') or
		       str:cmp(lib.str.plit 'no')    or str:cmp(lib.str.plit '0') then
			default = false
		else
			lib.warn('invalid configuration setting ',name,'="',{str.ptr,str.ct},'", expected boolean; using default value instead')
		end
		str:free()
	end
	return default







|
|
|
|

|
|
|







 







|







 







|







 







|






|



|

|






|



|




|

|







 







|
|

|
|







237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
...
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
terra convo:stdpage(pg: convo.page) self:statpage(200, pg) end

terra convo:bytestream(mime: pstring, data: lib.mem.ptr(uint8))
	-- TODO this is not a satisfactory solution; it's a bandaid on a gaping
	-- chest wound. ultimately we need to compile a whitelist of safe mime
	-- types as part of mimelib, but that is no small task. for now, this
	-- will keep the patient from immediately bleeding out
	if mime:cmp('text/html') or
		mime:cmp('text/xml') or
		mime:cmp('application/xhtml+xml') or
		mime:cmp('application/vnd.wap.xhtml+xml')
	then -- danger will robinson
		mime = 'text/plain'
	elseif mime:cmp('application/x-shockwave-flash') then
		mime = 'application/octet-stream'
	end
	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)
	lib.net.mg_send(self.con, data.ptr, data.ct)
	lib.net.mg_send(self.con, '\r\n', 2)
end

terra convo:reroute_cookie(dest: rawstring, cookie: rawstring)
................................................................................
	if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end

	var ti: lib.str.acc ti:compose('error :: ', title)
	var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>')
	var body = [convo.page] {
		title = ti:finalize();
		body = bo:finalize();
		class = 'error';
		cache = false;
	}

	self:statpage(code, body)

	body.title:free()
	body.body:free()
................................................................................
		query = msg;
		cancel = cancel;
	}
	var ti: lib.str.acc ti:pcompose(&self.srv.pool,'confirm :: ', title)
	var body = conf:poolstr(&self.srv.pool) -- defer body:free()
	var cf = [convo.page] {
		title = ti:finalize();
		class = 'query';
		body = body; cache = false;
	}
	self:stdpage(cf)
	--cf.title:free()
end

terra convo:stra(sz: intptr) -- convenience function
................................................................................
								var halt = lib.str.find(@lsent, lsr)
								if halt:ref() then
									lsent.ct = halt.ptr - lsent.ptr
								end
								lsr:free() end

							for i=0,upmap.ct do
								var hdrbrk = lib.str.find(upmap(i), '\r\n\r\n')
								if hdrbrk:ref() then
									var hdrtxt = pstring {upmap(i).ptr,upmap(i).ct - hdrbrk.ct}
									var hdrs = lib.str.splitmap(hdrtxt, '\r\n',6)
									var ctt = pstring.null()
									var ctd = pstring.null()
									for j=0, hdrs.ct do
										var brk = lib.str.find(hdrs(j),':')
										if brk:ref() then
											var hdr = pstring{hdrs(j).ptr,hdrs(j).ct - brk.ct}
											var val = pstring{brk.ptr+1, brk.ct-1}:ffw()
											if hdr:cmp('Content-Type') then
												ctt = val
											elseif hdr:cmp('Content-Disposition') then
												ctd = val
											end
										end
									end
									if ctd:ref() then
										var ctdvals = lib.str.splitmap(ctd, ';', 4) defer ctdvals:free()
										if ctdvals(0):cmp('form-data') and ctdvals.ct > 1 then
											var fld = pstring.null()
											var file = pstring.null()
											for j=1, ctdvals.ct do var v = ctdvals(j):ffw()
												var x = lib.str.find(v,'=')
												if x:ref() then
													var key = pstring{v.ptr, v.ct - x.ct}
													var val = pstring{x.ptr + 1, x.ct - 1}
													var decval, ofs, sp = lib.str.toknext(val,@';',true)
													if key:cmp('name') then
														fld = decval
													elseif key:cmp('filename') then
														file = decval
													else decval:free() end
												end
											end
											if fld:ref() then
												var nextup = co.uploads:new()
												if ctt:ref() then
................................................................................
	end
	return default
end

terra cfgcache:cfbool(name: rawstring, default: bool)
	var str = self.overlord:conf_get(name)
	if str.ptr ~= nil then
		if str:cmp('true') or str:cmp('on') or
		   str:cmp('yes')  or str:cmp('1') then
			default = true
		elseif str:cmp('false') or str:cmp('off') or
		       str:cmp('no')    or str:cmp('0') then
			default = false
		else
			lib.warn('invalid configuration setting ',name,'="',{str.ptr,str.ct},'", expected boolean; using default value instead')
		end
		str:free()
	end
	return default

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

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
...
526
527
528
529
530
531
532

533
534
535
536
537
538
539
...
665
666
667
668
669
670
671










672
673
674
675
676
677
678
679
		grid-row: 1 / 2;
		display: grid;
		grid-template-columns: 1.1in 1fr;
		grid-template-rows: max-content 1fr;
		> .avatar {
			display: block;
			width: 1in; height: 1in;
			object-fit: contain;
			grid-column: 1 / 2;
			grid-row: 1 / 3;
			border: 1px solid black;
		}
		> .id {
			grid-column: 2 / 3;
			grid-row: 1 / 2;
................................................................................
	transition: 0.2s ease-out;
	>.avatar {
		grid-column: 1/2; grid-row: 1/2;
		background: linear-gradient(to bottom, tone(-53%), tone(-57%));
		img {
			display: block; width: 1in; height: 1in; margin:0;
			border-right: 1px solid tone(-65%);

		}
	}
	>a[href].username {
		display: block;
		grid-column: 1/3;
		grid-row: 2/3;
		text-align: left;
................................................................................
			padding-top: 0.12in;
			background: linear-gradient(to right, tone(-50%), tone(-50%,-0.7));
			border: 1px solid tone(-55%);
			border-left: none;
			text-shadow: 1px 1px 0 black;
		}
	}











}

hr {
	border: none;
	border-top: 1px solid tone(-30%);
	border-bottom: 1px solid tone(-55%);
}







|







 







>







 







>
>
>
>
>
>
>
>
>
>
|







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

hr {
	border: none;
	border-top: 1px solid tone(-30%);
	border-bottom: 1px solid tone(-55%);
}

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

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

terra m.powerset:affect_users()







|







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

terra m.powerset:affect_users()

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

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







<







36
37
38
39
40
41
42

43
44
45
46
47
48
49
do local strptr = (lib.mem.ptr(int8))
	local strref = (lib.mem.ref(int8))
	local byteptr = (lib.mem.ptr(uint8))
	local function install_funcs(ty)
		ty.metamethods.__cast = function(from,to,e)
			local v = e:asvalue()
			if type(v) == 'string' then

				return `ty {ptr = v, ct = [#v]}
			elseif from == &int8 then
				return `ty {ptr = e, ct = m.sz(e)}
			elseif to == &int8 then
				return e.ptr
			end
		end