parsav  Check-in [251b382f5c]

Overview
Comment:somewhat defuckulate build system
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 251b382f5c302eb3ca626d539ec46e9876e54cb0a3c14d945b56421c8b50e7f4
User & Date: lexi on 2021-01-22 16:48:53
Other Links: manifest | tags
Context
2021-01-22
17:18
defuckulate bytestream_trusted check-in: 7af9e0961e user: lexi tags: trunk
16:48
somewhat defuckulate build system check-in: 251b382f5c user: lexi tags: trunk
2021-01-19
22:02
add dist mechanism check-in: d248dc5965 user: lexi tags: trunk
Changes

Modified config.lua from [cba33505b3] to [74b801cbf1].

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
...
178
179
180
181
182
183
184


185
186
187
188
189
190
191

192

193
194
195
196
197
198
199
200
201
202
			if type(e) == 'function'
				then return e(conf)
				else return e
			end
		end
		return coalesce(
			pkgenv(v),
			pkv(e or v),
			(fbo and eval(fbo[v])),
			(fbv and eval(fbv[v])))
	end
	local name = cnfvar('override') or name
	local pcname = coalesce(cnfvar('pcname'), name)
	if conf.posix then
		pkc  = function(...) if locdep then return nil end
................................................................................
			end
		else pkc = nul end
	else
		print '(warn) configuring on non-POSIX OS, all relevant paths must be specified manually in environment variables or build will fail!'
	end
	locdep = u.ping('./lib/' .. name)
	local incdir, libdir, prefix


	if locdep then
		prefix = './lib/' .. name
		libdir = prefix .. coalesce(cnfvar('libbuilddir'),cnfvar('builddir'),'')
		incdir = prefix .. coalesce(cnfvar('srcincdir'),cnfvar('builddir'),'/include')
	else
		prefix = coalesce(cnfvar('prefix'), '/usr')
		libdir = cnfvar('libdir')

		incdir = cnfvar('incdir','includedir')

	end
	libdir = libdir or prefix .. '/lib'
	incdir = incdir or prefix .. '/include'

	local libstr = pkc '--libs-only-l' -- (--static is not reliable)
	local libs = fb and fb.libs or {}
	local linkstatic = locdep
	if (not locdep) and libstr then
		libs = {}
		for m in string.gmatch(libstr, '-l(%g+)') do







|







 







>
>


|
|

|
|
>
|
>

|
|







157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
			if type(e) == 'function'
				then return e(conf)
				else return e
			end
		end
		return coalesce(
			pkgenv(v),
			e ~= false and pkv(e or v) or nil,
			(fbo and eval(fbo[v])),
			(fbv and eval(fbv[v])))
	end
	local name = cnfvar('override') or name
	local pcname = coalesce(cnfvar('pcname'), name)
	if conf.posix then
		pkc  = function(...) if locdep then return nil end
................................................................................
			end
		else pkc = nul end
	else
		print '(warn) configuring on non-POSIX OS, all relevant paths must be specified manually in environment variables or build will fail!'
	end
	locdep = u.ping('./lib/' .. name)
	local incdir, libdir, prefix
	local libsfx = coalesce(cnfvar('libsuffix',false),'')
	local incsfx = coalesce(cnfvar('incsuffix',false),'')
	if locdep then
		prefix = './lib/' .. name
		libdir = prefix .. coalesce(cnfvar 'libbuilddir',cnfvar 'builddir','')
		incdir = prefix .. coalesce(cnfvar 'srcincdir',  cnfvar 'builddir','/include')
	else
		prefix = coalesce(cnfvar 'prefix', '/usr')
		libdir = cnfvar 'libdir'
			if libdir then libdir = libdir .. libsfx end
		incdir = cnfvar('incdir','includedir') 
			if incdir then incdir = incdir .. incsfx end
	end
	libdir = libdir or (prefix .. '/lib'     .. libsfx)
	incdir = incdir or (prefix .. '/include' .. incsfx)

	local libstr = pkc '--libs-only-l' -- (--static is not reliable)
	local libs = fb and fb.libs or {}
	local linkstatic = locdep
	if (not locdep) and libstr then
		libs = {}
		for m in string.gmatch(libstr, '-l(%g+)') do

Modified parsav.t from [b6e7e54998] to [dd405c9e82].

433
434
435
436
437
438
439

440
441
442
443
444
445
446
lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')


lib.load {
	'mem', 'math', 'str', 'file', 'crypt', 'ipc';
	'http', 'html', 'session', 'tpl', 'store', 'acl';

	'smackdown'; -- md-alike parser
}







>







433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.jc = lib.loadlib('json-c','json.h')

lib.load {
	'mem', 'math', 'str', 'file', 'crypt', 'ipc';
	'http', 'html', 'session', 'tpl', 'store', 'acl';

	'smackdown'; -- md-alike parser
}

Modified pkgdata.lua from [8c50f1b4b0] to [85c9cc69e8].

5
6
7
8
9
10
11
12











13
14
15
16
17
18
19
..
21
22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
	mbedtls = { 
		libs = {'mbedtls', 'mbedcrypto', 'mbedx509'};
		osvars = {
			linux_nixos = { -- lacks a *.pc on nixos systems
				prefix = sthunk('nix', 'path-info', 'nixos.mbedtls');
			}
		};
		vars = { builddir = '/library' };











	};
	mongoose = { vars = { builddir = '' } };
	libpq = {
		osvars = {
			linux_nixos = {
				prefix = sthunk('nix', 'path-info', 'nixos.postgresql.lib');
				incdir = function()
................................................................................
					return (util.exec(a)) .. '/include';
				end;
			};
		};
		vars = {pcname = 'postgresql';}
	};
	libc = {
		libs = {'dl'}; -- libc.so does not need explicit mention
		osvars = {

			linux_nixos = {
				prefix = sthunk('nix', 'path-info', 'nixos.glibc');
				override = 'glibc';
			};
			linux = { override = 'glibc'; };
		}
	};
}







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







 







|

>




<



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48
	mbedtls = { 
		libs = {'mbedtls', 'mbedcrypto', 'mbedx509'};
		osvars = {
			linux_nixos = { -- lacks a *.pc on nixos systems
				prefix = sthunk('nix', 'path-info', 'nixos.mbedtls');
			}
		};
		vars = { builddir = '/library', srcincdir = '/include' };
	};
	['json-c'] = {
		osvars = {
			linux_nixos = {
				prefix = sthunk('nix','path-info','nixos.json_c');
			};
		};
		vars = {
			builddir = '';
			incsuffix = '/json-c'; -- only used when path generated from prefix
		}
	};
	mongoose = { vars = { builddir = '' } };
	libpq = {
		osvars = {
			linux_nixos = {
				prefix = sthunk('nix', 'path-info', 'nixos.postgresql.lib');
				incdir = function()
................................................................................
					return (util.exec(a)) .. '/include';
				end;
			};
		};
		vars = {pcname = 'postgresql';}
	};
	libc = {
		libs = {'c'}; -- libc.so probably does not need explicit mention, but
		osvars = {
			linux = { override = 'glibc'; };
			linux_nixos = {
				prefix = sthunk('nix', 'path-info', 'nixos.glibc');
				override = 'glibc';
			};

		}
	};
}

Modified render/conf/users.t from [be5a9e1656] to [2f981259f7].

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
			   :lpush('" max="'):push(lib.math.decstr(max, &decbuf[20]),0)
			   :lpush('" value="'):push(lib.math.decstr(value, &decbuf[20]),0):lpush('"></div>')
		end
	end
end)

local input_pusher = function(kind,wrap,uniq)
	local fn = terra(acc: &lib.str.acc, name: pstr, val: pstr, lbl: pstr, on: bool, enabled: bool, class: pstr)
		if wrap then acc:lpush('<label>') end
		acc:lpush(['<input type="'..kind..'" name="']):ppush(name)
		if not wrap then
			acc:lpush('" id="'):ppush(name)
			if uniq then acc:lpush('-'):ppush(val) end
		end
		if val:ref()   then acc:lpush('" value="'):ppush(val) end
		if class:ref() then acc:lpush('" class="'):ppush(class) end
		acc:lpush('"')
		if on then acc:lpush(' checked') end
		if not enabled then acc:lpush(' disabled') end
		acc:lpush('>')
		if not wrap then acc:lpush('<label for="'):ppush(name)
		                 if uniq then acc:lpush('-'):ppush(val) end
		                 acc:lpush('">')
		            else acc:lpush(' ') end
		acc:ppush(lbl):lpush('</label>')
	end
	fn.name = string.format('push-input-element<%q>',kind)
	return fn
................................................................................
				push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
			end
			cinp:lpush('</div><div class="elem"><div class="check-panel">')

			if user.ptr.id ~= co.who.id and
			   ((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
				(user.ptr.rights.rank >  0 and co.who.rights.powers.demote())) then
				push_checkbox(&cinp, 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
			end

			cinp:lpush('</div></div>')

			if (co.who.rights.powers.elevate() or
			   co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
				var map = array([lib.store.powmap])
................................................................................
					for i=0, [map.type.N] do
						if (co.who.rights.powers and map[i].val):sz() > 0 then
							var on = (user.ptr.rights.powers and map[i].val):sz() > 0
							var enabled = (     on  and co.who.rights.powers.demote() ) or
										  ((not on) and co.who.rights.powers.elevate())
							var namea: lib.str.acc namea:pcompose(&co.srv.pool,'power-', map[i].name)
							var name = namea:finalize()
							push_pickbox(&cinp, name, pstr.null(), map[i].name, on, enabled, pstr.null())
							--name:free()
						end
					end
				cinp:lpush('</div></details>')
			end

			if co.who.id ~= uid and co.who.rights.powers.purge() then







|



|








|







 







|







 







|







182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
			   :lpush('" max="'):push(lib.math.decstr(max, &decbuf[20]),0)
			   :lpush('" value="'):push(lib.math.decstr(value, &decbuf[20]),0):lpush('"></div>')
		end
	end
end)

local input_pusher = function(kind,wrap,uniq)
	local fn = terra(acc: &lib.str.acc, id: pstr, name: pstr, val: pstr, lbl: pstr, on: bool, enabled: bool, class: pstr)
		if wrap then acc:lpush('<label>') end
		acc:lpush(['<input type="'..kind..'" name="']):ppush(name)
		if not wrap then
			acc:lpush('" id="'):ppush(id)
			if uniq then acc:lpush('-'):ppush(val) end
		end
		if val:ref()   then acc:lpush('" value="'):ppush(val) end
		if class:ref() then acc:lpush('" class="'):ppush(class) end
		acc:lpush('"')
		if on then acc:lpush(' checked') end
		if not enabled then acc:lpush(' disabled') end
		acc:lpush('>')
		if not wrap then acc:lpush('<label for="'):ppush(id)
		                 if uniq then acc:lpush('-'):ppush(val) end
		                 acc:lpush('">')
		            else acc:lpush(' ') end
		acc:ppush(lbl):lpush('</label>')
	end
	fn.name = string.format('push-input-element<%q>',kind)
	return fn
................................................................................
				push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
			end
			cinp:lpush('</div><div class="elem"><div class="check-panel">')

			if user.ptr.id ~= co.who.id and
			   ((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
				(user.ptr.rights.rank >  0 and co.who.rights.powers.demote())) then
				push_checkbox(&cinp, 'staff', 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
			end

			cinp:lpush('</div></div>')

			if (co.who.rights.powers.elevate() or
			   co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
				var map = array([lib.store.powmap])
................................................................................
					for i=0, [map.type.N] do
						if (co.who.rights.powers and map[i].val):sz() > 0 then
							var on = (user.ptr.rights.powers and map[i].val):sz() > 0
							var enabled = (     on  and co.who.rights.powers.demote() ) or
										  ((not on) and co.who.rights.powers.elevate())
							var namea: lib.str.acc namea:pcompose(&co.srv.pool,'power-', map[i].name)
							var name = namea:finalize()
							push_pickbox(&cinp, name, 'power', map[i].name, map[i].name, on, enabled, pstr.null())
							--name:free()
						end
					end
				cinp:lpush('</div></details>')
			end

			if co.who.id ~= uid and co.who.rights.powers.purge() then

Modified route.t from [48c04bb947] to [8f0d6bf9b0].

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
924
925
926
927
928
929
930






931
932
933
934
935
936
937
...
965
966
967
968
969
970
971
972
973
974
975
976
977




978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
			var act = co:ppostv('act')
			if rel.recip.block() then
				if act:cmp('follow') or act:cmp('subscribe') then
					co:complain(403,'blocked','you cannot follow a user you are blocked by') return
				end
			end
			if act:cmp('circle') then
				lib.dbg('encircling user!')
				var allcircs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
				var mycircs = co.srv:circle_memberships_uid(&co.srv.pool, co.who.id, actor.id)
				var marked = co.srv.pool:alloc(bool, allcircs.ct)
				var member = co.srv.pool:alloc(bool, allcircs.ct)
				for i = 0, marked.ct do
					marked(i) = false
					member(i) = false
................................................................................
		handle.ct = uri.ct - 2
		uri:advance(uri.ct)
	elseif handle.ct + 2 < uri.ct then uri:advance(handle.ct + 2) end

	lib.dbg('looking up user by xid "', {handle.ptr,handle.ct} ,'", path: ', {uri.ptr,uri.ct})

	var path = lib.http.hier(&co.srv.pool, uri) --defer path:free()
	for i=0,path.ct do
		lib.dbg('got path component ', {path.ptr[i].ptr, path.ptr[i].ct})
	end

	var actor = co.srv:actor_fetch_xid(handle)
	if actor.ptr == nil then
		co:complain(404,'no such user','no such user known to this server')
		return
	end
	defer actor:free()
................................................................................
	if not data then goto e404 end
	do defer data:free() defer mime:free()
		co:bytestream(mime,data)
	return end

	::e404:: do co:complain(404, 'artifact not found', 'no such artifact has been uploaded to this instance') return end
end







-- entry points
terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t)
	lib.dbg('handling URI of form ', {uri.ptr,uri.ct})
	co.navbar = lib.render.nav(co)
	-- some routes are non-hierarchical, and can be resolved with a simple strcmp
	-- we run through those first before giving up and parsing the URI
................................................................................
	elseif uri:cmp( '/logout') then
		if co.aid == 0
			then goto notfound
			else co:reroute_cookie('/','auth=; Path=/')
		end
	else -- hierarchical routes
		var path = lib.http.hier(&co.srv.pool, uri) --defer path:free()
		if path.ct > 1 and path(0):cmp(lib.str.lit('user')) then
			http.actor_profile_uid(co, path, meth)
		elseif path.ct > 1 and path(0):cmp(lib.str.lit('post')) then
			http.tweet_page(co, path, meth)
		elseif path(0):cmp(lib.str.lit('tl')) then
			http.timeline(co, path)




		elseif path(0):cmp(lib.str.lit('media')) then
			if co.aid == 0 then goto unauth end
			http.media_manager(co, path, meth, co.who.id)
		elseif path(0):cmp(lib.str.lit('doc')) then
			if not meth_get(meth) then goto wrongmeth end
			http.documentation(co, path)
		elseif path(0):cmp(lib.str.lit('conf')) then
			if co.aid == 0 then goto unauth end
			http.configure(co,path,meth)
		else goto notfound end
	end
	do return end

	::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end
	::notfound:: co:complain(404, 'not found', 'no such resource available') do return end
	::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end
end







<







 







<
<
<







 







>
>
>
>
>
>







 







|

|

|

>
>
>
>
|


|


|










17
18
19
20
21
22
23

24
25
26
27
28
29
30
...
106
107
108
109
110
111
112



113
114
115
116
117
118
119
...
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
...
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
			var act = co:ppostv('act')
			if rel.recip.block() then
				if act:cmp('follow') or act:cmp('subscribe') then
					co:complain(403,'blocked','you cannot follow a user you are blocked by') return
				end
			end
			if act:cmp('circle') then

				var allcircs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
				var mycircs = co.srv:circle_memberships_uid(&co.srv.pool, co.who.id, actor.id)
				var marked = co.srv.pool:alloc(bool, allcircs.ct)
				var member = co.srv.pool:alloc(bool, allcircs.ct)
				for i = 0, marked.ct do
					marked(i) = false
					member(i) = false
................................................................................
		handle.ct = uri.ct - 2
		uri:advance(uri.ct)
	elseif handle.ct + 2 < uri.ct then uri:advance(handle.ct + 2) end

	lib.dbg('looking up user by xid "', {handle.ptr,handle.ct} ,'", path: ', {uri.ptr,uri.ct})

	var path = lib.http.hier(&co.srv.pool, uri) --defer path:free()




	var actor = co.srv:actor_fetch_xid(handle)
	if actor.ptr == nil then
		co:complain(404,'no such user','no such user known to this server')
		return
	end
	defer actor:free()
................................................................................
	if not data then goto e404 end
	do defer data:free() defer mime:free()
		co:bytestream(mime,data)
	return end

	::e404:: do co:complain(404, 'artifact not found', 'no such artifact has been uploaded to this instance') return end
end

local json = {}

terra json.webfinger(co: &lib.srv.convo)
	
end

-- entry points
terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t)
	lib.dbg('handling URI of form ', {uri.ptr,uri.ct})
	co.navbar = lib.render.nav(co)
	-- some routes are non-hierarchical, and can be resolved with a simple strcmp
	-- we run through those first before giving up and parsing the URI
................................................................................
	elseif uri:cmp( '/logout') then
		if co.aid == 0
			then goto notfound
			else co:reroute_cookie('/','auth=; Path=/')
		end
	else -- hierarchical routes
		var path = lib.http.hier(&co.srv.pool, uri) --defer path:free()
		if path.ct > 1 and path(0):cmp('user') then
			http.actor_profile_uid(co, path, meth)
		elseif path.ct > 1 and path(0):cmp('post') then
			http.tweet_page(co, path, meth)
		elseif path(0):cmp('tl') then
			http.timeline(co, path)
		elseif path(0):cmp('.well-known') then
			if path(1):cmp('webfinger') then
				json.webfinger(co)
			end
		elseif path(0):cmp('media') then
			if co.aid == 0 then goto unauth end
			http.media_manager(co, path, meth, co.who.id)
		elseif path(0):cmp('doc') then
			if not meth_get(meth) then goto wrongmeth end
			http.documentation(co, path)
		elseif path(0):cmp('conf') then
			if co.aid == 0 then goto unauth end
			http.configure(co,path,meth)
		else goto notfound end
	end
	do return end

	::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end
	::notfound:: co:complain(404, 'not found', 'no such resource available') do return end
	::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end
end

Modified srv.t from [fbf604f655] to [113b729c73].

304
305
306
307
308
309
310












311
312
313
314
315
316
317
...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
		self:rawpage(200, pg, [lib.mem.ptr(lib.http.header)] {
			ptr = &hdrs[0], ct = 3
		})
	end
end

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('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)
	var hdrs = array(
		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
		lib.http.header { key = 'Location',     value = dest },
		lib.http.header { key = 'Set-Cookie',   value = cookie }







>
>
>
>
>
>
>
>
>
>
>
>







 







|
<
<







304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
...
331
332
333
334
335
336
337
338


339
340
341
342
343
344
345
		self:rawpage(200, pg, [lib.mem.ptr(lib.http.header)] {
			ptr = &hdrs[0], ct = 3
		})
	end
end

terra convo:stdpage(pg: convo.page) self:statpage(200, pg) end

terra convo:bytestream_trusted(lockdown: bool, mime: pstring, data: lib.mem.ptr(uint8))
	var lockhdr = "Content-Security-Policy: sandbox; default-src 'none'; form-action 'none'; navigate-to 'none';\r\n"
	if not lockdown then lockhdr = "" end
	lib.net.mg_printf(self.con, "HTTP/1.1 200 OK\r\nContent-Type: %.*s\r\nContent-Length: %llu\r\n%sX-Content-Options: nosniff\r\n\r\n", mime.ct, mime.ptr, data.ct + 2, lockdown)
	lib.net.mg_send(self.con, data.ptr, data.ct)
	lib.net.mg_send(self.con, '\r\n', 2)
end

terra convo:json(data: pstring)
	self:bytestream_trusted(false, 'application/ld+json', data:blob())
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('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
	self:bytestream_trusted(true, mime, data)


end

terra convo:reroute_cookie(dest: rawstring, cookie: rawstring)
	var hdrs = array(
		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
		lib.http.header { key = 'Location',     value = dest },
		lib.http.header { key = 'Set-Cookie',   value = cookie }