parsav  Diff

Differences From Artifact [9e0d1b7489]:

To Artifact [675eda18a7]:


     7      7   	secret: lib.mem.ptr(int8)
     8      8   	pol_sec: secmode.t
     9      9   	pol_reg: bool
    10     10   	credmgd: bool
    11     11   	maxupsz: intptr
    12     12   	instance: lib.mem.ptr(int8)
    13     13   	overlord: &srv
           14  +	ui_hue: uint16
    14     15   }
    15     16   local struct srv {
    16     17   	sources: lib.mem.ptr(lib.store.source)
    17     18   	webmgr: lib.net.mg_mgr
    18     19   	webcon: &lib.net.mg_connection
    19     20   	cfg: cfgcache
    20     21   	id: rawstring
................................................................................
   104    105   					if [ok] then break
   105    106   						else r = empty end
   106    107   				end
   107    108   			end
   108    109   		in r end
   109    110   	end
   110    111   end)
          112  +
          113  +terra lib.store.post:publish(s: &srv)
          114  +	self:comp()
          115  +	self.posted = lib.osclock.time(nil)
          116  +	self.discovered = self.posted
          117  +	self.chgcount = 0
          118  +	self.edited = 0
          119  +	self.id = s:post_create(self)
          120  +	return self.id
          121  +end
   111    122   
   112    123   local struct convo {
   113    124   	srv: &srv
   114    125   	con: &lib.net.mg_connection
   115    126   	msg: &lib.net.mg_http_message
   116    127   	aid: uint64 -- 0 if logged out
   117    128   	aid_issue: lib.store.timepoint
   118    129   	who: &lib.store.actor -- who we're logged in as, if aid ~= 0
   119    130   	peer: lib.store.inet
   120    131   	reqtype: lib.http.mime.t -- negotiated content type
          132  +	method: lib.http.method.t
          133  +	live_last: lib.store.timepoint
   121    134   -- cache
          135  +	ui_hue: uint16
   122    136   	navbar: lib.mem.ptr(int8)
   123    137   	actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
   124    138   -- private
   125    139   	varbuf: lib.mem.ptr(int8)
   126    140   	vbofs: &int8
   127    141   }
          142  +
          143  +struct convo.page {
          144  +	title: pstring
          145  +	body: pstring
          146  +	class: pstring
          147  +	cache: bool
          148  +}
   128    149   
   129    150   -- this is unfortunately necessary to work around a terra bug
   130    151   -- it can't seem to handle forward-declarations of structs in C
   131    152   
   132    153   local getpeer
   133    154   do local struct strucheader {
   134    155   		next: &lib.net.mg_connection
................................................................................
   136    157   		peer: lib.net.mg_addr
   137    158   	}
   138    159   	terra getpeer(con: &lib.net.mg_connection)
   139    160   		return [&strucheader](con).peer
   140    161   	end
   141    162   end
   142    163   
          164  +terra convo:rawpage(code: uint16, pg: convo.page, hdrs: lib.mem.ptr(lib.http.header))
          165  +	var doc = data.view.docskel {
          166  +		instance = self.srv.cfg.instance;
          167  +		title = pg.title;
          168  +		body = pg.body;
          169  +		class = pg.class;
          170  +		navlinks = self.navbar;
          171  +		attr = '';
          172  +	}
          173  +	var attrbuf: int8[32]
          174  +	if self.aid ~= 0 and self.ui_hue ~= 323 then
          175  +		var hdecbuf: int8[21]
          176  +		var hdec = lib.math.decstr(self.ui_hue, &hdecbuf[20])
          177  +		lib.str.cpy(&attrbuf[0], ' style="--hue:')
          178  +		lib.str.cpy(&attrbuf[14], hdec)
          179  +		var len = &hdecbuf[20] - hdec 
          180  +		lib.str.cpy(&attrbuf[14] + len, '"')
          181  +		doc.attr = &attrbuf[0]
          182  +	end
          183  +
          184  +	if self.method == [lib.http.method.head]
          185  +		then doc:head(self.con,code,hdrs)
          186  +		else doc:send(self.con,code,hdrs)
          187  +	end
          188  +end
          189  +
          190  +terra convo:statpage(code: uint16, pg: convo.page)
          191  +	var hdrs = array(
          192  +		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
          193  +		lib.http.header { key = 'Cache-Control', value = 'no-store' }
          194  +	)
          195  +	self:rawpage(code,pg, [lib.mem.ptr(lib.http.header)] {
          196  +		ptr = &hdrs[0];
          197  +		ct = [hdrs.type.N] - lib.trn(pg.cache,1,0);
          198  +	})
          199  +end
          200  +
          201  +terra convo:livepage(pg: convo.page, lastup: lib.store.timepoint)
          202  +	var nbuf: int8[21]
          203  +	var hdrs = array(
          204  +		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
          205  +		lib.http.header { key = 'Cache-Control', value = 'no-store' },
          206  +		lib.http.header {
          207  +			key = 'X-Live-Newest-Artifact';
          208  +			value = lib.math.decstr(lastup, &nbuf[20]);
          209  +		},
          210  +		lib.http.header { key = 'Content-Length', value = '0' }
          211  +	)
          212  +	if self.live_last ~= 0 and self.live_last <= lastup then
          213  +		lib.net.mg_printf(self.con, 'HTTP/1.1 %s', lib.http.codestr(200))
          214  +		for i = 0, [hdrs.type.N] do
          215  +			lib.net.mg_printf(self.con, '%s: %s\r\n', hdrs[i].key, hdrs[i].value)
          216  +		end
          217  +		lib.net.mg_printf(self.con, '\r\n')
          218  +	else
          219  +		self:rawpage(200, pg, [lib.mem.ptr(lib.http.header)] {
          220  +			ptr = &hdrs[0], ct = 3
          221  +		})
          222  +	end
          223  +end
          224  +
          225  +terra convo:stdpage(pg: convo.page) self:statpage(200, pg) end
          226  +
   143    227   terra convo:reroute_cookie(dest: rawstring, cookie: rawstring)
   144    228   	var hdrs = array(
   145    229   		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
   146    230   		lib.http.header { key = 'Location',     value = dest },
   147    231   		lib.http.header { key = 'Set-Cookie',   value = cookie }
   148    232   	)
   149    233   
   150    234   	var body = data.view.docskel {
   151    235   		instance = self.srv.cfg.instance.ptr;
   152    236   		title = 'rerouting';
   153    237   		body = 'you are being redirected';
   154    238   		class = 'error';
   155    239   		navlinks = '';
          240  +		attr = '';
   156    241   	}
   157    242   
   158    243   	body:send(self.con, 303, [lib.mem.ptr(lib.http.header)] {
   159    244   		ptr = &hdrs[0], ct = [hdrs.type.N] - lib.trn(cookie == nil,1,0)
   160    245   	})
   161    246   end
   162    247   
................................................................................
   170    255   		lib.dbg('sending cookie ',{&sesskey[0],15})
   171    256   		p = lib.str.ncpy(p, '; Path=/', 9)
   172    257   	end
   173    258   	self:reroute_cookie(dest, &sesskey[0])
   174    259   end
   175    260    
   176    261   terra convo:complain(code: uint16, title: rawstring, msg: rawstring)
   177         -	var hdrs = array(
   178         -		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
   179         -		lib.http.header { key = 'Cache-Control', value = 'no-store' }
   180         -	)
          262  +	if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end
   181    263   
   182    264   	var ti: lib.str.acc ti:compose('error :: ', title)
   183    265   	var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.webp"><h1>',title,'</h1><p>',msg,'</p></div>')
   184         -	var body = data.view.docskel {
   185         -		instance = self.srv.cfg.instance;
          266  +	var body = [convo.page] {
   186    267   		title = ti:finalize();
   187    268   		body = bo:finalize();
   188    269   		class = lib.str.plit 'error';
   189         -		navlinks = lib.coalesce(self.navbar, [lib.mem.ptr(int8)]{ptr='',ct=0});
          270  +		cache = false;
   190    271   	}
   191    272   
   192         -	if body.body.ptr == nil then
   193         -		body.body = lib.str.plit"i'm sorry, dave. i can't let you do that"
   194         -	end
   195         -
   196         -	body:send(self.con, code, [lib.mem.ptr(lib.http.header)] {
   197         -		ptr = &hdrs[0], ct = [hdrs.type.N]
   198         -	})
          273  +	self:statpage(code, body)
   199    274   
   200    275   	body.title:free()
   201    276   	body.body:free()
   202    277   end
   203    278   
   204    279   convo.methods.assertpow = macro(function(self, pow)
   205    280   	return quote
................................................................................
   207    282   		if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then
   208    283   			ok = false
   209    284   			self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action'])
   210    285   		end
   211    286   	in ok end
   212    287   end)
   213    288   
   214         -struct convo.page {
   215         -	title: pstring
   216         -	body: pstring
   217         -	class: pstring
   218         -	cache: bool
   219         -}
   220         -
   221         -terra convo:stdpage(pg: convo.page)
   222         -	var doc = data.view.docskel {
   223         -		instance = self.srv.cfg.instance;
   224         -		title = pg.title;
   225         -		body = pg.body;
   226         -		class = pg.class;
   227         -		navlinks = self.navbar;
   228         -	}
   229         -
   230         -	var hdrs = array(
   231         -		lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
   232         -		lib.http.header { key = 'Cache-Control', value = 'no-store' }
   233         -	)
   234         -
   235         -	doc:send(self.con,200,[lib.mem.ptr(lib.http.header)] {ct = [hdrs.type.N] - lib.trn(pg.cache,1,0), ptr = &hdrs[0]})
   236         -end
   237         -
   238    289   -- CALL ONLY ONCE PER VAR
   239    290   terra convo:postv(name: rawstring)
   240    291   	if self.varbuf.ptr == nil then
   241    292   		self.varbuf = lib.mem.heapa(int8, self.msg.body.len + self.msg.query.len)
   242    293   		self.vbofs = self.varbuf.ptr
   243    294   	end
   244    295   	var o = lib.net.mg_http_get_var(&self.msg.body, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr))
................................................................................
   299    350   		if lib.str.ncmp(mimevar.ptr, mime, lib.math.biggest(mimevar.ct, [#mime])) == 0 then
   300    351   			ret = [lib.http.mime[name]]
   301    352   		else ret = [mimeneg] end
   302    353   	in ret end
   303    354   end
   304    355   
   305    356   local handle = {
   306         -	http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque)
   307         -		var server = [&srv](ext)
          357  +	http = terra(con: &lib.net.mg_connection, event_kind: int, event: &opaque, userdata: &opaque)
          358  +		var server = [&srv](userdata)
   308    359   		var mgpeer = getpeer(con)
   309    360   		var peer = lib.store.inet { port = mgpeer.port; }
   310    361   		if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end
   311    362   		if peer.pv == 6 then
   312    363   			for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end
   313    364   		else -- v4
   314    365   			@[&uint32](&peer.v4) = mgpeer.ip
................................................................................
   319    370   		-- for now i'm leaving it as is, but note that netmask restrictions
   320    371   		-- WILL NOT WORK until upstream gets its shit together. FIXME
   321    372   
   322    373   		-- needs to check for an X-Forwarded-For header from nginx and
   323    374   		-- use that instead of the peer iff peer is ::1/127.1 FIXME
   324    375   		-- maybe also haproxy support?
   325    376   
   326         -		switch event do
          377  +		switch event_kind do
   327    378   			case lib.net.MG_EV_HTTP_MSG then
   328    379   				lib.dbg('routing HTTP request')
   329         -				var msg = [&lib.net.mg_http_message](p)
          380  +				var msg = [&lib.net.mg_http_message](event)
   330    381   				var co = convo {
   331    382   					con = con, srv = server, msg = msg;
   332    383   					aid = 0, aid_issue = 0, who = nil;
   333    384   					reqtype = lib.http.mime.none;
   334         -					peer = peer;
          385  +					peer = peer, live_last = 0;
   335    386   				} co.varbuf.ptr = nil
   336    387   				  co.navbar.ptr = nil
   337    388   				  co.actorcache.top = 0
   338    389   				  co.actorcache.cur = 0
          390  +				  co.ui_hue = server.cfg.ui_hue
   339    391   
   340    392   				-- first, check for an accept header. if it's there, we need to
   341    393   				-- iterate over the values and pick the highest-priority one
   342    394   				do var acc = lib.http.findheader(msg, 'Accept')
   343    395   					-- TODO handle q-value
   344         -					if acc.ptr ~= nil then
          396  +					if acc ~= nil and acc.ptr ~= nil then
   345    397   						var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr }
   346    398   						var i = 0 while i < acc.ct do
   347    399   							if acc.ptr[i] == @',' or acc.ptr[i] == @';' then
   348    400   								mimevar.ct = (acc.ptr+i) - mimevar.ptr
   349    401   								var t = [mimeneg]
   350    402   								if t ~= lib.http.mime.none then
   351    403   									co.reqtype = t
................................................................................
   377    429   					else co.reqtype = lib.http.mime.html end
   378    430   				::foundtype::end
   379    431   
   380    432   				-- we need to check if there's any cookies sent with the request,
   381    433   				-- and if so, whether they contain any credentials. this will be
   382    434   				-- used to set the auth parameters in the http conversation
   383    435   				var cookies_p = lib.http.findheader(msg, 'Cookie')
   384         -				if cookies_p ~= nil then
          436  +				if cookies_p ~= nil and cookies_p.ptr ~= nil then
   385    437   					var cookies = cookies_p.ptr
   386    438   					var key = [lib.mem.ref(int8)] {ptr = cookies, ct = 0}
   387    439   					var val = [lib.mem.ref(int8)] {ptr = nil, ct = 0}
   388    440   					var i = 0 while i < cookies_p.ct    and
   389    441   					                cookies[i] ~= 0     and
   390    442   					                cookies[i] ~= @'\r' and
   391    443   									cookies[i] ~= @'\n' do -- cover all the bases
................................................................................
   423    475   				end
   424    476   
   425    477   				if co.aid ~= 0 then
   426    478   					var sess, usr = co.srv:actor_session_fetch(co.aid, peer, co.aid_issue)
   427    479   					if sess.ok == false then co.aid = 0 co.aid_issue = 0 else
   428    480   						co.who = usr.ptr
   429    481   						co.who.rights.powers = server:actor_powers_fetch(co.who.id)
          482  +						var userhue, hueok = server:actor_conf_int_get(co.who.id, 'ui-accent')
          483  +						if hueok then co.ui_hue = userhue end
   430    484   					end
   431    485   				end
          486  +
          487  +				var livelast_p = lib.http.findheader(msg, 'X-Live-Last-Arrival')
          488  +				if livelast_p ~= nil and livelast_p.ptr ~= nil then
          489  +					var ll, ok = lib.math.decparse(pstring{ptr = livelast_p.ptr, ct = livelast_p.ct - 1})
          490  +					if ok then co.live_last = ll end
          491  +				end
          492  +
   432    493   
   433    494   				var uridec = lib.mem.heapa(int8, msg.uri.len) defer uridec:free()
   434    495   				var urideclen = lib.net.mg_url_decode(msg.uri.ptr, msg.uri.len, uridec.ptr, uridec.ct, 1)
   435    496   
   436    497   				var uri = uridec
   437    498   				if urideclen == -1 then
   438    499   					for i = 0,msg.uri.len do
................................................................................
   442    503   						end
   443    504   					end
   444    505   					uri.ct = msg.uri.len
   445    506   				else uri.ct = urideclen end
   446    507   				lib.dbg('routing URI ', {uri.ptr, uri.ct})
   447    508   				
   448    509   				if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then
          510  +					co.method = [lib.http.method.get]
   449    511   					route.dispatch_http(&co, uri, [lib.http.method.get])
   450    512   				elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then
          513  +					co.method = [lib.http.method.get]
   451    514   					route.dispatch_http(&co, uri, [lib.http.method.post])
   452    515   				elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then
          516  +					co.method = [lib.http.method.head]
   453    517   					route.dispatch_http(&co, uri, [lib.http.method.head])
   454    518   				elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then
          519  +					co.method = [lib.http.method.options]
   455    520   					route.dispatch_http(&co, uri, [lib.http.method.options])
   456    521   				else
   457    522   					co:complain(400,'unknown method','you have submitted an invalid http request')
   458    523   				end
   459    524   
   460    525   				if co.aid ~= 0 then lib.mem.heapf(co.who) end
   461    526   				if co.varbuf.ptr ~= nil then co.varbuf:free() end
................................................................................
   670    735   	do self.pol_reg = false
   671    736   	var sreg = self.overlord:conf_get('policy-self-register')
   672    737   	if sreg:ref() then
   673    738   		if lib.str.cmp(sreg.ptr, 'on') == 0
   674    739   			then self.pol_reg = true
   675    740   			else self.pol_reg = false
   676    741   		end
   677         -	end
   678         -	sreg:free() end
          742  +		sreg:free()
          743  +	end end
   679    744   
   680    745   	do self.credmgd = false
   681    746   	var sreg = self.overlord:conf_get('credential-store')
   682    747   	if sreg:ref() then
   683    748   		if lib.str.cmp(sreg.ptr, 'managed') == 0
   684    749   			then self.credmgd = true
   685    750   			else self.credmgd = false
   686    751   		end
   687         -	end
   688         -	sreg:free() end
          752  +		sreg:free()
          753  +	end end
   689    754   
   690    755   	do self.maxupsz = [1024 * 100] -- 100 kilobyte default
   691    756   	var sreg = self.overlord:conf_get('maximum-artifact-size')
   692    757   	if sreg:ref() then
   693    758   		var sz, ok = lib.math.fsz_parse(sreg)
   694    759   		if ok then self.maxupsz = sz else
   695    760   			lib.warn('invalid configuration value for maximum-artifact-size; keeping default 100K upload limit')
   696    761   		end
          762  +		sreg:free() end
   697    763   	end
   698         -	sreg:free() end
   699    764   	
   700    765   	self.pol_sec = secmode.lockdown
   701    766   	var smode = self.overlord:conf_get('policy-security')
   702    767   	if smode.ptr ~= nil then
   703    768   		if lib.str.cmp(smode.ptr, 'public') == 0 then
   704    769   			self.pol_sec = secmode.public
   705    770   		elseif lib.str.cmp(smode.ptr, 'private') == 0 then
   706    771   			self.pol_sec = secmode.private
   707    772   		elseif lib.str.cmp(smode.ptr, 'lockdown') == 0 then
   708    773   			self.pol_sec = secmode.lockdown
   709    774   		elseif lib.str.cmp(smode.ptr, 'isolate') == 0 then
   710    775   			self.pol_sec = secmode.isolate
   711    776   		end
          777  +		smode:free()
   712    778   	end
   713         -	smode:free()
          779  +
          780  +	self.ui_hue = config.default_ui_accent
          781  +	var shue = self.overlord:conf_get('ui-accent')
          782  +	if shue.ptr ~= nil then
          783  +		var hue,ok = lib.math.decparse(shue)
          784  +		if ok then self.ui_hue = hue end
          785  +		shue:free()
          786  +	end
   714    787   end
   715    788   
   716    789   return {
   717    790   	overlord = srv;
   718    791   	convo = convo;
   719    792   	route = route;
   720    793   	secmode = secmode;
   721    794   }