parsav  Diff

Differences From Artifact [9e0d1b7489]:

To Artifact [675eda18a7]:


7
8
9
10
11
12
13

14
15
16
17
18
19
20
...
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
...
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
162
...
170
171
172
173
174
175
176
177
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
...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338

339
340
341
342
343
344
345
346
347
348
349
350
351
...
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
...
423
424
425
426
427
428
429


430
431







432
433
434
435
436
437
438
...
442
443
444
445
446
447
448

449
450

451
452

453
454

455
456
457
458
459
460
461
...
670
671
672
673
674
675
676
677
678

679
680
681
682
683
684
685
686
687
688

689
690
691
692
693
694
695
696
697
698

699
700
701
702
703
704
705
706
707
708
709
710
711

712






713

714
715
716
717
718
719
720
721
	secret: lib.mem.ptr(int8)
	pol_sec: secmode.t
	pol_reg: bool
	credmgd: bool
	maxupsz: intptr
	instance: lib.mem.ptr(int8)
	overlord: &srv

}
local struct srv {
	sources: lib.mem.ptr(lib.store.source)
	webmgr: lib.net.mg_mgr
	webcon: &lib.net.mg_connection
	cfg: cfgcache
	id: rawstring
................................................................................
					if [ok] then break
						else r = empty end
				end
			end
		in r end
	end
end)











local struct convo {
	srv: &srv
	con: &lib.net.mg_connection
	msg: &lib.net.mg_http_message
	aid: uint64 -- 0 if logged out
	aid_issue: lib.store.timepoint
	who: &lib.store.actor -- who we're logged in as, if aid ~= 0
	peer: lib.store.inet
	reqtype: lib.http.mime.t -- negotiated content type


-- cache

	navbar: lib.mem.ptr(int8)
	actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
-- private
	varbuf: lib.mem.ptr(int8)
	vbofs: &int8
}








-- this is unfortunately necessary to work around a terra bug
-- it can't seem to handle forward-declarations of structs in C

local getpeer
do local struct strucheader {
		next: &lib.net.mg_connection
................................................................................
		peer: lib.net.mg_addr
	}
	terra getpeer(con: &lib.net.mg_connection)
		return [&strucheader](con).peer
	end
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 }
	)

	var body = data.view.docskel {
		instance = self.srv.cfg.instance.ptr;
		title = 'rerouting';
		body = 'you are being redirected';
		class = 'error';
		navlinks = '';

	}

	body:send(self.con, 303, [lib.mem.ptr(lib.http.header)] {
		ptr = &hdrs[0], ct = [hdrs.type.N] - lib.trn(cookie == nil,1,0)
	})
end

................................................................................
		lib.dbg('sending cookie ',{&sesskey[0],15})
		p = lib.str.ncpy(p, '; Path=/', 9)
	end
	self:reroute_cookie(dest, &sesskey[0])
end
 
terra convo:complain(code: uint16, title: rawstring, msg: rawstring)
	var hdrs = array(
		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
		lib.http.header { key = 'Cache-Control', value = 'no-store' }
	)

	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.webp"><h1>',title,'</h1><p>',msg,'</p></div>')
	var body = data.view.docskel {
		instance = self.srv.cfg.instance;
		title = ti:finalize();
		body = bo:finalize();
		class = lib.str.plit 'error';
		navlinks = lib.coalesce(self.navbar, [lib.mem.ptr(int8)]{ptr='',ct=0});
	}

	if body.body.ptr == nil then
		body.body = lib.str.plit"i'm sorry, dave. i can't let you do that"
	end

	body:send(self.con, code, [lib.mem.ptr(lib.http.header)] {
		ptr = &hdrs[0], ct = [hdrs.type.N]
	})

	body.title:free()
	body.body:free()
end

convo.methods.assertpow = macro(function(self, pow)
	return quote
................................................................................
		if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then
			ok = false
			self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action'])
		end
	in ok end
end)

struct convo.page {
	title: pstring
	body: pstring
	class: pstring
	cache: bool
}

terra convo:stdpage(pg: convo.page)
	var doc = data.view.docskel {
		instance = self.srv.cfg.instance;
		title = pg.title;
		body = pg.body;
		class = pg.class;
		navlinks = self.navbar;
	}

	var hdrs = array(
		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
		lib.http.header { key = 'Cache-Control', value = 'no-store' }
	)

	doc:send(self.con,200,[lib.mem.ptr(lib.http.header)] {ct = [hdrs.type.N] - lib.trn(pg.cache,1,0), ptr = &hdrs[0]})
end

-- CALL ONLY ONCE PER VAR
terra convo:postv(name: rawstring)
	if self.varbuf.ptr == nil then
		self.varbuf = lib.mem.heapa(int8, self.msg.body.len + self.msg.query.len)
		self.vbofs = self.varbuf.ptr
	end
	var o = lib.net.mg_http_get_var(&self.msg.body, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr))
................................................................................
		if lib.str.ncmp(mimevar.ptr, mime, lib.math.biggest(mimevar.ct, [#mime])) == 0 then
			ret = [lib.http.mime[name]]
		else ret = [mimeneg] end
	in ret end
end

local handle = {
	http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque)
		var server = [&srv](ext)
		var mgpeer = getpeer(con)
		var peer = lib.store.inet { port = mgpeer.port; }
		if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end
		if peer.pv == 6 then
			for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end
		else -- v4
			@[&uint32](&peer.v4) = mgpeer.ip
................................................................................
		-- for now i'm leaving it as is, but note that netmask restrictions
		-- WILL NOT WORK until upstream gets its shit together. FIXME

		-- needs to check for an X-Forwarded-For header from nginx and
		-- use that instead of the peer iff peer is ::1/127.1 FIXME
		-- maybe also haproxy support?

		switch event do
			case lib.net.MG_EV_HTTP_MSG then
				lib.dbg('routing HTTP request')
				var msg = [&lib.net.mg_http_message](p)
				var co = convo {
					con = con, srv = server, msg = msg;
					aid = 0, aid_issue = 0, who = nil;
					reqtype = lib.http.mime.none;
					peer = peer;
				} co.varbuf.ptr = nil
				  co.navbar.ptr = nil
				  co.actorcache.top = 0
				  co.actorcache.cur = 0


				-- first, check for an accept header. if it's there, we need to
				-- iterate over the values and pick the highest-priority one
				do var acc = lib.http.findheader(msg, 'Accept')
					-- TODO handle q-value
					if acc.ptr ~= nil then
						var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr }
						var i = 0 while i < acc.ct do
							if acc.ptr[i] == @',' or acc.ptr[i] == @';' then
								mimevar.ct = (acc.ptr+i) - mimevar.ptr
								var t = [mimeneg]
								if t ~= lib.http.mime.none then
									co.reqtype = t
................................................................................
					else co.reqtype = lib.http.mime.html end
				::foundtype::end

				-- we need to check if there's any cookies sent with the request,
				-- and if so, whether they contain any credentials. this will be
				-- used to set the auth parameters in the http conversation
				var cookies_p = lib.http.findheader(msg, 'Cookie')
				if cookies_p ~= nil then
					var cookies = cookies_p.ptr
					var key = [lib.mem.ref(int8)] {ptr = cookies, ct = 0}
					var val = [lib.mem.ref(int8)] {ptr = nil, ct = 0}
					var i = 0 while i < cookies_p.ct    and
					                cookies[i] ~= 0     and
					                cookies[i] ~= @'\r' and
									cookies[i] ~= @'\n' do -- cover all the bases
................................................................................
				end

				if co.aid ~= 0 then
					var sess, usr = co.srv:actor_session_fetch(co.aid, peer, co.aid_issue)
					if sess.ok == false then co.aid = 0 co.aid_issue = 0 else
						co.who = usr.ptr
						co.who.rights.powers = server:actor_powers_fetch(co.who.id)


					end
				end








				var uridec = lib.mem.heapa(int8, msg.uri.len) defer uridec:free()
				var urideclen = lib.net.mg_url_decode(msg.uri.ptr, msg.uri.len, uridec.ptr, uridec.ct, 1)

				var uri = uridec
				if urideclen == -1 then
					for i = 0,msg.uri.len do
................................................................................
						end
					end
					uri.ct = msg.uri.len
				else uri.ct = urideclen end
				lib.dbg('routing URI ', {uri.ptr, uri.ct})
				
				if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then

					route.dispatch_http(&co, uri, [lib.http.method.get])
				elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then

					route.dispatch_http(&co, uri, [lib.http.method.post])
				elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then

					route.dispatch_http(&co, uri, [lib.http.method.head])
				elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then

					route.dispatch_http(&co, uri, [lib.http.method.options])
				else
					co:complain(400,'unknown method','you have submitted an invalid http request')
				end

				if co.aid ~= 0 then lib.mem.heapf(co.who) end
				if co.varbuf.ptr ~= nil then co.varbuf:free() end
................................................................................
	do self.pol_reg = false
	var sreg = self.overlord:conf_get('policy-self-register')
	if sreg:ref() then
		if lib.str.cmp(sreg.ptr, 'on') == 0
			then self.pol_reg = true
			else self.pol_reg = false
		end
	end
	sreg:free() end


	do self.credmgd = false
	var sreg = self.overlord:conf_get('credential-store')
	if sreg:ref() then
		if lib.str.cmp(sreg.ptr, 'managed') == 0
			then self.credmgd = true
			else self.credmgd = false
		end
	end
	sreg:free() end


	do self.maxupsz = [1024 * 100] -- 100 kilobyte default
	var sreg = self.overlord:conf_get('maximum-artifact-size')
	if sreg:ref() then
		var sz, ok = lib.math.fsz_parse(sreg)
		if ok then self.maxupsz = sz else
			lib.warn('invalid configuration value for maximum-artifact-size; keeping default 100K upload limit')
		end
	end
	sreg:free() end

	
	self.pol_sec = secmode.lockdown
	var smode = self.overlord:conf_get('policy-security')
	if smode.ptr ~= nil then
		if lib.str.cmp(smode.ptr, 'public') == 0 then
			self.pol_sec = secmode.public
		elseif lib.str.cmp(smode.ptr, 'private') == 0 then
			self.pol_sec = secmode.private
		elseif lib.str.cmp(smode.ptr, 'lockdown') == 0 then
			self.pol_sec = secmode.lockdown
		elseif lib.str.cmp(smode.ptr, 'isolate') == 0 then
			self.pol_sec = secmode.isolate
		end

	end






	smode:free()

end

return {
	overlord = srv;
	convo = convo;
	route = route;
	secmode = secmode;
}







>







 







>
>
>
>
>
>
>
>
>
>










>
>

>






>
>
>
>
>
>
>







 







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













>







 







|
<
<
<



|
<



|


|
<
<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
|







 







|


|




|




>





|







 







|







 







>
>


>
>
>
>
>
>
>







 







>


>


>


>







 







<
|
>








<
|
>








<
|
>













>

>
>
>
>
>
>
|
>








7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
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
...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
...
255
256
257
258
259
260
261
262



263
264
265
266

267
268
269
270
271
272
273






274
275
276
277
278
279
280
...
282
283
284
285
286
287
288
























289
290
291
292
293
294
295
...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
...
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
...
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
...
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
...
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
...
735
736
737
738
739
740
741

742
743
744
745
746
747
748
749
750
751

752
753
754
755
756
757
758
759
760
761

762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
	secret: lib.mem.ptr(int8)
	pol_sec: secmode.t
	pol_reg: bool
	credmgd: bool
	maxupsz: intptr
	instance: lib.mem.ptr(int8)
	overlord: &srv
	ui_hue: uint16
}
local struct srv {
	sources: lib.mem.ptr(lib.store.source)
	webmgr: lib.net.mg_mgr
	webcon: &lib.net.mg_connection
	cfg: cfgcache
	id: rawstring
................................................................................
					if [ok] then break
						else r = empty end
				end
			end
		in r end
	end
end)

terra lib.store.post:publish(s: &srv)
	self:comp()
	self.posted = lib.osclock.time(nil)
	self.discovered = self.posted
	self.chgcount = 0
	self.edited = 0
	self.id = s:post_create(self)
	return self.id
end

local struct convo {
	srv: &srv
	con: &lib.net.mg_connection
	msg: &lib.net.mg_http_message
	aid: uint64 -- 0 if logged out
	aid_issue: lib.store.timepoint
	who: &lib.store.actor -- who we're logged in as, if aid ~= 0
	peer: lib.store.inet
	reqtype: lib.http.mime.t -- negotiated content type
	method: lib.http.method.t
	live_last: lib.store.timepoint
-- cache
	ui_hue: uint16
	navbar: lib.mem.ptr(int8)
	actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
-- private
	varbuf: lib.mem.ptr(int8)
	vbofs: &int8
}

struct convo.page {
	title: pstring
	body: pstring
	class: pstring
	cache: bool
}

-- this is unfortunately necessary to work around a terra bug
-- it can't seem to handle forward-declarations of structs in C

local getpeer
do local struct strucheader {
		next: &lib.net.mg_connection
................................................................................
		peer: lib.net.mg_addr
	}
	terra getpeer(con: &lib.net.mg_connection)
		return [&strucheader](con).peer
	end
end

terra convo:rawpage(code: uint16, pg: convo.page, hdrs: lib.mem.ptr(lib.http.header))
	var doc = data.view.docskel {
		instance = self.srv.cfg.instance;
		title = pg.title;
		body = pg.body;
		class = pg.class;
		navlinks = self.navbar;
		attr = '';
	}
	var attrbuf: int8[32]
	if self.aid ~= 0 and self.ui_hue ~= 323 then
		var hdecbuf: int8[21]
		var hdec = lib.math.decstr(self.ui_hue, &hdecbuf[20])
		lib.str.cpy(&attrbuf[0], ' style="--hue:')
		lib.str.cpy(&attrbuf[14], hdec)
		var len = &hdecbuf[20] - hdec 
		lib.str.cpy(&attrbuf[14] + len, '"')
		doc.attr = &attrbuf[0]
	end

	if self.method == [lib.http.method.head]
		then doc:head(self.con,code,hdrs)
		else doc:send(self.con,code,hdrs)
	end
end

terra convo:statpage(code: uint16, pg: convo.page)
	var hdrs = array(
		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
		lib.http.header { key = 'Cache-Control', value = 'no-store' }
	)
	self:rawpage(code,pg, [lib.mem.ptr(lib.http.header)] {
		ptr = &hdrs[0];
		ct = [hdrs.type.N] - lib.trn(pg.cache,1,0);
	})
end

terra convo:livepage(pg: convo.page, lastup: lib.store.timepoint)
	var nbuf: int8[21]
	var hdrs = array(
		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
		lib.http.header { key = 'Cache-Control', value = 'no-store' },
		lib.http.header {
			key = 'X-Live-Newest-Artifact';
			value = lib.math.decstr(lastup, &nbuf[20]);
		},
		lib.http.header { key = 'Content-Length', value = '0' }
	)
	if self.live_last ~= 0 and self.live_last <= lastup then
		lib.net.mg_printf(self.con, 'HTTP/1.1 %s', lib.http.codestr(200))
		for i = 0, [hdrs.type.N] do
			lib.net.mg_printf(self.con, '%s: %s\r\n', hdrs[i].key, hdrs[i].value)
		end
		lib.net.mg_printf(self.con, '\r\n')
	else
		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: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 }
	)

	var body = data.view.docskel {
		instance = self.srv.cfg.instance.ptr;
		title = 'rerouting';
		body = 'you are being redirected';
		class = 'error';
		navlinks = '';
		attr = '';
	}

	body:send(self.con, 303, [lib.mem.ptr(lib.http.header)] {
		ptr = &hdrs[0], ct = [hdrs.type.N] - lib.trn(cookie == nil,1,0)
	})
end

................................................................................
		lib.dbg('sending cookie ',{&sesskey[0],15})
		p = lib.str.ncpy(p, '; Path=/', 9)
	end
	self:reroute_cookie(dest, &sesskey[0])
end
 
terra convo:complain(code: uint16, title: rawstring, msg: 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.webp"><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()
end

convo.methods.assertpow = macro(function(self, pow)
	return quote
................................................................................
		if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then
			ok = false
			self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action'])
		end
	in ok end
end)

























-- CALL ONLY ONCE PER VAR
terra convo:postv(name: rawstring)
	if self.varbuf.ptr == nil then
		self.varbuf = lib.mem.heapa(int8, self.msg.body.len + self.msg.query.len)
		self.vbofs = self.varbuf.ptr
	end
	var o = lib.net.mg_http_get_var(&self.msg.body, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr))
................................................................................
		if lib.str.ncmp(mimevar.ptr, mime, lib.math.biggest(mimevar.ct, [#mime])) == 0 then
			ret = [lib.http.mime[name]]
		else ret = [mimeneg] end
	in ret end
end

local handle = {
	http = terra(con: &lib.net.mg_connection, event_kind: int, event: &opaque, userdata: &opaque)
		var server = [&srv](userdata)
		var mgpeer = getpeer(con)
		var peer = lib.store.inet { port = mgpeer.port; }
		if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end
		if peer.pv == 6 then
			for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end
		else -- v4
			@[&uint32](&peer.v4) = mgpeer.ip
................................................................................
		-- for now i'm leaving it as is, but note that netmask restrictions
		-- WILL NOT WORK until upstream gets its shit together. FIXME

		-- needs to check for an X-Forwarded-For header from nginx and
		-- use that instead of the peer iff peer is ::1/127.1 FIXME
		-- maybe also haproxy support?

		switch event_kind do
			case lib.net.MG_EV_HTTP_MSG then
				lib.dbg('routing HTTP request')
				var msg = [&lib.net.mg_http_message](event)
				var co = convo {
					con = con, srv = server, msg = msg;
					aid = 0, aid_issue = 0, who = nil;
					reqtype = lib.http.mime.none;
					peer = peer, live_last = 0;
				} co.varbuf.ptr = nil
				  co.navbar.ptr = nil
				  co.actorcache.top = 0
				  co.actorcache.cur = 0
				  co.ui_hue = server.cfg.ui_hue

				-- first, check for an accept header. if it's there, we need to
				-- iterate over the values and pick the highest-priority one
				do var acc = lib.http.findheader(msg, 'Accept')
					-- TODO handle q-value
					if acc ~= nil and acc.ptr ~= nil then
						var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr }
						var i = 0 while i < acc.ct do
							if acc.ptr[i] == @',' or acc.ptr[i] == @';' then
								mimevar.ct = (acc.ptr+i) - mimevar.ptr
								var t = [mimeneg]
								if t ~= lib.http.mime.none then
									co.reqtype = t
................................................................................
					else co.reqtype = lib.http.mime.html end
				::foundtype::end

				-- we need to check if there's any cookies sent with the request,
				-- and if so, whether they contain any credentials. this will be
				-- used to set the auth parameters in the http conversation
				var cookies_p = lib.http.findheader(msg, 'Cookie')
				if cookies_p ~= nil and cookies_p.ptr ~= nil then
					var cookies = cookies_p.ptr
					var key = [lib.mem.ref(int8)] {ptr = cookies, ct = 0}
					var val = [lib.mem.ref(int8)] {ptr = nil, ct = 0}
					var i = 0 while i < cookies_p.ct    and
					                cookies[i] ~= 0     and
					                cookies[i] ~= @'\r' and
									cookies[i] ~= @'\n' do -- cover all the bases
................................................................................
				end

				if co.aid ~= 0 then
					var sess, usr = co.srv:actor_session_fetch(co.aid, peer, co.aid_issue)
					if sess.ok == false then co.aid = 0 co.aid_issue = 0 else
						co.who = usr.ptr
						co.who.rights.powers = server:actor_powers_fetch(co.who.id)
						var userhue, hueok = server:actor_conf_int_get(co.who.id, 'ui-accent')
						if hueok then co.ui_hue = userhue end
					end
				end

				var livelast_p = lib.http.findheader(msg, 'X-Live-Last-Arrival')
				if livelast_p ~= nil and livelast_p.ptr ~= nil then
					var ll, ok = lib.math.decparse(pstring{ptr = livelast_p.ptr, ct = livelast_p.ct - 1})
					if ok then co.live_last = ll end
				end


				var uridec = lib.mem.heapa(int8, msg.uri.len) defer uridec:free()
				var urideclen = lib.net.mg_url_decode(msg.uri.ptr, msg.uri.len, uridec.ptr, uridec.ct, 1)

				var uri = uridec
				if urideclen == -1 then
					for i = 0,msg.uri.len do
................................................................................
						end
					end
					uri.ct = msg.uri.len
				else uri.ct = urideclen end
				lib.dbg('routing URI ', {uri.ptr, uri.ct})
				
				if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then
					co.method = [lib.http.method.get]
					route.dispatch_http(&co, uri, [lib.http.method.get])
				elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then
					co.method = [lib.http.method.get]
					route.dispatch_http(&co, uri, [lib.http.method.post])
				elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then
					co.method = [lib.http.method.head]
					route.dispatch_http(&co, uri, [lib.http.method.head])
				elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then
					co.method = [lib.http.method.options]
					route.dispatch_http(&co, uri, [lib.http.method.options])
				else
					co:complain(400,'unknown method','you have submitted an invalid http request')
				end

				if co.aid ~= 0 then lib.mem.heapf(co.who) end
				if co.varbuf.ptr ~= nil then co.varbuf:free() end
................................................................................
	do self.pol_reg = false
	var sreg = self.overlord:conf_get('policy-self-register')
	if sreg:ref() then
		if lib.str.cmp(sreg.ptr, 'on') == 0
			then self.pol_reg = true
			else self.pol_reg = false
		end

		sreg:free()
	end end

	do self.credmgd = false
	var sreg = self.overlord:conf_get('credential-store')
	if sreg:ref() then
		if lib.str.cmp(sreg.ptr, 'managed') == 0
			then self.credmgd = true
			else self.credmgd = false
		end

		sreg:free()
	end end

	do self.maxupsz = [1024 * 100] -- 100 kilobyte default
	var sreg = self.overlord:conf_get('maximum-artifact-size')
	if sreg:ref() then
		var sz, ok = lib.math.fsz_parse(sreg)
		if ok then self.maxupsz = sz else
			lib.warn('invalid configuration value for maximum-artifact-size; keeping default 100K upload limit')
		end

		sreg:free() end
	end
	
	self.pol_sec = secmode.lockdown
	var smode = self.overlord:conf_get('policy-security')
	if smode.ptr ~= nil then
		if lib.str.cmp(smode.ptr, 'public') == 0 then
			self.pol_sec = secmode.public
		elseif lib.str.cmp(smode.ptr, 'private') == 0 then
			self.pol_sec = secmode.private
		elseif lib.str.cmp(smode.ptr, 'lockdown') == 0 then
			self.pol_sec = secmode.lockdown
		elseif lib.str.cmp(smode.ptr, 'isolate') == 0 then
			self.pol_sec = secmode.isolate
		end
		smode:free()
	end

	self.ui_hue = config.default_ui_accent
	var shue = self.overlord:conf_get('ui-accent')
	if shue.ptr ~= nil then
		var hue,ok = lib.math.decparse(shue)
		if ok then self.ui_hue = hue end
		shue:free()
	end
end

return {
	overlord = srv;
	convo = convo;
	route = route;
	secmode = secmode;
}