parsav  Check-in [64ae6724c2]

Overview
Comment:enable webfinger
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 64ae6724c26d73c349c71d534d955bb90ac06c4813e562d990a07a853df8bc91
User & Date: lexi on 2021-01-24 23:18:29
Other Links: manifest | tags
Context
2021-01-25
12:40
first steps towards litepub support check-in: 26937ca853 user: lexi tags: trunk
2021-01-24
23:18
enable webfinger check-in: 64ae6724c2 user: lexi tags: trunk
2021-01-22
17:22
improve compose icon check-in: db0e155b9d user: lexi tags: trunk
Changes

Modified common.lua from [c868f8cd22] to [31fe76fcd7].

    20     20   	if val then return os.execute(cmd) else
    21     21   		local fd = io.popen(cmd,'r')
    22     22   		local t = fd:read('*a')
    23     23   		return chomp(t), fd:close()
    24     24   	end
    25     25   end
    26     26   
    27         -local function dump(v,pfx,cyc)
           27  +local function copy(a)
           28  +	local new = {}
           29  +	for k,v in pairs(a) do new[k] = v end
           30  +	return new
           31  +end
           32  +
           33  +local function cat(a,b)
           34  +	a = copy(a)
           35  +	local ofs = #a
           36  +	for k,v in pairs(b) do
           37  +		if type(k) == 'number' then
           38  +			a[k+ofs] = v
           39  +		else a[k] = v end
           40  +	end
           41  +	return a
           42  +end
           43  +
           44  +local function search(tbl,pred,lst,path)
           45  +	lst = lst or {} path = path or {}
           46  +	if type(pred) ~= 'function' then
           47  +		local val = pred
           48  +		pred = function(a,k)
           49  +			if type(a) == 'table' and a ~= val then return end
           50  +			return a == val
           51  +		end
           52  +	end
           53  +	for k,v in pairs(tbl) do
           54  +		local res = pred(v,k)
           55  +		local np = cat(path, {tbl})
           56  +		if res == true then
           57  +			table.insert(lst, {
           58  +				key = k;
           59  +				value = v;
           60  +				parent = tbl;
           61  +				path = np;
           62  +			})
           63  +		elseif res == nil then
           64  +			search(v,pred,lst,np)
           65  +		end
           66  +	end
           67  +	return lst
           68  +end
           69  +
           70  +local function dump(v,pfx,cyc,ismeta)
    28     71   	pfx = pfx or ''
    29     72   	cyc = cyc or {}
    30     73   	local np = pfx .. '  '
    31     74   
    32     75   	if type(v) == 'table' then
    33     76   		if cyc[v] then return '<...>' else cyc[v] = true end
    34     77   	end
................................................................................
    37     80   		return string.format('%q', v)
    38     81   	elseif type(v) == 'table' then
    39     82   		local str = ''
    40     83   		for k,v in pairs(v) do
    41     84   			local tkey, tval = dump(k,np,cyc), dump(v,np,cyc)
    42     85   			str = str .. string.format('%s[%s] = %s\n', np, tkey,tval)
    43     86   		end
    44         -		return '{\n' .. str .. pfx .. '}\n'
           87  +		local meta = ''
           88  +		if getmetatable(v) then
           89  +			meta = dump(getmetatable(v),pfx,cyc,true) .. '::'
           90  +		end
           91  +		if ismeta then
           92  +			return string.format('%s<|\n%s%s|>',meta,str,pfx)
           93  +		else
           94  +			return meta..'{\n' .. str .. pfx .. '}\n'
           95  +		end
    45     96   	else
    46     97   		return string.format('%s', v)
    47     98   	end
    48     99   end
          100  +
    49    101   local ping = function(path)
    50    102   	local f = io.open(path)
    51    103   	if f then f:close() return true end
    52    104   	return false
    53    105   end
    54    106   local tobool = function(s)
    55    107   	if s == true then return true
................................................................................
    68    120   	local seed = 1 for i = 1, 8 do
    69    121   		seed = seed * string.byte(string.sub(ent,i,i))
    70    122   	end
    71    123   	math.randomseed(seed)
    72    124   else math.randomseed(os.time()) end
    73    125   
    74    126   return {
    75         -	exec = exec;
    76    127   	dump = dump;
    77         -	ping = ping;
    78         -	chomp = chomp;
    79         -	map = map;
    80         -	tobool = tobool;
          128  +	exec = exec, ping = ping;
          129  +	map = map, copy = copy, cat = cat, search = search;
          130  +	chomp = chomp, tobool = tobool;
    81    131   	find = function(lst,pred)
    82    132   		for k,v in pairs(lst) do
    83    133   			local test = pred(v,k) 
    84    134   			if test then return test end
    85    135   		end
    86    136   		return nil
    87    137   	end;
................................................................................
    94    144   			s = s .. string.char(ofs) 
    95    145   		end
    96    146   		return s
    97    147   	end;
    98    148   	append = function(a,b)
    99    149   		for _, v in pairs(b) do a[#a+1] = v end
   100    150   	end;
   101         -	has = function(haystack,needle,eq)
   102         -		eq = eq or function(a,b) return a == b end
          151  +	has = function(haystack,pred)
          152  +		if type(pred) ~= 'function' then
          153  +			local val = pred
          154  +			pred = function(a) return a == val end
          155  +		end
   103    156   		for k,v in pairs(haystack) do
   104         -			if eq(needle,v) then return k end
          157  +			if pred(v,k) then return k,v end
   105    158   		end
   106    159   	end;
   107    160   	keys = function(ary)
   108    161   		local kt = {}
   109    162   		for k,v in pairs(ary) do kt[#kt+1] = k end
   110    163   		return kt
   111    164   	end;

Modified mem.t from [8c252399a5] to [d10508934e].

   106    106   		self.ct = self.ct - n
   107    107   		return self.ptr
   108    108   	end
   109    109   	terra t.methods.null(): t return t { ptr = nil, ct = 0 } end -- maybe should be a macro?
   110    110   	terra t:ref() return self.ptr ~= nil end
   111    111   	t.metamethods.__not = macro(function(self) return `not self:ref() end)
   112    112   	t.metamethods.__apply = macro(function(self,idx) return `self.ptr[ [idx or 0] ] end)
          113  +	t.metamethods.__add = terra(self: &t, sz: intptr): t
          114  +		var n = @self n:advance(sz) return n
          115  +	end
   113    116   	t.metamethods.__update = macro(function(self,idx,rhs)
   114    117   		return quote self.ptr[idx] = rhs end end)
   115    118   	t.metamethods.__cast = function(from,to,exp)
   116    119   		if to == t then
   117    120   			if from == niltype then return `t.null()
   118    121   			elseif from == &ty then return `t {ptr = exp, ct = 1}
   119    122   			elseif from == ty then return `t {ptr = &exp, ct = 1}

Modified route.t from [8f0d6bf9b0] to [325df84c8e].

   923    923   	return end
   924    924   
   925    925   	::e404:: do co:complain(404, 'artifact not found', 'no such artifact has been uploaded to this instance') return end
   926    926   end
   927    927   
   928    928   local json = {}
   929    929   
   930         -terra json.webfinger(co: &lib.srv.convo)
   931         -	
          930  +do wftpl = lib.tpl.mk [[{
          931  +		"subject": @$subj,
          932  +		"links": [
          933  +			{ "rel": "self", "type": "application/ld+json", "href": @$href }
          934  +		]
          935  +	}]]
          936  +	terra json.webfinger(co: &lib.srv.convo)
          937  +		var res = co:pgetv('resource')
          938  +		if (not res) or not res:startswith 'acct:' then goto err end
          939  +		
          940  +		-- technically we should look this user up in the database to make sure
          941  +		-- they actually exist, buuut that's costly and i doubt that's actually
          942  +		-- necessary for webfinger to do its job. so we cheat and just do string
          943  +		-- munging so lookups are as cheap as possible. TODO make sure this works
          944  +		-- in practice and doesn't cause any weird security problems
          945  +		var acct = res + 5
          946  +		var svp = lib.str.find(acct, '@')
          947  +		if svp:ref() then
          948  +			acct.ct = (svp.ptr - acct.ptr)
          949  +			svp:advance(1)
          950  +			if not svp:cmp(co.srv.cfg.domain) then goto err end
          951  +		end
          952  +		var tp = wftpl {
          953  +			subj = res;
          954  +			href = co:qstr('https://', co.srv.cfg.domain, '/@', acct);
          955  +		}
          956  +		co:json(tp:poolstr(&co.srv.pool))
          957  +
          958  +		do return end -- error conditions
          959  +		::err:: do co:json('{}') return end
          960  +	end
   932    961   end
   933    962   
   934    963   -- entry points
   935    964   terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t)
   936    965   	lib.dbg('handling URI of form ', {uri.ptr,uri.ct})
   937    966   	co.navbar = lib.render.nav(co)
   938    967   	-- some routes are non-hierarchical, and can be resolved with a simple strcmp

Modified srv.t from [c8974a3f52] to [557555fd0b].

     8      8   	pol_sec: secmode.t
     9      9   	pol_reg: bool
    10     10   	pol_autoherald: bool
    11     11   	credmgd: bool
    12     12   	maxupsz: intptr
    13     13   	poolinitsz: intptr
    14     14   	instance: pstring
           15  +	domain: pstring
    15     16   	overlord: &srv
    16     17   	ui_cue_staff: pstring
    17     18   	ui_cue_founder: pstring
    18     19   	ui_hue: uint16
    19     20   	nranks: uint16
    20     21   	maxinvites: uint16
    21     22   	master: uint64
................................................................................
    28     29   	webmgr: lib.net.mg_mgr
    29     30   	webcon: &lib.net.mg_connection
    30     31   	cfg: cfgcache
    31     32   	id: rawstring
    32     33   	pool: lib.mem.pool
    33     34   }
    34     35   
    35         -terra cfgcache:free() -- :/
           36  +terra cfgcache:free() -- :/ TODO replace with pool
    36     37   	self.secret:free()
    37     38   	self.instance:free()
           39  +	self.domain:free()
    38     40   	self.ui_cue_staff:free()
    39     41   	self.ui_cue_founder:free()
    40     42   	self.usrdef_pol_follow:free()
    41     43   	self.usrdef_pol_follow_req:free()
    42     44   end
    43     45   
    44     46   terra srv:post_enum_author_uid(uid: uint64, r: lib.store.range): lib.mem.vec(lib.mem.ptr(lib.store.post))
................................................................................
   507    509   
   508    510   local route = {} -- these are defined in route.t, as they need access to renderers
   509    511   terra route.dispatch_http ::  {&convo, lib.mem.ptr(int8), lib.http.method.t} -> {}
   510    512   
   511    513   local mimetypes = {
   512    514   	{'html', 'text/html'};
   513    515   	{'json', 'application/json'};
          516  +	{'json', 'application/ld+json'};
          517  +	{'json', 'application/activity+json'};
   514    518   	{'mkdown', 'text/markdown'};
   515    519   	{'text', 'text/plain'};
   516    520   	{'ansi', 'text/x-ansi'};
   517    521   }
   518    522   
   519    523   local mimevar = symbol(lib.mem.ref(int8))
   520    524   local mimeneg = `lib.http.mime.none
................................................................................
   529    533   	in ret end
   530    534   end
   531    535   
   532    536   local handle = {
   533    537   	http = terra(con: &lib.net.mg_connection, event_kind: int, event: &opaque, userdata: &opaque)
   534    538   		var server = [&srv](userdata)
   535    539   		var mgpeer = getpeer(con)
   536         -		var peer = lib.store.inet { port = mgpeer.port; }
   537         -		if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end
   538         -		if peer.pv == 6 then
   539         -			for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end
   540         -		else -- v4
   541         -			@[&uint32](&peer.v4) = mgpeer.ip
   542         -		end
          540  +		-- var pbuf: int8[128]
          541  +
   543    542   		-- the peer property is currently broken and there is precious
   544    543   		-- little i can do about this -- it always reports a peer v4 IP
   545         -		-- of 0.0.0.0, altho the port seems to come through correctly.
   546         -		-- for now i'm leaving it as is, but note that netmask restrictions
   547         -		-- WILL NOT WORK until upstream gets its shit together. FIXME
          544  +		-- of 0.0.0.0 for v6 connections, altho the port seems to come
          545  +		-- through correctly.  -- for now i'm leaving it as is, but note
          546  +		-- that netmask restrictions WILL NOT WORK until upstream gets
          547  +		-- its shit together. FIXME
   548    548   
   549    549   		-- needs to check for an X-Forwarded-For header from nginx and
   550    550   		-- use that instead of the peer iff peer is ::1/127.1 FIXME
   551    551   		-- maybe also haproxy support?
   552    552   
   553    553   		switch event_kind do
   554    554   			case lib.net.MG_EV_HTTP_MSG then
          555  +				-- lib.net.mg_ntoa(&mgpeer,&pbuf[0],127)
          556  +				-- lib.dbg('got connection from client ',&pbuf[0])
          557  +				var peer = lib.store.inet { port = mgpeer.port; }
          558  +				if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end
          559  +				if peer.pv == 6 then
          560  +					for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end
          561  +				else -- v4
          562  +					@[&uint32](&peer.v4) = mgpeer.ip
          563  +				end
          564  +
   555    565   				lib.dbg('routing HTTP request')
   556    566   				var msg = [&lib.net.mg_http_message](event)
   557    567   				var co = convo {
   558    568   					con = con, srv = server, msg = msg;
   559    569   					aid = 0, aid_issue = 0, who = nil;
   560    570   					reqtype = lib.http.mime.none;
   561    571   					peer = peer, live_last = 0;
................................................................................
  1110   1120   		str:free()
  1111   1121   	end
  1112   1122   	return default
  1113   1123   end
  1114   1124   
  1115   1125   terra cfgcache:load()
  1116   1126   	self.instance = self.overlord:conf_get('instance-name')
         1127  +	self.domain = self.overlord:conf_get('domain')
  1117   1128   	self.secret = self.overlord:conf_get('server-secret')
  1118   1129   
  1119   1130   	self.pol_reg = self:cfbool('policy-self-register', false)
  1120   1131   	self.pol_autoherald = self:cfbool('policy-self-herald', true)
  1121   1132   
  1122   1133   	do self.credmgd = false
  1123   1134   	var sreg = self.overlord:conf_get('credential-store')

Modified str.t from [0c9ba1d780] to [6c699847d5].

    59     59   
    60     60   			var sz = lib.math.biggest(self.ct, other.ct)
    61     61   			for i = 0, sz do
    62     62   				if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end
    63     63   				if self.ptr[i] ~= other.ptr[i] then return false end
    64     64   			end
    65     65   			return true
           66  +		end
           67  +		terra ty:startswith(other: ty)
           68  +			for i=0, other.ct do
           69  +				if other(i) == 0 then return true end
           70  +				if other(i) ~= self(i) then return false end
           71  +			end
           72  +			return true
    66     73   		end
    67     74   		terra ty:ffw()
    68     75   			var newp = m.ffw(self.ptr,self.ct)
    69     76   			var newct = self.ct - (newp - self.ptr)
    70     77   			return ty { ptr = newp, ct = newct }
    71     78   		end
    72     79   		terra ty:blob()
................................................................................
   559    566   	var cur = str while cur.ct > 0 do
   560    567   		var add, cont = disemvowel_codepoint(cur)
   561    568   		if add:ref() then acc:ppush(add) end
   562    569   		cur = cont
   563    570   	end
   564    571   	return acc:finalize()
   565    572   end
          573  +
          574  +terra m.qesc(pool: &lib.mem.pool, str: m.t): m.t
          575  + -- escape double-quotes
          576  +	var a: m.acc a:pool(pool, str.ct + str.ct/2)
          577  +	a:lpush '"'
          578  +	for i=0, str.ct do
          579  +		if     str(i) == @'"'  then a:lpush '\\"'
          580  +		elseif str(i) == @'\\' then a:lpush '\\\\'
          581  +		elseif str(i) < 0x20 then -- for json
          582  +			var hex = lib.math.hexbyte(str(i))
          583  +			a:lpush('\\u00'):push(&hex[0], 2)
          584  +		else   a:push(str.ptr + i,1) end
          585  +	end
          586  +	a:lpush '"'
          587  +	return a:finalize()
          588  +end
   566    589   
   567    590   return m

Modified tpl.t from [7216746946] to [ce74a1083d].

    34     34   	str = str:gsub('%s+[\n$]','')
    35     35   	str = str:gsub('\n','')
    36     36   	str = str:gsub('</a><a ','</a> <a ') -- keep nav links from getting smooshed
    37     37   	str = str:gsub(tplchar .. '%?([-%w]+)', function(file)
    38     38   		if not docs[file] then docs[file] = data.doc[file] end
    39     39   		return string.format('<a href="#help-%s" class="help">?</a>', file)
    40     40   	end)
    41         -	for start, mode, key, stop in string.gmatch(str,'()'..tplchar..'([:!]?)([-a-zA-Z0-9_]+)()') do
           41  +	for start, mode, key, stop in string.gmatch(str,'()'..tplchar..'([:!$]?)([-a-zA-Z0-9_]+)()') do
    42     42   		if string.sub(str,start-1,start-1) ~= '\\' then
    43     43   			segs[#segs+1] = string.sub(str,last,start-1)
    44     44   			fields[#segs] = { key = key:gsub('-','_'), mode = (mode ~= '' and mode or nil) }
    45     45   			last = stop
    46     46   		end
    47     47   	end
    48     48   	segs[#segs+1] = string.sub(str,last)
................................................................................
    73     73   			if not kfac[fld.key] then
    74     74   				rec.entries[#rec.entries + 1] = {
    75     75   					field = fld.key;
    76     76   					type = lib.mem.ptr(int8);
    77     77   				}
    78     78   			end
    79     79   			kfac[fld.key] = (kfac[fld.key] or 0) + 1
    80         -			sanmode[fld.key] = fld.mode == ':' and 6 or fld.mode == '!' and 5 or 1
           80  +			sanmode[fld.key] = fld.mode == ':' and 6
           81  +				or fld.mode == '!' and 5
           82  +				or fld.mode == '$' and 2 or 1
    81     83   		end
    82     84   		for key, fac in pairs(kfac) do
    83     85   			local sanfac = sanmode[key]
    84     86   			
    85     87   			tallyup[#tallyup + 1] = quote
    86     88   				[runningtally] = [runningtally] + ([symself].[key].ct)*fac*sanfac
    87     89   			end
................................................................................
    99    101   	for idx, seg in ipairs(segs) do
   100    102   		copiers[#copiers+1] = quote [cpypos] = lib.mem.cpy([cpypos], [&opaque]([seg]), [#seg]) end
   101    103   		senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
   102    104   		appenders[#appenders+1] = quote [accumulator]:push([seg], [#seg]) end
   103    105   		if fields[idx] and fields[idx].mode then
   104    106   			local f = fields[idx]
   105    107   			local fp = `symself.[f.key]
          108  +			local sanexp
          109  +			if f.mode == '$' then
          110  +				sanexp = `lib.str.qesc(pool, fp)
          111  +			else
          112  +				sanexp = `lib.html.sanitize(pool, fp, [f.mode == ':'])
          113  +			end
   106    114   			copiers[#copiers+1] = quote 
   107    115   				if fp.ct > 0 then
   108         -					var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
          116  +					var san = sanexp
   109    117   					[cpypos] = lib.mem.cpy([cpypos], [&opaque](san.ptr), san.ct)
   110    118   					--san:free()
   111    119   				end
   112    120   			end
   113    121   			senders[#senders+1] = quote
   114    122   				if fp.ct > 0 then
   115         -					var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
          123  +					var san = sanexp
   116    124   					lib.net.mg_send([destcon], san.ptr, san.ct)
   117    125   					--san:free()
   118    126   				end
   119    127   			end
   120    128   			appenders[#appenders+1] = quote
   121    129   				if fp.ct > 0 then
   122         -					var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
          130  +					var san = sanexp
   123    131   					[accumulator]:ppush(san)
   124    132   					--san:free()
   125    133   				end
   126    134   			end
   127    135   		elseif fields[idx] then
   128    136   			local f = fields[idx]
   129    137   			local fp = `symself.[f.key]