parsav  Diff

Differences From Artifact [557555fd0b]:

To Artifact [68c9cc33d4]:


     1      1   -- vim: ft=terra
     2      2   local util = lib.util
     3      3   local secmode = lib.enum { 'public', 'private', 'lockdown', 'isolate' }
     4      4   local pstring = lib.mem.ptr(int8)
            5  +local mimetypes = {
            6  +	{'html', 'text/html'};
            7  +	{'json', 'application/json'};
            8  +	{'json', 'application/activity+json'};
            9  +	{'json', 'application/ld+json'};
           10  +	{'mkdown', 'text/markdown'};
           11  +	{'text', 'text/plain'};
           12  +	{'ansi', 'text/x-ansi'};
           13  +}
           14  +
     5     15   local struct srv
     6     16   local struct cfgcache {
     7     17   	secret: pstring
     8     18   	pol_sec: secmode.t
     9     19   	pol_reg: bool
    10     20   	pol_autoherald: bool
    11     21   	credmgd: bool
................................................................................
   178    188   
   179    189   local usrdefs = {
   180    190   	str = {
   181    191   		['acl-follow'    ] = {cfgfld = 'usrdef_pol_follow', fallback = 'local'};
   182    192   		['acl-follow-req'] = {cfgfld = 'usrdef_pol_follow_req', fallback = 'all'};
   183    193   	};
   184    194   }
          195  +
          196  +terra convo:matchmime(mime: lib.http.mime.t): bool
          197  +	return self.reqtype == [lib.http.mime.none]
          198  +		or self.reqtype == mime
          199  +end
   185    200   
   186    201   terra convo:usercfg_str(uid: uint64, setting: pstring): pstring
   187    202   	var set = self.srv:actor_conf_str_get(&self.srv.pool, uid, setting)
   188    203   	if not set then
   189    204   		[(function()
   190    205   			local q = quote return pstring.null() end
   191    206   			for key, dfl in pairs(usrdefs.str) do
................................................................................
   316    331   	if not lockdown then lockhdr = "" end
   317    332   	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, lockhdr)
   318    333   	lib.net.mg_send(self.con, data.ptr, data.ct)
   319    334   	lib.net.mg_send(self.con, '\r\n', 2)
   320    335   end
   321    336   
   322    337   terra convo:json(data: pstring)
   323         -	self:bytestream_trusted(false, 'application/ld+json', data:blob())
          338  +	self:bytestream_trusted(false, 'application/activity+json; charset=utf-8', data:blob())
   324    339   end
   325    340   
   326    341   terra convo:bytestream(mime: pstring, data: lib.mem.ptr(uint8))
   327    342   	-- TODO this is not a satisfactory solution; it's a bandaid on a gaping
   328    343   	-- chest wound. ultimately we need to compile a whitelist of safe mime
   329    344   	-- types as part of mimelib, but that is no small task. for now, this
   330    345   	-- will keep the patient from immediately bleeding out
................................................................................
   370    385   		p = p + lib.session.cookie_gen(self.srv.cfg.secret, aid, lib.osclock.time(nil), p)
   371    386   		lib.dbg('sending cookie ',{&sesskey[0],15})
   372    387   		p = lib.str.ncpy(p, '; Path=/', 9)
   373    388   	end
   374    389   	self:reroute_cookie(dest, &sesskey[0])
   375    390   end
   376    391    
          392  +terra convo:stra(sz: intptr) -- convenience function
          393  +	var s: lib.str.acc
          394  +	s:pool(&self.srv.pool,sz)
          395  +	return s
          396  +end
          397  +
          398  +convo.methods.qstr = macro(function(self, ...) -- convenience string builder
          399  +	local exp = {...}
          400  +	return `lib.str.acc{}:pcompose(&self.srv.pool, [exp]):finalize()
          401  +end)
          402  +
   377    403   terra convo:complain(code: uint16, title: rawstring, msg: rawstring)
   378    404   	if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end
   379    405   
   380         -	var ti: lib.str.acc ti:compose('error :: ', title)
   381         -	var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>')
   382         -	var body = [convo.page] {
   383         -		title = ti:finalize();
   384         -		body = bo:finalize();
   385         -		class = 'error';
   386         -		cache = false;
   387         -	}
   388         -
   389         -	self:statpage(code, body)
   390         -
   391         -	body.title:free()
   392         -	body.body:free()
          406  +	if self:matchmime(lib.http.mime.html) then
          407  +		var body = [convo.page] {
          408  +			title = self:qstr('error :: ', title);
          409  +			body = self:qstr('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>');
          410  +			class = 'error';
          411  +			cache = false;
          412  +		}
          413  +
          414  +		self:statpage(code, body)
          415  +	else
          416  +		var pg = lib.http.page { respcode = code, body = pstring.null() }
          417  +		var ctt = lib.http.mime.none
          418  +		if self:matchmime(lib.http.mime.json) then ctt = lib.http.mime.json
          419  +			pg.body = ([lib.tpl.mk'{"_parsav_error":@$ekind, "_parsav_error_desc":@$edesc}']
          420  +				{ekind = title, edesc = msg}):poolstr(&self.srv.pool)
          421  +		elseif self:matchmime(lib.http.mime.text) then ctt = lib.http.mime.text
          422  +			pg.body = self:qstr('error: ',title,'\n',msg)
          423  +		elseif self:matchmime(lib.http.mime.mkdown) then ctt = lib.http.mime.mkdown
          424  +			pg.body = self:qstr('# error :: ',title,'\n\n',msg)
          425  +		elseif self:matchmime(lib.http.mime.ansi) then ctt = lib.http.mime.ansi
          426  +			pg.body = self:qstr('\27[1;31merror :: ',title,'\27[m\n',msg)
          427  +		end
          428  +		var cthdr = lib.http.header { 'Content-Type', 'text/plain' }
          429  +		if ctt == lib.http.mime.none then
          430  +			pg.headers.ct = 0
          431  +		else
          432  +			pg.headers = lib.typeof(pg.headers) { &cthdr, 1 }
          433  +			switch ctt do
          434  +				case [ctt.type](lib.http.mime.json) then
          435  +					cthdr.value = 'application/json'
          436  +				end
          437  +				escape
          438  +					for i,v in ipairs(mimetypes) do local key,mime = v[1],v[2]
          439  +						if key ~= 'json' then
          440  +							emit quote case [ctt.type](lib.http.mime.[key]) then cthdr.value = [mime] end end
          441  +						end
          442  +					end
          443  +				end
          444  +			end
          445  +		end
          446  +		pg:send(self.con)
          447  +	end
          448  +end
          449  +
          450  +terra convo:fail(code: uint16)
          451  +	switch code do
          452  +		escape
          453  +			local stderrors = {
          454  +				{400, 'bad request', "the action you have attempted on this resource is not meaningful"};
          455  +				{401, 'unauthorized', "this resource is not available at your clearance level"};
          456  +				{403, 'forbidden', "we can neither confirm nor deny the existence of this resource"};
          457  +				{404, 'resource not found', "that resource is not extant on or known to this server"};
          458  +				{405, 'method not allowed', "the method you have attempted on this resource is not meaningful"};
          459  +				{406, 'not acceptable', "none of the suggested content types are a viable representation of this resource"};
          460  +				{500, 'internal server error', "parsav did a fucksy wucksy"};
          461  +			}
          462  +
          463  +			for i,v in ipairs(stderrors) do
          464  +				emit quote case uint16([v[1]]) then
          465  +					self:complain([v])
          466  +				end end
          467  +			end
          468  +		end
          469  +		else self:complain(500,'unknown error','an unrecognized error was thrown. this is a bug')
          470  +	end
   393    471   end
   394    472   
   395    473   terra convo:confirm(title: pstring, msg: pstring, cancel: pstring)
   396    474   	var conf = data.view.confirm {
   397    475   		title = title;
   398    476   		query = msg;
   399    477   		cancel = cancel;
................................................................................
   405    483   		class = 'query';
   406    484   		body = body; cache = false;
   407    485   	}
   408    486   	self:stdpage(cf)
   409    487   	--cf.title:free()
   410    488   end
   411    489   
   412         -terra convo:stra(sz: intptr) -- convenience function
   413         -	var s: lib.str.acc
   414         -	s:pool(&self.srv.pool,sz)
   415         -	return s
   416         -end
   417         -
   418         -convo.methods.qstr = macro(function(self, ...) -- convenience string builder
   419         -	local exp = {...}
   420         -	return `lib.str.acc{}:pcompose(&self.srv.pool, [exp]):finalize()
   421         -end)
   422         -
   423    490   convo.methods.assertpow = macro(function(self, pow)
   424    491   	return quote
   425    492   		var ok = true
   426    493   		if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then
   427    494   			ok = false
   428    495   			self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action'])
   429    496   		end
................................................................................
   504    571   end
   505    572   terra convo:pgetv(name: rawstring)
   506    573   	var s,l = self:getv(name)
   507    574   	return pstring { ptr = s, ct = l }
   508    575   end
   509    576   
   510    577   local route = {} -- these are defined in route.t, as they need access to renderers
   511         -terra route.dispatch_http ::  {&convo, lib.mem.ptr(int8), lib.http.method.t} -> {}
   512         -
   513         -local mimetypes = {
   514         -	{'html', 'text/html'};
   515         -	{'json', 'application/json'};
   516         -	{'json', 'application/ld+json'};
   517         -	{'json', 'application/activity+json'};
   518         -	{'mkdown', 'text/markdown'};
   519         -	{'text', 'text/plain'};
   520         -	{'ansi', 'text/x-ansi'};
   521         -}
          578  +terra route.dispatch_http ::  {&convo, lib.mem.ptr(int8)} -> {}
   522    579   
   523    580   local mimevar = symbol(lib.mem.ref(int8))
   524    581   local mimeneg = `lib.http.mime.none
   525    582   
   526    583   for i, t in ipairs(mimetypes) do
   527    584   	local name, mime = t[1], t[2]
   528    585   	mimeneg = quote
................................................................................
   821    878   							end
   822    879   							bsr:free()
   823    880   							upmap:free()
   824    881   						end
   825    882   					end
   826    883   				end
   827    884   
   828         -				route.dispatch_http(&co, uri, co.method)
          885  +				route.dispatch_http(&co, uri)
   829    886   
   830    887   				::fail::
   831    888   				if co.uploads.run > 0 then
   832    889   					for i=0,co.uploads.sz do
   833    890   						co.uploads(i).filename:free()
   834    891   						co.uploads(i).field:free()
   835    892   					end