parsav  Check-in [25e05466d5]

Overview
Comment:continued iteration
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 25e05466d5017e26f9260db3969473dcffc39fa3739edd1cc7abfa41baff5115
User & Date: lexi on 2020-12-21 01:08:01
Other Links: manifest | tags
Context
2020-12-22
23:01
milestone check-in: 419d1a1ebe user: lexi tags: trunk
2020-12-21
01:08
continued iteration check-in: 25e05466d5 user: lexi tags: trunk
2020-12-16
08:46
add nix build file check-in: df4ae251ef user: lexi tags: trunk
Changes

Modified backend/pgsql.t from [0360541ecf] to [2e402a5b93].

    31     31   				where id = $1::bigint
    32     32   		]];
    33     33   	};
    34     34   
    35     35   	actor_fetch_xid = {
    36     36   		params = {rawstring}, sql = [[
    37     37   			select a.id, a.nym, a.handle, a.origin,
    38         -			       a.bio, a.rank, a.quota, a.key,
           38  +			       a.bio, a.rank, a.quota, a.key, $1::text,
    39     39   
    40     40   				coalesce(s.domain,
    41     41   				        (select value from parsav_config
    42     42   							where key='domain' limit 1)) as domain
    43     43   
    44     44   			from      parsav_actors  as a
    45     45   			left join parsav_servers as s
................................................................................
    46     46   				on a.origin = s.id
    47     47   
    48     48   			where $1::text = (a.handle || '@' || domain) or
    49     49   			      $1::text = ('@' || a.handle || '@' || domain) or
    50     50   				  (a.origin is null and $1::text = ('@' || a.handle))
    51     51   		]];
    52     52   	};
           53  +
           54  +	actor_enum_local = {
           55  +		params = {}, sql = [[
           56  +			select id, nym, handle, origin,
           57  +			       bio, rank, quota, key,
           58  +				handle ||'@'||
           59  +				(select value from parsav_config
           60  +					where key='domain' limit 1) as xid
           61  +			from parsav_actors where origin is null
           62  +		]];
           63  +	};
           64  +
           65  +	actor_enum = {
           66  +		params = {}, sql = [[
           67  +			select a.id, a.nym, a.handle, a.origin,
           68  +			       a.bio, a.rank, a.quota, a.key,
           69  +				a.handle ||'@'||
           70  +				coalesce(s.domain,
           71  +				        (select value from parsav_config
           72  +							where key='domain' limit 1)) as xid
           73  +			from parsav_actors a
           74  +			left join parsav_servers s on s.id = a.origin
           75  +		]];
           76  +	};
    53     77   }
    54     78   
    55     79   local struct pqr {
    56     80   	sz: intptr
    57     81   	res: &lib.pq.PGresult
    58     82   }
    59     83   terra pqr:free() if self.sz > 0 then lib.pq.PQclear(self.res) end end
    60     84   terra pqr:null(row: intptr, col: intptr)
    61     85   	return (lib.pq.PQgetisnull(self.res, row, col) == 1)
    62     86   end
    63         -terra pqr:string(row: intptr, col: intptr)
           87  +terra pqr:len(row: intptr, col: intptr)
           88  +	return lib.pq.PQgetlength(self.res, row, col)
           89  +end
           90  +terra pqr:cols() return lib.pq.PQnfields(self.res) end
           91  +terra pqr:string(row: intptr, col: intptr) -- not to be exported!!
           92  +	var v = lib.pq.PQgetvalue(self.res, row, col)
           93  +--	var r: lib.mem.ptr(int8)
           94  +--	r.ct = lib.str.sz(v)
           95  +--	r.ptr = v
           96  +	return v
           97  +end
           98  +terra pqr:bin(row: intptr, col: intptr) -- not to be exported!! DO NOT FREE
           99  +	return [lib.mem.ptr(uint8)] {
          100  +		ptr = [&uint8](lib.pq.PQgetvalue(self.res, row, col));
          101  +		ct = lib.pq.PQgetlength(self.res, row, col);
          102  +	}
          103  +end
          104  +terra pqr:String(row: intptr, col: intptr) -- suitable to be exported
          105  +	var s = [lib.mem.ptr(int8)] { ptr = lib.str.dup(self:string(row,col)) }
          106  +	s.ct = lib.pq.PQgetlength(self.res, row, col)
          107  +	return s
          108  +end
          109  +terra pqr:bool(row: intptr, col: intptr)
          110  +	var v = lib.pq.PQgetvalue(self.res, row, col)
          111  +	if @v == 0x01 then return true else return false end
          112  +end
          113  +terra pqr:cidr(row: intptr, col: intptr)
    64    114   	var v = lib.pq.PQgetvalue(self.res, row, col)
    65         -	var r: lib.mem.ptr(int8)
    66         -	r.ct = lib.str.sz(v)
    67         -	r.ptr = lib.str.ndup(v, r.ct)
    68         -	return r
          115  +	var i: lib.store.inet
          116  +	if v[0] == 0x02 then i.pv = 4
          117  +	elseif v[0] == 0x03 then i.pv = 6
          118  +	else lib.bail('invalid CIDR type in stream') end
          119  +	i.fixbits = v[1]
          120  +	if v[2] ~= 0x1 then lib.bail('expected CIDR but got inet from stream') end
          121  +	if i.pv == 4 and v[3] ~= 0x04 or i.pv == 6 and v[3] ~= 0x10 then
          122  +		lib.bail('CIDR failed length sanity check') end
          123  +	
          124  +	var sz: intptr if i.pv == 4 then sz = 4 else sz = 16 end
          125  +	for j=0,sz do i.v6[j] = v[4 + j] end -- 😬
          126  +	return i
    69    127   end
    70    128   pqr.methods.int = macro(function(self, ty, row, col)
    71    129   	return quote
    72    130   		var i: ty:astype()
    73    131   		var v = lib.pq.PQgetvalue(self.res, row, col)
    74    132   		lib.math.netswap_ip(ty, v, &i)
    75    133   	in i end
    76    134   end)
          135  +
          136  +local pqt = {
          137  +	[lib.store.inet] = function(cidr)
          138  +		local tycode = cidr and 0x01 or 0x00
          139  +		return terra(i: lib.store.inet, buf: &uint8)
          140  +			var sz: intptr
          141  +			if i.pv == 4 then sz = 4 else sz = 16 end
          142  +			if buf == nil then buf = [&uint8](lib.mem.heapa_raw(sz + 4)) end
          143  +			if     i.pv == 4 then buf[0] = 0x02
          144  +			elseif i.pv == 6 then buf[0] = 0x03 end
          145  +			if cidr then -- our local 'inet' is not quite orthogonal to the
          146  +			             -- postgres inet type; tweak it to match (ignore port)
          147  +				buf[1] = i.fixbits
          148  +			elseif i.pv == 6 then buf[1] = 128
          149  +			else buf[1] = 32 end
          150  +			buf[2] = tycode
          151  +			buf[3] = sz
          152  +			for j=0,sz do buf[4 + j] = i.v6[j] end -- 😬
          153  +			return buf
          154  +		end
          155  +	end;
          156  +}
    77    157   
    78    158   local con = symbol(&lib.pq.PGconn)
    79    159   local prep = {}
          160  +local sqlsquash = function(s) return s:gsub('%s+',' '):gsub('^%s*(.-)%s*$','%1') end
    80    161   for k,q in pairs(queries) do
    81         -	local qt = (q.sql):gsub('%s+',' '):gsub('^%s*(.-)%s*$','%1')
          162  +	local qt = sqlsquash(q.sql)
    82    163   	local stmt = 'parsavpg_' .. k
    83    164   	prep[#prep + 1] = quote
    84    165   		var res = lib.pq.PQprepare([con], stmt, qt, [#q.params], nil)
    85    166   		defer lib.pq.PQclear(res)
    86    167   		if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_COMMAND_OK then
    87    168   			if res == nil then
    88    169   				lib.bail('grievous error occurred preparing ',k,' statement')
................................................................................
   105    186   			fixers[#fixers + 1] = quote
   106    187   				--lib.io.fmt('uid=%llu(%llx)\n',[args[i]],[args[i]])
   107    188   				[args[i]] = lib.math.netswap(ty, [args[i]])
   108    189   			end
   109    190   		end
   110    191   	end
   111    192   
   112         -	q.exec = terra(src: &lib.store.source, [args])
          193  +	terra q.exec(src: &lib.store.source, [args])
   113    194   		var params = arrayof([&int8], [casts])
   114    195   		var params_sz = arrayof(int, [counters])
   115    196   		var params_ft = arrayof(int, [ft])
   116    197   		[fixers]
   117    198   		var res = lib.pq.PQexecPrepared([&lib.pq.PGconn](src.handle), stmt,
   118    199   			[#args], params, params_sz, params_ft, 1)
   119    200   		if res == nil then
................................................................................
   129    210   			return pqr {0, nil}
   130    211   		else
   131    212   			return pqr {ct, res}
   132    213   		end
   133    214   	end
   134    215   end
   135    216   
   136         -local terra row_to_actor(r: &pqr, row: intptr): lib.store.actor
   137         -	var a = lib.store.actor {
   138         -		id = r:int(uint64, row, 0);
   139         -		nym = r:string(row, 1);
   140         -		handle = r:string(row, 2);
   141         -		bio = r:string(row, 4);
   142         -		key = r:string(row, 7);
   143         -		rights = lib.store.rights_default();
   144         -	}
   145         -	a.rights.rank = r:int(uint16, 0, 5);
   146         -	a.rights.quota = r:int(uint32, 0, 6);
   147         -	if r:null(0,3) then a.origin = 0
   148         -	else a.origin = r:int(uint64,0,3) end
          217  +local terra row_to_actor(r: &pqr, row: intptr): lib.mem.ptr(lib.store.actor)
          218  +	var a: lib.mem.ptr(lib.store.actor)
          219  +	if r:cols() >= 8 then 
          220  +		a = [ lib.str.encapsulate(lib.store.actor, {
          221  +			nym = {`r:string(row, 1); `r:len(row,1) + 1};
          222  +			handle = {`r:string(row, 2); `r:len(row,2) + 1};
          223  +			bio = {`r:string(row, 4); `r:len(row,4) + 1};
          224  +			xid = {`r:string(row, 8); `r:len(row,8) + 1};
          225  +		}) ]
          226  +	else
          227  +		a = [ lib.str.encapsulate(lib.store.actor, {
          228  +			nym = {`r:string(row, 1); `r:len(row,1) + 1};
          229  +			handle = {`r:string(row, 2); `r:len(row,2) + 1};
          230  +			bio = {`r:string(row, 4); `r:len(row,4) + 1};
          231  +		}) ]
          232  +		a.ptr.xid = nil
          233  +	end
          234  +	a.ptr.id = r:int(uint64, row, 0);
          235  +	a.ptr.rights = lib.store.rights_default();
          236  +	a.ptr.rights.rank = r:int(uint16, row, 5);
          237  +	a.ptr.rights.quota = r:int(uint32, row, 6);
          238  +	if r:null(row,7) then
          239  +		a.ptr.key.ct = 0 a.ptr.key.ptr = nil
          240  +	else
          241  +		a.ptr.key = r:bin(row,7)
          242  +	end
          243  +	if r:null(row,3) then a.ptr.origin = 0
          244  +	else a.ptr.origin = r:int(uint64,row,3) end
   149    245   	return a
   150    246   end
          247  +
          248  +local checksha = function(hnd, query, hash, origin, username, pw)
          249  +	local inet_buf = symbol(uint8[4 + 16])
          250  +	local validate = function(kind, cred, credlen)
          251  +		return quote 
          252  +			var osz: intptr if origin.pv == 4 then osz = 4 else osz = 16 end 
          253  +			var formats = arrayof([int], 1,1,1,1)
          254  +			var params = arrayof([&int8], username, kind,
          255  +				[&int8](&cred), [&int8](&inet_buf))
          256  +			var lens = arrayof(int, lib.str.sz(username), [#kind], credlen, osz + 4)
          257  +			var res = lib.pq.PQexecParams([&lib.pq.PGconn](hnd), query, 4, nil,
          258  +				params, lens, formats, 1)
          259  +			if res == nil then
          260  +				lib.bail('grievous failure checking pwhash')
          261  +			elseif lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
          262  +				lib.warn('pwhash query failed: ', lib.pq.PQresultErrorMessage(res), '\n', query)
          263  +			else
          264  +				var r = pqr {
          265  +					sz = lib.pq.PQntuples(res);
          266  +					res = res;
          267  +				}
          268  +				if r.sz > 0 then -- found a record! stop here
          269  +					var aid = r:int(uint64, 0,0)
          270  +					r:free()
          271  +					return aid
          272  +				end
          273  +			end
          274  +		end
          275  +	end
          276  +	
          277  +	local out = symbol(uint8[64])
          278  +	local vdrs = {}
          279  +
          280  +		local alg = lib.md['MBEDTLS_MD_SHA' .. tostring(hash)]
          281  +		vdrs[#vdrs+1] = quote
          282  +			if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(alg),
          283  +				[&uint8](pw), lib.str.sz(pw), out) ~= 0 then
          284  +				lib.bail('hashing failure!')
          285  +			end
          286  +			[ validate(string.format('pw-sha%u', hash), out, hash / 8) ]
          287  +		end
          288  +
          289  +	return quote
          290  +		lib.dbg(['searching for hashed password credentials in format SHA' .. tostring(hash)])
          291  +		var [inet_buf]
          292  +		[pqt[lib.store.inet](false)](origin, inet_buf)
          293  +		var [out]
          294  +		[vdrs]
          295  +		lib.dbg(['could not find password hash'])
          296  +	end
          297  +end
   151    298   
   152    299   local b = `lib.store.backend {
   153    300   	id = "pgsql";
   154    301   	open = [terra(src: &lib.store.source): &opaque
   155    302   		lib.report('connecting to postgres database: ', src.string.ptr)
   156    303   		var [con] = lib.pq.PQconnectdb(src.string.ptr)
   157    304   		if lib.pq.PQstatus(con) ~= lib.pq.CONNECTION_OK then
................................................................................
   158    305   			lib.warn('postgres backend connection failed')
   159    306   			lib.pq.PQfinish(con)
   160    307   			return nil
   161    308   		end
   162    309   		var res = lib.pq.PQexec(con, [[
   163    310   			select pg_catalog.set_config('search_path', 'public', false)
   164    311   		]])
   165         -		if res ~= nil then defer lib.pq.PQclear(res) end
   166         -		if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
          312  +		if res == nil then
          313  +			lib.warn('critical failure to secure postgres connection')
          314  +			lib.pq.PQfinish(con)
          315  +			return nil
          316  +		end
          317  +
          318  +		defer lib.pq.PQclear(res)
          319  +		if lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
   167    320   			lib.warn('failed to secure postgres connection')
   168    321   			lib.pq.PQfinish(con)
   169    322   			return nil
   170    323   		end
   171    324   
   172    325   		[prep]
   173    326   		return con
................................................................................
   174    327   	end];
   175    328   	close = [terra(src: &lib.store.source) lib.pq.PQfinish([&lib.pq.PGconn](src.handle)) end];
   176    329   
   177    330   	conf_get = [terra(src: &lib.store.source, key: rawstring)
   178    331   		var r = queries.conf_get.exec(src, key)
   179    332   		if r.sz == 0 then return [lib.mem.ptr(int8)] { ptr = nil, ct = 0 } else
   180    333   			defer r:free()
   181         -			return r:string(0,0)
          334  +			return r:String(0,0)
   182    335   		end
   183    336   	end];
   184    337   	conf_set = [terra(src: &lib.store.source, key: rawstring, val: rawstring)
   185    338   		queries.conf_set.exec(src, key, val):free() end];
   186    339   	conf_reset = [terra(src: &lib.store.source, key: rawstring)
   187    340   		queries.conf_reset.exec(src, key):free() end];
   188    341   	
   189    342   	actor_fetch_uid = [terra(src: &lib.store.source, uid: uint64)
   190    343   		var r = queries.actor_fetch_uid.exec(src, uid)
   191    344   		if r.sz == 0 then
   192         -			return [lib.stat(lib.store.actor)] { ok = false, error = 1}
   193         -		else
   194         -			defer r:free()
   195         -			var a = [lib.stat(lib.store.actor)] { ok = true }
   196         -			a.val = row_to_actor(&r, 0)
   197         -			a.val.source = src
          345  +			return [lib.mem.ptr(lib.store.actor)] { ct = 0, ptr = nil }
          346  +		else defer r:free()
          347  +			var a = row_to_actor(&r, 0)
          348  +			a.ptr.source = src
   198    349   			return a
   199    350   		end
   200    351   	end];
          352  +
          353  +	actor_enum = [terra(src: &lib.store.source)
          354  +		var r = queries.actor_enum.exec(src)
          355  +		if r.sz == 0 then
          356  +			return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil }
          357  +		else defer r:free()
          358  +			var mem = lib.mem.heapa([&lib.store.actor], r.sz)
          359  +			for i=0,r.sz do mem.ptr[i] = row_to_actor(&r, i).ptr end
          360  +			return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr }
          361  +		end
          362  +	end];
          363  +
          364  +	actor_enum_local = [terra(src: &lib.store.source)
          365  +		var r = queries.actor_enum_local.exec(src)
          366  +		if r.sz == 0 then
          367  +			return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil }
          368  +		else defer r:free()
          369  +			var mem = lib.mem.heapa([&lib.store.actor], r.sz)
          370  +			for i=0,r.sz do mem.ptr[i] = row_to_actor(&r, i).ptr end
          371  +			return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr }
          372  +		end
          373  +	end];
          374  +
          375  +	actor_auth_how = [terra(
          376  +			src: &lib.store.source,
          377  +			ip: lib.store.inet,
          378  +			username: rawstring
          379  +		)
          380  +		var authview = src:conf_get('auth-source') defer authview:free()
          381  +		var a: lib.str.acc defer a:free()
          382  +		a:compose('with mts as (select a.kind from ',authview,[' ' .. sqlsquash [[as a
          383  +			left join parsav_actors as u on u.id = a.uid
          384  +			where (a.uid is null or u.handle = $1::text or (
          385  +					a.uid = 0 and a.name = $1::text
          386  +				)) and
          387  +				(a.netmask is null or a.netmask >> $2::inet) and
          388  +				blacklist = false)
          389  +
          390  +			select
          391  +				(select count(*) from mts where kind like 'pw-%') > 0,
          392  +				(select count(*) from mts where kind like 'otp-%') > 0,
          393  +				(select count(*) from mts where kind like 'challenge-%') > 0,
          394  +				(select count(*) from mts where kind = 'trust') > 0 ]]]) -- cheat
          395  +		var cs: lib.store.credset cs:clear();
          396  +		var ipbuf: int8[20]
          397  +		;[pqt[lib.store.inet](false)](ip, [&uint8](&ipbuf))
          398  +		var ipbl: intptr if ip.pv == 4 then ipbl = 8 else ipbl = 20 end
          399  +		var params = arrayof(rawstring, username, [&int8](&ipbuf))
          400  +		var params_sz = arrayof(int, lib.str.sz(username), ipbl)
          401  +		var params_ft = arrayof(int, 1, 1)
          402  +		var res = lib.pq.PQexecParams([&lib.pq.PGconn](src.handle), a.buf, 2, nil,
          403  +			params, params_sz, params_ft, 1)
          404  +		if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
          405  +			if res == nil then
          406  +				lib.bail('grievous error occurred checking for auth methods')
          407  +			end
          408  +			lib.bail('could not get auth methods for user ',username,':\n',lib.pq.PQresultErrorMessage(res))
          409  +		end
          410  +		var r = pqr { res = res, sz = lib.pq.PQntuples(res) } 
          411  +		if r.sz == 0 then return cs end -- just in case
          412  +		(cs.pw << r:bool(0,0))
          413  +		(cs.otp << r:bool(0,1))
          414  +		(cs.challenge << r:bool(0,2))
          415  +		(cs.trust << r:bool(0,3))
          416  +		lib.pq.PQclear(res)
          417  +		return cs
          418  +	end];
          419  +	 
          420  +	actor_auth_pw = [terra(
          421  +			src: &lib.store.source,
          422  +			ip: lib.store.inet,
          423  +			username: rawstring,
          424  +			cred: rawstring
          425  +		)
          426  +		var authview = src:conf_get('auth-source') defer authview:free()
          427  +		var a: lib.str.acc defer a:free()
          428  +		a:compose('select a.aid from ',authview,[' ' .. sqlsquash [[as a
          429  +			left join parsav_actors as u on u.id = a.uid
          430  +			where (a.uid is null or u.handle = $1::text or (
          431  +					a.uid = 0 and a.name = $1::text
          432  +				)) and
          433  +				(a.kind = 'trust' or (a.kind = $2::text and a.cred = $3::bytea)) and
          434  +				(a.netmask is null or a.netmask >> $4::inet)
          435  +			order by blacklist desc limit 1]]])
          436  +
          437  +		[ checksha(`src.handle, `a.buf, 256, ip, username, cred) ] -- most common
          438  +		[ checksha(`src.handle, `a.buf, 512, ip, username, cred) ] -- most secure
          439  +		[ checksha(`src.handle, `a.buf, 384, ip, username, cred) ] -- weird
          440  +		[ checksha(`src.handle, `a.buf, 224, ip, username, cred) ] -- weirdest
          441  +
          442  +		-- TODO: check pbkdf2-hmac
          443  +		-- TODO: check OTP
          444  +		return 0
          445  +	end];
   201    446   }
   202    447   
   203    448   return b

Modified cmdparse.t from [c7f162fae3] to [bad20dd1d0].

     5      5   		local helpstr = 'usage: parsav [-' .. flags .. '] [<arg>...]\n'
     6      6   		options.entries = {
     7      7   			{field = 'arglist', type = lib.mem.ptr(rawstring)}
     8      8   		}
     9      9   		local shortcases, longcases, init = {}, {}, {}
    10     10   		local self = symbol(&options)
    11     11   		local arg = symbol(rawstring)
           12  +		local idxo = symbol(uint)
    12     13   		local skip = label()
           14  +		local sanitize = function(s) return s:gsub('_','-') end
    13     15   		for o,desc in pairs(tbl) do
           16  +			local consume = desc[3] or 0
    14     17   			options.entries[#options.entries + 1] = {
    15         -				field = o, type = bool
           18  +				field = o, type = (consume > 0) and uint or bool
    16     19   			}
    17     20   			helpstr = helpstr .. string.format('    -%s --%s: %s\n',
    18         -				desc[1], o, desc[2])
           21  +				desc[1], sanitize(o), desc[2])
    19     22   		end
    20     23   		for o,desc in pairs(tbl) do
    21     24   			local flag = desc[1]
    22         -			init[#init + 1] = quote [self].[o] = false end
           25  +			local consume = desc[3] or 0
           26  +			init[#init + 1] = quote [self].[o] = [consume > 0 and 0 or false] end
           27  +			local ch if consume > 0 then ch = quote
           28  +				[self].[o] = idxo
           29  +				idxo = idxo + consume
           30  +			end else ch = quote
           31  +				[self].[o] = true
           32  +			end end
    23     33   			shortcases[#shortcases + 1] = quote
    24         -				case [int8]([string.byte(flag)]) then [self].[o] = true end
           34  +				case [int8]([string.byte(flag)]) then [ch] end
    25     35   			end
    26     36   			longcases[#longcases + 1] = quote
    27         -				if lib.str.cmp([arg]+2, o) == 0 then
    28         -					[self].[o] = true
    29         -					goto [skip]
    30         -				end
           37  +				if lib.str.cmp([arg]+2, [sanitize(o)]) == 0 then [ch] goto [skip] end
    31     38   			end
    32     39   		end
           40  +		terra options:free() self.arglist:free() end
    33     41   		options.methods.parse = terra([self], argc: int, argv: &rawstring)
    34     42   			[init]
    35     43   			var parseopts = true
           44  +			var [idxo] = 1
    36     45   			self.arglist = lib.mem.heapa(rawstring, argc)
    37     46   			var finalargc = 0
    38         -			for i=0,argc do
           47  +			for i=1,argc do
    39     48   				var [arg] = argv[i]
    40     49   				if arg[0] == ('-')[0] and parseopts then
    41     50   					if arg[1] == ('-')[0] then -- long option
    42     51   						if arg[2] == 0 then -- last option
    43     52   							parseopts = false
    44     53   						else [longcases] end
    45     54   					else -- short options

Modified config.lua from [60797f7bc4] to [53779583db].

    10     10   	end
    11     11   end
    12     12   local posixes = {
    13     13   	linux = true; osx = true;
    14     14   	android = true; haiku = true;
    15     15   }
    16     16   local default_os = 'linux'
           17  +local defaultlist = function(env, ...)
           18  +	local e = os.getenv(env)
           19  +	local lst = {}
           20  +	if e then
           21  +		for v in e:gmatch('([^:]*)') do lst[#lst + 1] = v end
           22  +		return lst
           23  +	else return {...} end
           24  +end
    17     25   local conf = {
    18     26   	pkg = {};
    19     27   	dist      = default('parsav_dist', coalesce(
    20     28   		os.getenv('NIX_PATH')  and 'nixos',
    21     29   		os.getenv('NIX_STORE') and 'nixos',
    22     30   	''));
    23     31   	tgttrip   = default('parsav_arch_triple'); -- target triple, used in xcomp
................................................................................
    26     34   	endian    = default('parsav_arch_endian', 'little');
    27     35   	build     = {
    28     36   		id = u.rndstr(6);
    29     37   		release = u.ingest('release');
    30     38   		when = os.date();
    31     39   	};
    32     40   	feat = {};
           41  +	backends = defaultlist('parsav_backends', 'pgsql');
           42  +	braingeniousmode = false;
    33     43   }
    34     44   if u.ping '.fslckout' or u.ping '_FOSSIL_' then
    35     45   	if u.ping '_FOSSIL_' then default_os = 'windows' end
    36     46   	conf.build.branch = u.exec { 'fossil', 'branch', 'current' }
    37     47   	conf.build.checkout = (u.exec { 'fossil', 'sql',
    38     48   		[[select value from localdb.vvar where name = 'checkout-hash']]
    39     49   	}):gsub("^'(.*)'$", '%1')

Modified crypt.t from [340864c560] to [709e2a6426].

    10     10   	end;
    11     11   	toobig = -lib.pk.MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE;
    12     12   }
    13     13   const.maxpemsz = math.floor((const.keybits / 8)*6.4) + 128 -- idk why this formula works but it basically seems to
    14     14   
    15     15   local ctx = lib.pk.mbedtls_pk_context
    16     16   
           17  +local struct hashalg { id: uint8 bytes: intptr }
    17     18   local m = {
    18     19   	pemfile = uint8[const.maxpemsz];
           20  +	alg = {
           21  +		sha1 =   `hashalg {id = lib.md.MBEDTLS_MD_SHA1; bytes = 160/8};
           22  +		sha256 = `hashalg {id = lib.md.MBEDTLS_MD_SHA256; bytes = 256/8};
           23  +		sha512 = `hashalg {id = lib.md.MBEDTLS_MD_SHA512; bytes = 512/8};
           24  +		sha384 = `hashalg {id = lib.md.MBEDTLS_MD_SHA384; bytes = 384/8};
           25  +		sha224 = `hashalg {id = lib.md.MBEDTLS_MD_SHA224; bytes = 224/8};
           26  +		-- md5 = {id = lib.md.MBEDTLS_MD_MD5};-- !!!
           27  +	};
    19     28   }
    20     29   local callbacks = {}
    21     30   if config.feat.randomizer == 'kern' then
    22     31   	local rnd = terralib.externfunction('getrandom', {&opaque, intptr, uint} -> ptrdiff);
    23     32   	terra callbacks.randomize(ctx: &opaque, dest: &uint8, sz: intptr): int
    24     33   		return rnd(dest, sz, 0)
    25     34   	end
................................................................................
   132    141   		if lib.pk.mbedtls_pk_verify(pk, hk, hash, 0, [&uint8](sig), siglen) == 0 then
   133    142   			return true, secl
   134    143   		end
   135    144   	end
   136    145   	lib.dbg('all hash algorithms failed')
   137    146   	return false, 0
   138    147   end
          148  +
          149  +terra m.hmac(alg: hashalg, key: lib.mem.ptr(uint8), txt: lib.mem.ptr(int8), buf: &uint8)
          150  +	lib.md.mbedtls_md_hmac(
          151  +			lib.md.mbedtls_md_info_from_type(alg.id), 
          152  +			key.ptr, key.ct,
          153  +			[&uint8](txt.ptr), txt.ct,
          154  +			buf) -- sz(buf) >= hash output size
          155  +end
          156  +
          157  +terra m.hmaca(alg: hashalg, key: lib.mem.ptr(uint8), txt: lib.mem.ptr(int8))
          158  +	var buf = lib.mem.heapa(uint8, alg.bytes)
          159  +	m.hmac(alg, key, txt, buf.ptr)
          160  +	return buf
          161  +end
          162  +
          163  +terra m.hotp(key: &(uint8[10]), counter: uint64)
          164  +	var hmac: uint8[20]
          165  +	var ctr = [lib.mem.ptr(int8)]{ptr = [&int8](&counter), ct = 8}
          166  +	m.hmac(m.alg.sha1,
          167  +		[lib.mem.ptr(uint8)]{ptr = [&uint8](key), ct = 10},
          168  +		ctr, hmac)
          169  +	
          170  +	var ofs = hmac[19] and 0x0F
          171  +	var p: uint8[4]
          172  +	for i=0,4 do p[i] = hmac[ofs + i] end
          173  +
          174  +	return (@[&uint32](&p)) and 0x7FFFFFFF -- one hopes it's that easy
          175  +end
   139    176   
   140    177   return m

Added license.de version [34fb6d78d2].

            1  +OPEN-SOURCE-LIZENZ FÜR DIE EUROPÄISCHE UNION v. 1.2 
            2  +EUPL © Europäische Union 2007, 2016 
            3  +Diese Open-Source-Lizenz für die Europäische Union („EUPL“) gilt für Werke (im Sinne der nachfolgenden Begriffsbestimmung), die unter EUPL-Bedingungen zur Verfügung gestellt werden. Das Werk darf nur in der durch diese Lizenz gestatteten Form genutzt werden (insoweit eine solche Nutzung dem Urheber vorbehalten ist). 
            4  +Das Werk wird unter den Bedingungen dieser Lizenz zur Verfügung gestellt, wenn der Lizenzgeber (im Sinne der nachfolgenden Begriffsbestimmung) den folgenden Hinweis unmittelbar hinter dem Urheberrechtshinweis dieses Werks anbringt: 
            5  +                      Lizenziert unter der EUPL 
            6  +oder in einer anderen Form zum Ausdruck bringt, dass er es unter der EUPL lizenzieren möchte. 
            7  +
            8  +1.Begriffsbestimmungen 
            9  +Für diese Lizenz gelten folgende Begriffsbestimmungen:  
           10  +— „Lizenz“:diese Lizenz.  
           11  +— „Originalwerk“:das Werk oder die Software, die vom Lizenzgeber unter dieser Lizenz verbreitet oder zugänglich gemacht wird, und zwar als Quellcode und gegebenenfalls auch als ausführbarer Code.  
           12  +— „Bearbeitungen“:die Werke oder Software, die der Lizenznehmer auf der Grundlage des Originalwerks oder seiner Bearbeitungen schaffen kann. In dieser Lizenz wird nicht festgelegt, wie umfangreich die Änderung oder wie stark die Abhängigkeit vom Originalwerk für eine Einstufung als Bearbeitung sein muss; dies bestimmt sich nach dem Urheberrecht, das in dem unter Artikel 15 aufgeführten Land anwendbar ist.  
           13  +— „Werk“:das Originalwerk oder seine Bearbeitungen.  
           14  +— „Quellcode“:diejenige Form des Werkes, die zur Auffassung durch den Menschen bestimmt ist und die am besten geeignet ist, um vom Menschen verstanden und verändert zu werden.  
           15  +— „Ausführbarer Code“:die — üblicherweise — kompilierte Form des Werks, die von einem Computer als Programm ausgeführt werden soll.  
           16  +— „Lizenzgeber“:die natürliche oder juristische Person, die das Werk unter der Lizenz verbreitet oder zugänglich macht.  
           17  +— „Bearbeiter“:jede natürliche oder juristische Person, die das Werk unter der Lizenz verändert oder auf andere Weise zur Schaffung einer Bearbeitung beiträgt.  
           18  +— „Lizenznehmer“ („Sie“):jede natürliche oder juristische Person, die das Werk unter den Lizenzbedingungen nutzt.  
           19  +— „Verbreitung“ oder „Zugänglichmachung“:alle Formen von Verkauf, Überlassung, Verleih, Vermietung, Verbreitung, Weitergabe, Übermittlung oder anderweitiger Online- oder Offline-Bereitstellung von Vervielfältigungen des Werks oder Zugänglichmachung seiner wesentlichen Funktionen für dritte natürliche oder juristische Personen.
           20  +
           21  +2.Umfang der Lizenzrechte 
           22  +Der Lizenzgeber erteilt Ihnen hiermit für die Gültigkeitsdauer der am Originalwerk bestehenden Urheberrechte eine weltweite, unentgeltliche, nicht ausschließliche, unterlizenzierbare Lizenz, die Sie berechtigt: 
           23  +—  das Werk uneingeschränkt zu nutzen, 
           24  +—  das Werk zu vervielfältigen, 
           25  +—  das Werk zu verändern und Bearbeitungen auf der Grundlage des Werks zu schaffen, 
           26  +—  das Werk öffentlich zugänglich zu machen, was das Recht einschließt, das Werk oder Vervielfältigungsstücke davon öffentlich bereitzustellen oder wahrnehmbar zu machen oder das Werk, soweit möglich, öffentlich aufzuführen, 
           27  +—  das Werk oder Vervielfältigungen davon zu verbreiten, 
           28  +—  das Werk oder Vervielfältigungen davon zu vermieten oder zu verleihen, 
           29  +—  das Werk oder Vervielfältigungen davon weiter zu lizenzieren. 
           30  +Für die Wahrnehmung dieser Rechte können beliebige, derzeit bekannte oder künftige Medien, Träger und Formate verwendet werden, soweit das geltende Recht dem nicht entgegensteht. Für die Länder, in denen Urheberpersönlichkeitsrechte an dem Werk bestehen, verzichtet der Lizenzgeber im gesetzlich zulässigen Umfang auf seine Urheberpersönlichkeitsrechte, um die Lizenzierung der oben aufgeführten Verwertungsrechte wirksam durchführen zu können. Der Lizenzgeber erteilt dem Lizenznehmer ein nicht ausschließliches, unentgeltliches Nutzungsrecht an seinen Patenten, sofern dies zur Ausübung der durch die Lizenz erteilten Nutzungsrechte am Werk notwendig ist. 
           31  +
           32  +3.Zugänglichmachung des Quellcodes 
           33  +Der Lizenzgeber kann das Werk entweder als Quellcode oder als ausführbaren Code zur Verfügung stellen. Stellt er es als ausführbaren Code zur Verfügung, so stellt er darüber hinaus eine maschinenlesbare Kopie des Quellcodes für jedes von ihm verbreitete Vervielfältigungsstück des Werks zur Verfügung, oder er verweist in einem Vermerk im Anschluss an den dem Werk beigefügten Urheberrechtshinweis auf einen Speicherort, an dem problemlos und unentgeltlich auf den Quellcode zugegriffen werden kann, solange der Lizenzgeber das Werk verbreitet oder zugänglich macht. 
           34  +
           35  +4.Einschränkungen des Urheberrechts
           36  +Es ist nicht Zweck dieser Lizenz, Ausnahmen oder Schranken der ausschließlichen Rechte des Urhebers am Werk, die dem Lizenznehmer zugutekommen, einzuschränken. Auch die Erschöpfung dieser Rechte bleibt von dieser Lizenz unberührt. 
           37  +
           38  +5.Pflichten des Lizenznehmers 
           39  +Die Einräumung der oben genannten Rechte ist an mehrere Beschränkungen und Pflichten für den Lizenznehmer gebunden: 
           40  +
           41  +Urheberrechtshinweis, Lizenztext, Nennung des Bearbeiters: Der Lizenznehmer muss alle Urheberrechts-, Patent- oder Markenrechtshinweise und alle Hinweise auf die Lizenz und den Haftungsausschluss unverändert lassen. Jedem von ihm verbreiteten oder zugänglich gemachten Vervielfältigungsstück des Werks muss der Lizenznehmer diese Hinweise sowie diese Lizenz beifügen. Der Lizenznehmer muss auf jedem abgeleiteten Werk deutlich darauf hinweisen, dass das Werk geändert wurde, und das Datum der Bearbeitung angeben.
           42  +
           43  +„Copyleft“-Klausel: Der Lizenznehmer darf Vervielfältigungen des Originalwerks oder Bearbeitungen nur unter den Bedingungen dieser EUPL oder einer neueren Version dieser Lizenz verbreiten oder zugänglich machen, außer wenn das Originalwerk ausdrücklich nur unter dieser Lizenzversion — z. B. mit der Angabe „Nur EUPL V. 1.2“ — verbreitet werden darf. Der Lizenznehmer (der zum Lizenzgeber wird) darf für das Werk oder die Bearbeitung keine zusätzlichen Bedingungen anbieten oder vorschreiben, die die Bedingungen dieser Lizenz verändern oder einschränken. 
           44  +
           45  +Kompatibilitäts-Klausel: Wenn der Lizenznehmer Bearbeitungen, die auf dem Werk und einem anderen Werk, das unter einer kompatiblen Lizenz lizenziert wurde, basieren, oder die Kopien dieser Bearbeitungen verbreitet oder zugänglich macht, kann dies unter den Bedingungen dieser kompatiblen Lizenz erfolgen. Unter „kompatibler Lizenz“ ist eine im Anhang dieser Lizenz angeführte Lizenz zu verstehen. Sollten die Verpflichtungen des Lizenznehmers aus der kompatiblen Lizenz mit denjenigen aus der vorliegenden Lizenz (EUPL) in Konflikt stehen, werden die Verpflichtungen aus der kompatiblen Lizenz Vorrang haben.
           46  +
           47  +Bereitstellung des Quellcodes: Wenn der Lizenznehmer Vervielfältigungsstücke des Werks verbreitet oder zugänglich macht, muss er eine maschinenlesbare Fassung des Quellcodes mitliefern oder einen Speicherort angeben, über den problemlos und unentgeltlich so lange auf diesen Quellcode zugegriffen werden kann, wie der Lizenznehmer das Werk verbreitet oder zugänglich macht. 
           48  +
           49  +Rechtsschutz: Diese Lizenz erlaubt nicht die Benutzung von Kennzeichen, Marken oder geschützten Namensrechten des Lizenzgebers, soweit dies nicht für die angemessene und übliche Beschreibung der Herkunft des Werks und der inhaltlichen Wiedergabe des Urheberrechtshinweises erforderlich ist.
           50  +
           51  +6.Urheber und Bearbeiter 
           52  +Der ursprüngliche Lizenzgeber gewährleistet, dass er das Urheberrecht am Originalwerk innehat oder dieses an ihn lizenziert wurde und dass er befugt ist, diese Lizenz zu erteilen. 
           53  +Jeder Bearbeiter gewährleistet, dass er das Urheberrecht an den von ihm vorgenommenen Änderungen des Werks besitzt und befugt ist, diese Lizenz zu erteilen. 
           54  +Jedes Mal, wenn Sie die Lizenz annehmen, erteilen Ihnen der ursprüngliche Lizenzgeber und alle folgenden Bearbeiter eine Befugnis zur Nutzung ihrer Beiträge zum Werk unter den Bedingungen dieser Lizenz. 
           55  +
           56  +7.Gewährleistungsausschluss 
           57  +Die Arbeit an diesem Werk wird laufend fortgeführt; es wird durch unzählige Bearbeiter ständig verbessert. Das Werk ist nicht vollendet und kann daher Fehler („bugs“) enthalten, die dieser Art der Entwicklung inhärent sind. 
           58  +Aus den genannten Gründen wird das Werk unter dieser Lizenz „so, wie es ist“ ohne jegliche Gewährleistung zur Verfügung gestellt. Dies gilt unter anderem — aber nicht ausschließlich — für Marktreife, Verwendbarkeit für einen bestimmten Zweck, Mängelfreiheit, Richtigkeit sowie Nichtverletzung von anderen Immaterialgüterrechten als dem Urheberrecht (vgl. dazu Artikel 6 dieser Lizenz). 
           59  +Dieser Gewährleistungsausschluss ist wesentlicher Bestandteil der Lizenz und Bedingung für die Einräumung von Rechten an dem Werk.
           60  +
           61  +8.Haftungsausschluss/Haftungsbeschränkung 
           62  +Außer in Fällen von Vorsatz oder der Verursachung von Personenschäden haftet der Lizenzgeber nicht für direkte oder indirekte, materielle oder immaterielle Schäden irgendwelcher Art, die aus der Lizenz oder der Benutzung des Werks folgen; dies gilt unter anderem, aber nicht ausschließlich, für Firmenwertverluste, Produktionsausfall, Computerausfall oder Computerfehler, Datenverlust oder wirtschaftliche Schäden, und zwar auch dann, wenn der Lizenzgeber auf die Möglichkeit solcher Schäden hingewiesen wurde. Unabhängig davon haftet der Lizenzgeber im Rahmen der gesetzlichen Produkthaftung, soweit die entsprechenden Regelungen auf das Werk anwendbar sind. 
           63  +
           64  +9.Zusatzvereinbarungen 
           65  +Wenn Sie das Werk verbreiten, können Sie Zusatzvereinbarungen schließen, in denen Verpflichtungen oder Dienstleistungen festgelegt werden, die mit dieser Lizenz vereinbar sind. Sie dürfen Verpflichtungen indessen nur in Ihrem eigenen Namen und auf Ihre eigene Verantwortung eingehen, nicht jedoch im Namen des ursprünglichen Lizenzgebers oder eines anderen Bearbeiters, und nur, wenn Sie sich gegenüber allen Bearbeitern verpflichten, sie zu entschädigen, zu verteidigen und von der Haftung freizustellen, falls aufgrund der von Ihnen eingegangenen Gewährleistungsverpflichtung oder Haftungsübernahme Forderungen gegen sie geltend gemacht werden oder eine Haftungsverpflichtung entsteht. 
           66  +
           67  +10.Annahme der Lizenz 
           68  +Sie können den Bestimmungen dieser Lizenz zustimmen, indem Sie das Symbol „Lizenz annehmen“ unter dem Fenster mit dem Lizenztext anklicken oder indem Sie Ihre Zustimmung auf vergleichbare Weise in einer nach anwendbarem Recht zulässigen Form geben. Das Anklicken des Symbols gilt als Anzeichen Ihrer eindeutigen und unwiderruflichen Annahme der Lizenz und der darin enthaltenen Klauseln und Bedingungen. In gleicher Weise gilt als Zeichen der eindeutigen und unwiderruflichen Zustimmung die Ausübung eines Rechtes, das in Artikel 2 dieser Lizenz angeführt ist, wie das Erstellen einer Bearbeitung oder die Verbreitung oder Zugänglichmachung des Werks oder dessen Vervielfältigungen. 
           69  +
           70  +11.Informationspflichten
           71  +Wenn Sie das Werk verbreiten oder zugänglich machen (beispielsweise, indem Sie es zum Herunterladen von einer Website anbieten), müssen Sie über den Vertriebskanal oder das benutzte Verbreitungsmedium der Öffentlichkeit zumindest jene Informationen bereitstellen, die nach dem anwendbaren Recht bezüglich der Lizenzgeber, der Lizenz und ihrer Zugänglichkeit, des Abschlusses des Lizenzvertrags sowie darüber, wie die Lizenz durch den Lizenznehmer gespeichert und vervielfältigt werden kann, erforderlich sind.
           72  +
           73  +12.Beendigung der Lizenz 
           74  +Die Lizenz und die damit eingeräumten Rechte erlöschen automatisch, wenn der Lizenznehmer gegen die Lizenzbedingungen verstößt. Ein solches Erlöschen der Lizenz führt nicht zum Erlöschen der Lizenzen von Personen, denen das Werk vom Lizenznehmer unter dieser Lizenz zur Verfügung gestellt worden ist, solange diese Personen die Lizenzbedingungen erfüllen. 
           75  +13.Sonstiges
           76  +Unbeschadet des Artikels 9 stellt die Lizenz die vollständige Vereinbarung der Parteien über das Werk dar. Sind einzelne Bestimmungen der Lizenz nach geltendem Recht nichtig oder unwirksam, so berührt dies nicht die Wirksamkeit oder Durchsetzbarkeit der Lizenz an sich. Solche Bestimmungen werden vielmehr so ausgelegt oder modifiziert, dass sie wirksam und durchsetzbar sind. Die Europäische Kommission kann weitere Sprachfassungen oder neue Versionen dieser Lizenz oder aktualisierte Fassungen des Anhangs veröffentlichen, soweit dies notwendig und angemessen ist, ohne den Umfang der Lizenzrechte zu verringern. Neue Versionen werden mit einer eindeutigen Versionsnummer veröffentlicht. Alle von der Europäischen Kommission anerkannten Sprachfassungen dieser Lizenz sind gleichwertig. Die Parteien können sich auf die Sprachfassung ihrer Wahl berufen.
           77  +
           78  +14.Gerichtsstand
           79  +Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: 
           80  +—  Für alle Streitigkeiten über die Auslegung dieser Lizenz zwischen den Organen, Einrichtungen und sonstigen Stellen der Europäischen Union als Lizenzgeber und einem Lizenznehmer ist der Gerichtshof der Europäischen Union gemäß Artikel 272 des Vertrags über die Arbeitsweise der Europäischen Union zuständig; 
           81  +—  Gerichtsstand für Streitigkeiten zwischen anderen Parteien über die Auslegung dieser Lizenz ist allein der Ort, an dem der Lizenzgeber seinen Wohnsitz oder den wirtschaftlichen Mittelpunkt seiner Tätigkeit hat. 
           82  +
           83  +15.Anwendbares Recht
           84  +Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: 
           85  +—  Diese Lizenz unterliegt dem Recht des Mitgliedstaats der Europäischen Union, in dem der Lizenzgeber seinen Sitz, Wohnsitz oder eingetragenen Sitz hat; 
           86  +—  diese Lizenz unterliegt dem belgischen Recht, wenn der Lizenzgeber keinen Sitz, Wohnsitz oder eingetragenen Sitz in einem Mitgliedstaat der Europäischen Union hat.
           87  +   
           88  +Anlage 
           89  +„Kompatible Lizenzen“ nach Artikel 5 der EUPL sind: 
           90  +—  GNU General Public License (GPL) v. 2, v. 3 
           91  +—  GNU Affero General Public License (AGPL) v. 3 
           92  +—  Open Software License (OSL) v. 2.1, v. 3.0 
           93  +—  Eclipse Public License (EPL) v. 1.0 
           94  +—  CeCILL v. 2.0, v. 2.1 
           95  +—  Mozilla Public Licence (MPL) v. 2 
           96  +—  GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 
           97  +—  Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) für andere Werke als Software 
           98  +—  European Union Public Licence (EUPL) v. 1.1, v. 1.2 
           99  +—  Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) oder Strong Reciprocity (LiLiQ-R+)
          100  +Die Europäische Kommission kann diesen Anhang aktualisieren, um neuere Fassungen der obigen Lizenzen aufzunehmen, ohne hierfür eine neue Fassung der EUPL auszuarbeiten, solange diese Lizenzen die in Artikel 2 gewährten Rechte gewährleisten und den erfassten Quellcode vor ausschließlicher Aneignung schützen.
          101  +Alle sonstigen Änderungen oder Ergänzungen dieses Anhangs bedürfen der Ausarbeitung einer neuen Version der EUPL.    

Added license.en version [fc940c93f2].

            1  +                      EUROPEAN UNION PUBLIC LICENCE v. 1.2
            2  +                      EUPL © the European Union 2007, 2016
            3  +
            4  +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
            5  +below) which is provided under the terms of this Licence. Any use of the Work,
            6  +other than as authorised under this Licence is prohibited (to the extent such
            7  +use is covered by a right of the copyright holder of the Work).
            8  +
            9  +The Work is provided under the terms of this Licence when the Licensor (as
           10  +defined below) has placed the following notice immediately following the
           11  +copyright notice for the Work:
           12  +
           13  +        Licensed under the EUPL
           14  +
           15  +or has expressed by any other means his willingness to license under the EUPL.
           16  +
           17  +1. Definitions
           18  +
           19  +In this Licence, the following terms have the following meaning:
           20  +
           21  +- ‘The Licence’: this Licence.
           22  +
           23  +- ‘The Original Work’: the work or software distributed or communicated by the
           24  +  Licensor under this Licence, available as Source Code and also as Executable
           25  +  Code as the case may be.
           26  +
           27  +- ‘Derivative Works’: the works or software that could be created by the
           28  +  Licensee, based upon the Original Work or modifications thereof. This Licence
           29  +  does not define the extent of modification or dependence on the Original Work
           30  +  required in order to classify a work as a Derivative Work; this extent is
           31  +  determined by copyright law applicable in the country mentioned in Article 15.
           32  +
           33  +- ‘The Work’: the Original Work or its Derivative Works.
           34  +
           35  +- ‘The Source Code’: the human-readable form of the Work which is the most
           36  +  convenient for people to study and modify.
           37  +
           38  +- ‘The Executable Code’: any code which has generally been compiled and which is
           39  +  meant to be interpreted by a computer as a program.
           40  +
           41  +- ‘The Licensor’: the natural or legal person that distributes or communicates
           42  +  the Work under the Licence.
           43  +
           44  +- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
           45  +  Licence, or otherwise contributes to the creation of a Derivative Work.
           46  +
           47  +- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
           48  +  the Work under the terms of the Licence.
           49  +
           50  +- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
           51  +  renting, distributing, communicating, transmitting, or otherwise making
           52  +  available, online or offline, copies of the Work or providing access to its
           53  +  essential functionalities at the disposal of any other natural or legal
           54  +  person.
           55  +
           56  +2. Scope of the rights granted by the Licence
           57  +
           58  +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
           59  +sublicensable licence to do the following, for the duration of copyright vested
           60  +in the Original Work:
           61  +
           62  +- use the Work in any circumstance and for all usage,
           63  +- reproduce the Work,
           64  +- modify the Work, and make Derivative Works based upon the Work,
           65  +- communicate to the public, including the right to make available or display
           66  +  the Work or copies thereof to the public and perform publicly, as the case may
           67  +  be, the Work,
           68  +- distribute the Work or copies thereof,
           69  +- lend and rent the Work or copies thereof,
           70  +- sublicense rights in the Work or copies thereof.
           71  +
           72  +Those rights can be exercised on any media, supports and formats, whether now
           73  +known or later invented, as far as the applicable law permits so.
           74  +
           75  +In the countries where moral rights apply, the Licensor waives his right to
           76  +exercise his moral right to the extent allowed by law in order to make effective
           77  +the licence of the economic rights here above listed.
           78  +
           79  +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
           80  +any patents held by the Licensor, to the extent necessary to make use of the
           81  +rights granted on the Work under this Licence.
           82  +
           83  +3. Communication of the Source Code
           84  +
           85  +The Licensor may provide the Work either in its Source Code form, or as
           86  +Executable Code. If the Work is provided as Executable Code, the Licensor
           87  +provides in addition a machine-readable copy of the Source Code of the Work
           88  +along with each copy of the Work that the Licensor distributes or indicates, in
           89  +a notice following the copyright notice attached to the Work, a repository where
           90  +the Source Code is easily and freely accessible for as long as the Licensor
           91  +continues to distribute or communicate the Work.
           92  +
           93  +4. Limitations on copyright
           94  +
           95  +Nothing in this Licence is intended to deprive the Licensee of the benefits from
           96  +any exception or limitation to the exclusive rights of the rights owners in the
           97  +Work, of the exhaustion of those rights or of other applicable limitations
           98  +thereto.
           99  +
          100  +5. Obligations of the Licensee
          101  +
          102  +The grant of the rights mentioned above is subject to some restrictions and
          103  +obligations imposed on the Licensee. Those obligations are the following:
          104  +
          105  +Attribution right: The Licensee shall keep intact all copyright, patent or
          106  +trademarks notices and all notices that refer to the Licence and to the
          107  +disclaimer of warranties. The Licensee must include a copy of such notices and a
          108  +copy of the Licence with every copy of the Work he/she distributes or
          109  +communicates. The Licensee must cause any Derivative Work to carry prominent
          110  +notices stating that the Work has been modified and the date of modification.
          111  +
          112  +Copyleft clause: If the Licensee distributes or communicates copies of the
          113  +Original Works or Derivative Works, this Distribution or Communication will be
          114  +done under the terms of this Licence or of a later version of this Licence
          115  +unless the Original Work is expressly distributed only under this version of the
          116  +Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
          117  +(becoming Licensor) cannot offer or impose any additional terms or conditions on
          118  +the Work or Derivative Work that alter or restrict the terms of the Licence.
          119  +
          120  +Compatibility clause: If the Licensee Distributes or Communicates Derivative
          121  +Works or copies thereof based upon both the Work and another work licensed under
          122  +a Compatible Licence, this Distribution or Communication can be done under the
          123  +terms of this Compatible Licence. For the sake of this clause, ‘Compatible
          124  +Licence’ refers to the licences listed in the appendix attached to this Licence.
          125  +Should the Licensee's obligations under the Compatible Licence conflict with
          126  +his/her obligations under this Licence, the obligations of the Compatible
          127  +Licence shall prevail.
          128  +
          129  +Provision of Source Code: When distributing or communicating copies of the Work,
          130  +the Licensee will provide a machine-readable copy of the Source Code or indicate
          131  +a repository where this Source will be easily and freely available for as long
          132  +as the Licensee continues to distribute or communicate the Work.
          133  +
          134  +Legal Protection: This Licence does not grant permission to use the trade names,
          135  +trademarks, service marks, or names of the Licensor, except as required for
          136  +reasonable and customary use in describing the origin of the Work and
          137  +reproducing the content of the copyright notice.
          138  +
          139  +6. Chain of Authorship
          140  +
          141  +The original Licensor warrants that the copyright in the Original Work granted
          142  +hereunder is owned by him/her or licensed to him/her and that he/she has the
          143  +power and authority to grant the Licence.
          144  +
          145  +Each Contributor warrants that the copyright in the modifications he/she brings
          146  +to the Work are owned by him/her or licensed to him/her and that he/she has the
          147  +power and authority to grant the Licence.
          148  +
          149  +Each time You accept the Licence, the original Licensor and subsequent
          150  +Contributors grant You a licence to their contributions to the Work, under the
          151  +terms of this Licence.
          152  +
          153  +7. Disclaimer of Warranty
          154  +
          155  +The Work is a work in progress, which is continuously improved by numerous
          156  +Contributors. It is not a finished work and may therefore contain defects or
          157  +‘bugs’ inherent to this type of development.
          158  +
          159  +For the above reason, the Work is provided under the Licence on an ‘as is’ basis
          160  +and without warranties of any kind concerning the Work, including without
          161  +limitation merchantability, fitness for a particular purpose, absence of defects
          162  +or errors, accuracy, non-infringement of intellectual property rights other than
          163  +copyright as stated in Article 6 of this Licence.
          164  +
          165  +This disclaimer of warranty is an essential part of the Licence and a condition
          166  +for the grant of any rights to the Work.
          167  +
          168  +8. Disclaimer of Liability
          169  +
          170  +Except in the cases of wilful misconduct or damages directly caused to natural
          171  +persons, the Licensor will in no event be liable for any direct or indirect,
          172  +material or moral, damages of any kind, arising out of the Licence or of the use
          173  +of the Work, including without limitation, damages for loss of goodwill, work
          174  +stoppage, computer failure or malfunction, loss of data or any commercial
          175  +damage, even if the Licensor has been advised of the possibility of such damage.
          176  +However, the Licensor will be liable under statutory product liability laws as
          177  +far such laws apply to the Work.
          178  +
          179  +9. Additional agreements
          180  +
          181  +While distributing the Work, You may choose to conclude an additional agreement,
          182  +defining obligations or services consistent with this Licence. However, if
          183  +accepting obligations, You may act only on your own behalf and on your sole
          184  +responsibility, not on behalf of the original Licensor or any other Contributor,
          185  +and only if You agree to indemnify, defend, and hold each Contributor harmless
          186  +for any liability incurred by, or claims asserted against such Contributor by
          187  +the fact You have accepted any warranty or additional liability.
          188  +
          189  +10. Acceptance of the Licence
          190  +
          191  +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
          192  +placed under the bottom of a window displaying the text of this Licence or by
          193  +affirming consent in any other similar way, in accordance with the rules of
          194  +applicable law. Clicking on that icon indicates your clear and irrevocable
          195  +acceptance of this Licence and all of its terms and conditions.
          196  +
          197  +Similarly, you irrevocably accept this Licence and all of its terms and
          198  +conditions by exercising any rights granted to You by Article 2 of this Licence,
          199  +such as the use of the Work, the creation by You of a Derivative Work or the
          200  +Distribution or Communication by You of the Work or copies thereof.
          201  +
          202  +11. Information to the public
          203  +
          204  +In case of any Distribution or Communication of the Work by means of electronic
          205  +communication by You (for example, by offering to download the Work from a
          206  +remote location) the distribution channel or media (for example, a website) must
          207  +at least provide to the public the information requested by the applicable law
          208  +regarding the Licensor, the Licence and the way it may be accessible, concluded,
          209  +stored and reproduced by the Licensee.
          210  +
          211  +12. Termination of the Licence
          212  +
          213  +The Licence and the rights granted hereunder will terminate automatically upon
          214  +any breach by the Licensee of the terms of the Licence.
          215  +
          216  +Such a termination will not terminate the licences of any person who has
          217  +received the Work from the Licensee under the Licence, provided such persons
          218  +remain in full compliance with the Licence.
          219  +
          220  +13. Miscellaneous
          221  +
          222  +Without prejudice of Article 9 above, the Licence represents the complete
          223  +agreement between the Parties as to the Work.
          224  +
          225  +If any provision of the Licence is invalid or unenforceable under applicable
          226  +law, this will not affect the validity or enforceability of the Licence as a
          227  +whole. Such provision will be construed or reformed so as necessary to make it
          228  +valid and enforceable.
          229  +
          230  +The European Commission may publish other linguistic versions or new versions of
          231  +this Licence or updated versions of the Appendix, so far this is required and
          232  +reasonable, without reducing the scope of the rights granted by the Licence. New
          233  +versions of the Licence will be published with a unique version number.
          234  +
          235  +All linguistic versions of this Licence, approved by the European Commission,
          236  +have identical value. Parties can take advantage of the linguistic version of
          237  +their choice.
          238  +
          239  +14. Jurisdiction
          240  +
          241  +Without prejudice to specific agreement between parties,
          242  +
          243  +- any litigation resulting from the interpretation of this License, arising
          244  +  between the European Union institutions, bodies, offices or agencies, as a
          245  +  Licensor, and any Licensee, will be subject to the jurisdiction of the Court
          246  +  of Justice of the European Union, as laid down in article 272 of the Treaty on
          247  +  the Functioning of the European Union,
          248  +
          249  +- any litigation arising between other parties and resulting from the
          250  +  interpretation of this License, will be subject to the exclusive jurisdiction
          251  +  of the competent court where the Licensor resides or conducts its primary
          252  +  business.
          253  +
          254  +15. Applicable Law
          255  +
          256  +Without prejudice to specific agreement between parties,
          257  +
          258  +- this Licence shall be governed by the law of the European Union Member State
          259  +  where the Licensor has his seat, resides or has his registered office,
          260  +
          261  +- this licence shall be governed by Belgian law if the Licensor has no seat,
          262  +  residence or registered office inside a European Union Member State.
          263  +
          264  +Appendix
          265  +
          266  +‘Compatible Licences’ according to Article 5 EUPL are:
          267  +
          268  +- GNU General Public License (GPL) v. 2, v. 3
          269  +- GNU Affero General Public License (AGPL) v. 3
          270  +- Open Software License (OSL) v. 2.1, v. 3.0
          271  +- Eclipse Public License (EPL) v. 1.0
          272  +- CeCILL v. 2.0, v. 2.1
          273  +- Mozilla Public Licence (MPL) v. 2
          274  +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
          275  +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
          276  +  works other than software
          277  +- European Union Public Licence (EUPL) v. 1.1, v. 1.2
          278  +- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
          279  +  Reciprocity (LiLiQ-R+).
          280  +
          281  +The European Commission may update this Appendix to later versions of the above
          282  +licences without producing a new version of the EUPL, as long as they provide
          283  +the rights granted in Article 2 of this Licence and protect the covered Source
          284  +Code from exclusive appropriation.
          285  +
          286  +All other changes or additions to this Appendix require the production of a new
          287  +EUPL version.

Modified math.t from [fe958b6645] to [f642fd73ba].

    78     78   	for i = 0, len do
    79     79   		var v, ok = m.shorthand.cval(s[i])
    80     80   		if ok == false then return 0, false end
    81     81   		val = (val * 64) + v
    82     82   	end
    83     83   	return val, true
    84     84   end
           85  +
           86  +terra m.hexdigit(hb: uint8): int8
           87  +	var a = hb and 0x0F
           88  +	if a < 10 then return 0x30 + a
           89  +	else return 0x61 + (a-10) end
           90  +end
           91  +
           92  +terra m.hexbyte(b: uint8): int8[2]
           93  +	return array(m.hexdigit((b and 0xF0) >> 4), m.hexdigit(b and 0x0F))
           94  +end
           95  +
           96  +terra m.hexstr(src: &uint8, str: rawstring, sz: intptr)
           97  +	for i = 0, sz do
           98  +		var d = m.hexbyte(src[i])
           99  +		str[i*2] = d[0]
          100  +		str[i*2 + 1] = d[1]
          101  +	end
          102  +end
          103  +
          104  +terra m.b32char(v: uint8): int8
          105  +	if v <= 25 then return 0x61 + v
          106  +	elseif v < 31 then return 0x32 + (v-26)
          107  +	else return 0 end
          108  +end
          109  +
          110  +terra m.b32(v: uint64, buf: rawstring) -- 5 bytes -> 8 chars
          111  +	while v > 0 do
          112  +		var val = v % 32
          113  +		v = v / 32
          114  +		@buf = m.b32char(val)
          115  +		buf = buf + 1
          116  +	end
          117  +end
          118  +
          119  +terra m.b32str(a: lib.mem.ptr(uint64))
          120  +	
          121  +end
    85    122   
    86    123   return m

Added mem.t version [07d2cb4a7e].

            1  +-- vim: ft=terra
            2  +local m = {
            3  +	zero = macro(function(r)
            4  +		return quote
            5  +			for i = 0, [r.tree.type.N] do r[i] = 0 end
            6  +		end
            7  +	end);
            8  +	heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
            9  +	heapr_raw = terralib.externfunction('realloc', {&opaque, intptr} -> &opaque);
           10  +	heapf = terralib.externfunction('free', &opaque -> {});
           11  +	cpy = terralib.externfunction('mempcpy',{&opaque, &opaque, intptr} -> &opaque);
           12  +}
           13  +
           14  +m.heapa = macro(function(ty, sz)
           15  +	local p = m.ptr(ty:astype())
           16  +	return `p {
           17  +		ptr = [&ty:astype()](m.heapa_raw(sizeof(ty) * sz));
           18  +		ct = sz;
           19  +	}
           20  +end)
           21  +
           22  +m.ptr = terralib.memoize(function(ty)
           23  +	local t = terralib.types.newstruct(string.format('ptr<%s>', ty))
           24  +	t.entries = {
           25  +		{'ptr', &ty};
           26  +		{'ct', intptr};
           27  +	}
           28  +	t.ptr_basetype = ty
           29  +	local recurse = false
           30  +	if ty:isstruct() then
           31  +		if ty.methods.free then recurse = true end
           32  +	end
           33  +	t.metamethods.__not = macro(function(self)
           34  +		return `self.ptr
           35  +	end)
           36  +	t.methods = {
           37  +		free = terra(self: &t): bool
           38  +			[recurse and quote
           39  +				self.ptr:free()
           40  +			end or {}]
           41  +			if self.ct > 0 then
           42  +				m.heapf(self.ptr)
           43  +				self.ct = 0
           44  +				return true
           45  +			end
           46  +			return false
           47  +		end;
           48  +		init = terra(self: &t, newct: intptr): bool
           49  +			var nv = [&ty](m.heapa_raw(sizeof(ty) * newct))
           50  +			if nv ~= nil then
           51  +				self.ptr = nv
           52  +				self.ct = newct
           53  +				return true
           54  +			else return false end
           55  +		end;
           56  +		resize = terra(self: &t, newct: intptr): bool
           57  +			var nv: &ty
           58  +			if self.ct > 0
           59  +				then nv = [&ty](m.heapr_raw(self.ptr, sizeof(ty) * newct))
           60  +				else nv = [&ty](m.heapa_raw(sizeof(ty) * newct))
           61  +			end
           62  +			if nv ~= nil then
           63  +				self.ptr = nv
           64  +				self.ct = newct
           65  +				return true
           66  +			else return false end
           67  +		end;
           68  +	}
           69  +	return t
           70  +end)
           71  +
           72  +m.vec = terralib.memoize(function(ty)
           73  +	local v = terralib.types.newstruct(string.format('vec<%s>', ty.name))
           74  +	v.entries = {
           75  +		{field = 'storage', type = m.ptr(ty)};
           76  +		{field = 'sz', type = intptr};
           77  +		{field = 'run', type = intptr};
           78  +	}
           79  +	local terra biggest(a: intptr, b: intptr)
           80  +		if a > b then return a else return b end
           81  +	end
           82  +	terra v:assure(n: intptr)
           83  +		if self.storage.ct < n then
           84  +			self.storage:resize(biggest(n, self.storage.ct + self.run))
           85  +		end
           86  +	end
           87  +	v.methods = {
           88  +		init = terra(self: &v, run: intptr): bool
           89  +			if not self.storage:init(run) then return false end
           90  +			self.run = run
           91  +			self.sz = 0
           92  +			return true
           93  +		end;
           94  +		new = terra(self: &v): &ty
           95  +			self:assure(self.sz + 1)
           96  +			self.sz = self.sz + 1
           97  +			return self.storage.ptr + (self.sz - 1)
           98  +		end;
           99  +		push = terra(self: &v, val: ty)
          100  +			self:assure(self.sz + 1)
          101  +			self.storage.ptr[self.sz] = val
          102  +			self.sz = self.sz + 1
          103  +		end;
          104  +		free = terra(self: &v) self.storage:free() end;
          105  +		last = terra(self: &v, idx: intptr): &ty
          106  +			if self.sz > idx then
          107  +				return self.storage.ptr + (self.sz - (idx+1))
          108  +			else lib.bail('vector underrun!') end
          109  +		end;
          110  +		crush = terra(self: &v)
          111  +			self.storage:resize(self.sz)
          112  +			return self.storage
          113  +		end;
          114  +	}
          115  +	v.metamethods.__apply = terra(self: &v, idx: intptr): &ty -- no index??
          116  +		if self.sz > idx then
          117  +			return self.storage.ptr + idx
          118  +		else lib.bail('vector overrun!') end
          119  +	end
          120  +	return v 
          121  +end)
          122  +
          123  +return m

Modified parsav.md from [d4a5d691bd] to [93a3706cc3].

     1      1   # parsav
     2      2   
     3      3   **parsav** is a lightweight fediverse server
     4      4   
            5  +## backends
            6  +parsav is designed to be storage-agnostic, and can draw data from multiple backends at a time. backends can be enabled or disabled at compile time to avoid unnecessary dependencies.
            7  +
            8  +* postgresql
            9  +
     5     10   ## dependencies
     6     11   
     7         -* libhttp
           12  +* mongoose
     8     13   * json-c
     9     14   * mbedtls
    10         -* postgresql-libs
           15  +* **postgresql backend:**
           16  +  * postgresql-libs 
    11     17   
    12     18   ## building
    13     19   
    14         -first, either install any missing dependencies as shared libraries, or build them as static libraries as described below:
           20  +first, either install any missing dependencies as shared libraries, or build them as static libraries with the command `make dep.$LIBRARY`. as a shortcut, `make dep` will build all dependencies as static libraries. note that if the build system finds a static version of a librari in the `lib/` folder, it will use that instead of any system library.
           21  +
           22  +postgresql-libs must be installed systemwide, as `parsav` does not currently provide for statically compiling and linking it
           23  +
           24  +## configuring
           25  +
           26  +the `parsav` configuration is comprised of two components: the backends list and the config store. the backends list is a simple text file that tells `parsav` which data sources to draw from. the config store is a key-value store which contains the rest of the server's configuration, and is loaded from the backends. the configuration store can be spread across the backends; backends will be checked for configuration keys according to the order in which they are listed. changes to the configuration store affect parsav in real time; you only need to restart the server if you make a change to the backend list.
           27  +
           28  +eventually, we'll add a command-line tool `parsav-cfg` to enable easy modification of the configuration store from the command line; for now, you'll need to modify the database by hand or use the online administration menu. the schema.sql file contains commands to prompt for various important values like the name of your administrative user.
           29  +
           30  +by default, parsav looks for a file called `backend.conf` in the current directory when it is launched. you can override this default with the `parsav_backend_file` environment or with the `-b`/`--backend-file` flag. `backend.conf` lists one backend per line, in the form `id type confstring`. for instance, if you had two postgresql databases, you might write a backend file like
           31  +
           32  +    master   pgsql   host=localhost dbname=parsav
           33  +	tweets   pgsql   host=420.69.dread.cloud dbname=content
           34  +
           35  +the form the configuration string takes depends on the specific backend.
           36  +
           37  +### postgresql backend
           38  +
           39  +currently, postgres needs to be configured manually before parsav can make use of it to store data. the first step is to create a database for parsav's use. once you've done that, you need to create the database schema with the command `$ psql (-h $host) -d $database -f schema.sql`. you'll be prompted for some crucial settings to install in the configuration store, such as the name of the relation you want to use for authentication (we'll call it `parsav_auth` from here on out).
           40  +
           41  +parsav separates the storage of user credentials from the storage of other user data, in order to facilitate centralized user accounting. you don't need to take advantage of this feature, and if you don't want to, you can just create a `parsav_auth` table and have done. however, `parsav_auth` can also be a view, collecting a list of authorized users and their various credentials from whatever source you please.
           42  +
           43  +`parsav_auth` has the following schema:
           44  +
           45  +    create table parsav_auth (
           46  +		aid bigint primary key,
           47  +		uid bigint,
           48  +		newname text,
           49  +		kind text not null,
           50  +		cred bytea not null,
           51  +		restrict text[],
           52  +		netmask cidr,
           53  +		blacklist bool
           54  +    )
           55  +
           56  +`aid` is a unique value identifying the authentication method. it must be deterministic -- values based on time of creation or a hash of `uid`+`kind`+`cred` are ideal. `uid` is the identifier of the user the row specifies credentials for. `kind` is a string indicating the credential type, and `cred` is the content of that credential.for the meaning of these fields and use of this structure, see **authentication** below.
           57  +
           58  +## authentication 
           59  +in the most basic case, an authentication record would be something like `{uid = 123, kind = "pw-sha512", cred = "12bf90…a10e"}`. but `parsav` is not restricted to username-password authentication, and in addition to various hashing styles, it also will support more esoteric forms of authentcation. any individual user can have as many auth rows as she likes. there is also a `restrict` field, which is normally null, but can be specified in order to restrict a particular credential to certain operations, such as posting tweets or updating a bio. `blacklist` indicates that any attempt to authenticate that matches this row will be denied, regardless of whether it matches other rows. if `netmask` is present, this authentication will only succeed if it comes from the specified IP mask.
           60  +
           61  +`uid` can also be `0` (not null, which matches any user!), indicating that there is not yet a record in `parsav_actors` for this account. if this is the case, `name` must contain the handle of the account to be created when someone attempts to log in with this credential. whether `name` is used in the authentication process depends on whether the authentication method accepts a username. all rows with the same `uid` *must* have the same `name`.
           62  +
           63  +below is a full list of authentication types we intend to support. a checked box indicates the scheme has been implemented.
           64  +
           65  +* ☑ pw-sha{512,384,256,224}: an ordinary password, hashed with the appropriate algorithm
           66  +* ☐ pw-{sha1,md5,clear} (insecure, must be manually enabled at compile time with the config variable `parsav_let_me_be_a_dumbass="i know what i'm doing"`)
           67  +* ☐ pw-pbkdf2-hmac-sha{…}: a password hashed with the Password-Based Key Derivation Function 2 instead of plain SHA2
           68  +* ☐ api-digest-sha{…}: a value that can be hashed with the current epoch to derive a temporary access key without logging in. these are used for API calls, sent in the header `X-API-Key`.
           69  +* ☐ otp-time-sha1: a TOTP PSK: the first two bytes represent the step, the third byte the OTP length, and the remaining ten bytes the secret key
           70  +* ☐ tls-cert-fp: a fingerprint of a client certificate
           71  +* ☐ tls-cert-ca: a value of the form `fp/key=value` where a client certificate with the property `key=value` (e.g. `uid=cyberlord19`) signed by a certificate authority matching the given fingerprint `fp` can authenticate the user
           72  +* ☐ challenge-rsa-sha256: an RSA public key. the user is presented with a challenge and must sign it with the corresponding private key using SHA256.
           73  +* ☐ challenge-ecc-sha256: a Curve25519 public key. the user is presented with a challenge and must sign it with the corresponding private key using SHA256.
           74  +* ☐ challenge-ecc448-sha256: a Curve448 public key. the user is presented with a challenge and must sign it with the corresponding private key using SHA256.
           75  +* ☑ trust: authentication always succeeds. only use in combination with netmask!!!
           76  +
           77  +## license
           78  +
           79  +parsav is released under the terms of the EUPL v1.2. copies of this license are included in the repository. dependencies are produced
           80  +
           81  +## future direction
    15     82   
    16         -* libhttp: run `$ make lib/libhttp/lib/libhttp.a`
    17         -* json-c (deps: `cmake`): run `$ make lib/json-c/libjson-c.a`
    18         -* mbedtls: run `$ make lib/mbedtls/lib/mbed{crypto,tls,x509}.a`
           83  +parsav needs more storage backends, as it currently supports only postgres. some possibilities, in order of priority, are:
    19     84   
    20         -you can install static libraries for all dependencies with `$ make dep`, but this is recommended only if you have none of the above
           85  +* plain text/filesystem storage
           86  +* lmdb
           87  +* sqlite3
           88  +* generic odbc
           89  +* lua
           90  +* ldap?? possibly just for users
           91  +* cdb (for static content, maybe?)
           92  +* mariadb/mysql
           93  +* the various nosql horrors, e.g. redis, mongo, and so on

Modified parsav.t from [ce122c09dc] to [fcc06cebed].

     2      2   
     3      3   local util = dofile('common.lua')
     4      4   local buildopts, buildargs = util.parseargs{...}
     5      5   config = dofile('config.lua')
     6      6   
     7      7   lib = {
     8      8   	init = {};
            9  +	load = function(lst)
           10  +		for _, l in pairs(lst) do
           11  +			lib[l] = terralib.loadfile(l .. '.t')()
           12  +		end
           13  +	end;
     9     14   	loadlib = function(name,hdr)
    10     15   		local p = config.pkg[name]
    11     16   		-- for _,v in pairs(p.dylibs) do
    12     17   		-- 	terralib.linklibrary(p.libdir .. '/' .. v)
    13     18   		-- end
    14     19   		return terralib.includec(p.incdir .. '/' .. hdr)
    15     20   	end;
................................................................................
    17     22   		return macro(function(v,...)
    18     23   			for ty,fn in pairs(tbl) do
    19     24   				if v.tree.type == ty then return fn(v,...) end
    20     25   			end
    21     26   			return (tbl[false])(v,...)
    22     27   		end)
    23     28   	end;
    24         -	emit = function(...)
           29  +	emit_unitary = function(fd,...)
    25     30   		local code = {}
    26     31   		for i,v in ipairs{...} do
    27     32   			if type(v) == 'string' or type(v) == 'number' then
    28     33   				local str = tostring(v)
    29     34   				code[#code+1] = `lib.io.send(2, str, [#str])
           35  +			elseif type(v) == 'table' and #v == 2 then
           36  +				code[#code+1] = `lib.io.send(2, [v[1]], [v[2]])
    30     37   			elseif v.tree:is 'constant' then
    31     38   				local str = tostring(v:asvalue())
    32     39   				code[#code+1] = `lib.io.send(2, str, [#str])
    33     40   			else
    34     41   				code[#code+1] = quote var n = v in
    35     42   					lib.io.send(2, n, lib.str.sz(n)) end
    36     43   			end
    37     44   		end
    38         -		code[#code+1] = `lib.io.send(2, '\n', 1)
           45  +		code[#code+1] = `lib.io.send(fd, '\n', 1)
    39     46   		return code
    40     47   	end;
           48  +	emitv = function(fd,...)
           49  +		local vec = {}
           50  +		local defs = {}
           51  +		for i,v in ipairs{...} do
           52  +			local str, ct
           53  +			if type(v) == 'table' and v.tree and not (v.tree:is 'constant') then
           54  +				if v.tree.type.convertible == 'tuple' then
           55  +					str = `v._0
           56  +					ct = `v._1
           57  +				else
           58  +					local n = symbol(v.tree.type)
           59  +					defs[#defs + 1] = quote var [n] = v end
           60  +					str = n
           61  +					ct = `lib.str.sz(n)
           62  +				end
           63  +			else
           64  +				if type(v) == 'string' or type(v) == 'number' then
           65  +					str = tostring(v) 
           66  +				else--if v.tree:is 'constant' then
           67  +					str = tostring(v:asvalue())
           68  +				end
           69  +				ct = ct or #str
           70  +			end
           71  +			vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque](str), iov_len = ct}
           72  +		end
           73  +		vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque]('\n'), iov_len = 1}
           74  +		return quote
           75  +			[defs]
           76  +			var strs = array( [vec] )
           77  +		in lib.uio.writev(fd, strs, [#vec]) end
           78  +	end;
    41     79   	trn = macro(function(cond, i, e)
    42     80   		return quote
    43     81   			var c: bool = [cond]
    44     82   			var r: i.tree.type
    45     83   			if c == true then r = i else r = e end
    46     84   		in r end
    47     85   	end);
................................................................................
    52     90   	io = {
    53     91   		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
    54     92   		recv = terralib.externfunction('read',  {int, rawstring, intptr} -> ptrdiff);
    55     93   		say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
    56     94   		fmt = terralib.externfunction('printf',
    57     95   			terralib.types.funcpointer({rawstring},{int},true));
    58     96   	};
    59         -	str = {
    60         -		sz = terralib.externfunction('strlen', rawstring -> intptr);
    61         -		cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
    62         -		ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int);
    63         -		cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
    64         -		ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
    65         -		ndup = terralib.externfunction('strndup',{rawstring, intptr} -> rawstring);
    66         -		fmt = terralib.externfunction('asprintf',
    67         -			terralib.types.funcpointer({&rawstring},{int},true));
    68         -	};
           97  +	str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };
    69     98   	copy = function(tbl)
    70     99   		local new = {}
    71    100   		for k,v in pairs(tbl) do new[k] = v end
    72    101   		setmetatable(new, getmetatable(tbl))
    73    102   		return new
    74    103   	end;
    75         -	mem = {
    76         -		zero = macro(function(r)
    77         -			return quote
    78         -				for i = 0, [r.tree.type.N] do r[i] = 0 end
    79         -			end
    80         -		end);
    81         -		heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
    82         -		heapr_raw = terralib.externfunction('realloc', {&opaque, intptr} -> &opaque);
    83         -		heapf = terralib.externfunction('free', &opaque -> {});
    84         -		cpy = terralib.externfunction('mempcpy',{&opaque, &opaque, intptr} -> &opaque);
    85         -		heapa = macro(function(ty, sz)
    86         -			local p = lib.mem.ptr(ty:astype())
    87         -			return `p {
    88         -				ptr = [&ty:astype()](lib.mem.heapa_raw(sizeof(ty) * sz));
    89         -				ct = sz;
    90         -			}
    91         -		end)
    92         -	};
    93    104   }
          105  +if config.posix then
          106  +	lib.uio = terralib.includec 'sys/uio.h';
          107  +	lib.emit = lib.emitv -- use more efficient call where available
          108  +else lib.emit = lib.emit_unitary end
    94    109   
    95    110   local noise = global(uint8,1)
    96    111   local noise_header = function(code,txt,mod)
    97    112   	if mod then
    98    113   		return string.format('\27[%s;1m(parsav::%s %s)\27[m ', code,mod,txt)
    99    114   	else
   100    115   		return string.format('\27[%s;1m(parsav %s)\27[m ', code,txt)
   101    116   	end
   102    117   end
   103    118   local defrep = function(level,n,code)
   104    119   	return macro(function(...)
   105         -		local q = lib.emit(noise_header(code,n), ...)
          120  +		local q = lib.emit(2, noise_header(code,n), ...)
   106    121   		return quote
   107    122   			if noise >= level then [q] end
   108    123   		end
   109    124   	end);
   110    125   end
   111    126   lib.dbg = defrep(3,'debug', '32')
   112    127   lib.report = defrep(2,'info', '35')
   113    128   lib.warn = defrep(1,'warn', '33')
   114    129   lib.bail = macro(function(...)
   115         -	local q = lib.emit(noise_header('31','fatal'), ...)
          130  +	local q = lib.emit(2, noise_header('31','fatal'), ...)
   116    131   	return quote
   117    132   		[q]
   118    133   		lib.proc.exit(1)
   119    134   	end
   120    135   end);
   121    136   lib.stat = terralib.memoize(function(ty)
   122    137   	local n = struct {
................................................................................
   137    152   	elseif #tbl >= 2^8 then ty = uint16 end
   138    153   	local o = { t = ty }
   139    154   	for i, name in ipairs(tbl) do
   140    155   		o[name] = i
   141    156   	end
   142    157   	return o
   143    158   end
   144         -lib.mem.ptr = terralib.memoize(function(ty)
   145         -	local t = terralib.types.newstruct(string.format('ptr<%s>', ty))
   146         -	t.entries = {
   147         -		{'ptr', &ty};
   148         -		{'ct', intptr};
   149         -	}
   150         -	t.ptr_basetype = ty
   151         -	local recurse = false
   152         -	if ty:isstruct() then
   153         -		if ty.methods.free then recurse = true end
   154         -	end
   155         -	t.methods = {
   156         -		free = terra(self: &t): bool
   157         -			[recurse and quote
   158         -				self.ptr:free()
   159         -			end or {}]
   160         -			if self.ct > 0 then
   161         -				lib.mem.heapf(self.ptr)
   162         -				self.ct = 0
   163         -				return true
          159  +lib.set = function(tbl)
          160  +	local bytes = math.ceil(#tbl / 8)
          161  +	local o = {}
          162  +	for i, name in ipairs(tbl) do o[name] = i end
          163  +	local struct set { _store: uint8[bytes] }
          164  +	local struct bit { _v: intptr _set: &set}
          165  +	terra set:clear() for i=0,bytes do self._store[i] = 0 end end
          166  +	set.members = tbl
          167  +	set.name = string.format('set<%s>', table.concat(tbl, '|'))
          168  +	set.metamethods.__entrymissing = macro(function(val, obj)
          169  +		if o[val] == nil then error('value ' .. val .. ' not in set') end
          170  +		return `bit { _v=[o[val] - 1], _set = &obj }
          171  +	end)
          172  +	set.methods.dump = macro(function(self)
          173  +		local q = quote lib.io.say('dumping set:\n') end
          174  +		for i,v in ipairs(tbl) do
          175  +			q = quote
          176  +				[q]
          177  +				if [bool](self.[v])
          178  +					then lib.io.say([' - ' .. v .. ': true\n'])
          179  +					else lib.io.say([' - ' .. v .. ': false\n'])
          180  +				end
   164    181   			end
   165         -			return false
   166         -		end;
   167         -		init = terra(self: &t, newct: intptr): bool
   168         -			var nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
   169         -			if nv ~= nil then
   170         -				self.ptr = nv
   171         -				self.ct = newct
   172         -				return true
   173         -			else return false end
   174         -		end;
   175         -		resize = terra(self: &t, newct: intptr): bool
   176         -			var nv: &ty
   177         -			if self.ct > 0
   178         -				then nv = [&ty](lib.mem.heapr_raw(self.ptr, sizeof(ty) * newct))
   179         -				else nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
          182  +		end
          183  +		return q
          184  +	end)
          185  +	set.metamethods.__add = macro(function(self,other)
          186  +		local new = symbol(set)
          187  +		local q = quote var [new] new:clear() end
          188  +		for i = 0, bytes - 1 do
          189  +			q = quote [q]
          190  +				new._store[i] = self._store[i] or other._store[i]
   180    191   			end
   181         -			if nv ~= nil then
   182         -				self.ptr = nv
   183         -				self.ct = newct
   184         -				return true
   185         -			else return false end
   186         -		end;
   187         -	}
   188         -	return t
   189         -end)
   190         -lib.mem.vec = terralib.memoize(function(ty)
   191         -	local v = terralib.types.newstruct(string.format('vec<%s>', ty.name))
   192         -	v.entries = {
   193         -		{field = 'storage', type = lib.mem.ptr(ty)};
   194         -		{field = 'sz', type = intptr};
   195         -		{field = 'run', type = intptr};
   196         -	}
   197         -	local terra biggest(a: intptr, b: intptr)
   198         -		if a > b then return a else return b end
          192  +		end
          193  +		return quote [q] in new end
          194  +	end)
          195  +	bit.metamethods.__cast = function(from,to,e)
          196  +		local q = quote var s = e
          197  +			in (s._set._store[s._v/8] and (1 << s._v % 8)) end
          198  +		if to == bit then error('casting to bit is not meaningful')
          199  +		elseif to == bool then return `([q] ~= 0)
          200  +		elseif to:isintegral() then return q
          201  +		elseif from == bit then error('cannot cast bit to ' .. tostring(to))
          202  +		else return nil end
   199    203   	end
   200         -	terra v:assure(n: intptr)
   201         -		if self.storage.ct < n then
   202         -			self.storage:resize(biggest(n, self.storage.ct + self.run))
          204  +	bit.metamethods.__apply = terra(self: &bit): bool return @self end
          205  +	bit.metamethods.__lshift = terra(self: &bit, hl: bool)
          206  +		var byte = self._v / 8
          207  +		var bit = self._v % 8
          208  +		if hl then
          209  +			self._set._store[byte] = self._set._store[byte] or (1 << bit)
          210  +		else
          211  +			self._set._store[byte] = self._set._store[byte] and not (1 << bit)
   203    212   		end
   204    213   	end
   205         -	v.methods = {
   206         -		init = terra(self: &v, run: intptr): bool
   207         -			if not self.storage:init(run) then return false end
   208         -			self.run = run
   209         -			self.sz = 0
   210         -			return true
   211         -		end;
   212         -		new = terra(self: &v): &ty
   213         -			self:assure(self.sz + 1)
   214         -			self.sz = self.sz + 1
   215         -			return self.storage.ptr + (self.sz - 1)
   216         -		end;
   217         -		push = terra(self: &v, val: ty)
   218         -			self:assure(self.sz + 1)
   219         -			self.storage.ptr[self.sz] = val
   220         -			self.sz = self.sz + 1
   221         -		end;
   222         -		free = terra(self: &v) self.storage:free() end;
   223         -		last = terra(self: &v, idx: intptr): &ty
   224         -			if self.sz > idx then
   225         -				return self.storage.ptr + (self.sz - (idx+1))
   226         -			else lib.bail('vector underrun!') end
   227         -		end;
   228         -		crush = terra(self: &v)
   229         -			self.storage:resize(self.sz)
   230         -			return self.storage
   231         -		end;
   232         -	}
   233         -	v.metamethods.__apply = terra(self: &v, idx: intptr): &ty -- no index??
   234         -		if self.sz > idx then
   235         -			return self.storage.ptr + idx
   236         -		else lib.bail('vector overrun!') end
   237         -	end
   238         -	return v 
   239         -end)
          214  +	return set
          215  +end
   240    216   
   241    217   lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
   242    218   lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
   243    219   lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
   244    220   lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
   245    221   lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
   246    222   lib.net = lib.loadlib('mongoose','mongoose.h')
   247    223   lib.pq = lib.loadlib('libpq','libpq-fe.h')
   248         -lib.file = terralib.loadfile('file.t')()
   249         -lib.math = terralib.loadfile('math.t')()
   250         -lib.crypt = terralib.loadfile('crypt.t')()
   251         -lib.http = terralib.loadfile('http.t')()
   252         -lib.tpl = terralib.loadfile('tpl.t')()
   253         -lib.string = terralib.loadfile('string.t')()
   254         -lib.store = terralib.loadfile('store.t')()
          224  +
          225  +lib.load {
          226  +	'mem', 'str', 'file', 'math', 'crypt';
          227  +	'http', 'tpl', 'store';
          228  +}
   255    229   
   256    230   local be = {}
   257         -for _, b in pairs { 'pgsql' } do
          231  +for _, b in pairs(config.backends) do
   258    232   	be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
   259    233   end
   260    234   lib.store.backends = global(`array([be]))
   261    235   
   262    236   lib.cmdparse = terralib.loadfile('cmdparse.t')()
   263    237   lib.srv = terralib.loadfile('srv.t')()
   264    238   
................................................................................
   279    253   
   280    254   local pemdump = macro(function(pub, kp)
   281    255   	local msg = (pub:asvalue() and ' * emitting public key\n') or ' * emitting private key\n'
   282    256   	return quote
   283    257   		var buf: lib.crypt.pemfile
   284    258   		lib.mem.zero(buf)
   285    259   		lib.crypt.pem(pub, &kp, buf)
   286         -		lib.io.send(1, msg, [#msg])
   287         -		lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
   288         -		lib.io.send(1, '\n', 1)
          260  +		lib.emit(msg, buf, '\n')
          261  +		--lib.io.send(1, msg, [#msg])
          262  +		--lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
          263  +		--lib.io.send(1, '\n', 1)
   289    264   	end
   290    265   end)
   291    266   
   292    267   do
   293    268   	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
   294    269   	terra version() lib.io.send(1, p, [#p]) end
   295    270   end
................................................................................
   303    278   	end
   304    279   	noise = 1
   305    280   end
   306    281   
   307    282   local options = lib.cmdparse {
   308    283   	version = {'V', 'display information about the binary build and exit'};
   309    284   	quiet = {'q', 'do not print to standard out'};
   310         -	help = {'h', 'display this list'}
          285  +	help = {'h', 'display this list'};
          286  +	backend_file = {'b', 'init from specified backend file', 1};
   311    287   }
   312    288   
   313    289   terra entry(argc: int, argv: &rawstring): int
          290  +	if argc < 1 then lib.bail('bad invocation!') end
          291  +
   314    292   	noise_init()
   315    293   	[lib.init]
   316    294   
   317    295   	-- shut mongoose the fuck up
   318    296   	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)
          297  +	var srv: lib.srv
          298  +
          299  +	do var mode: options
          300  +		mode:parse(argc,argv) defer mode:free()
          301  +		if mode.version then version() return 0 end
          302  +		if mode.help then
          303  +			lib.io.send(1,  [options.helptxt], [#options.helptxt])
          304  +			return 0
          305  +		end
          306  +		var cnf: rawstring
          307  +		if mode.backend_file ~= 0
          308  +			then if mode.arglist.ct >= mode.backend_file
          309  +					then cnf = mode.arglist.ptr[mode.backend_file - 1]
          310  +					else lib.bail('bad invocation, backend file not specified') end
          311  +			else cnf = lib.proc.getenv('parsav_backend_file')
          312  +		end
          313  +		if cnf == nil then cnf = "backend.conf" end
   319    314   
   320         -	var mode: options
   321         -	mode:parse(argc,argv)
   322         -	if mode.version then
   323         -		version()
   324         -		return 0
          315  +		srv:start(cnf)
   325    316   	end
   326         -	if mode.help then
   327         -		lib.io.send(1,  [options.helptxt], [#options.helptxt])
   328         -		return 0
   329         -	end
   330         -	var srv: lib.srv
   331         -	srv:start('backend.conf')
          317  +
   332    318   	lib.report('listening for requests')
   333    319   	while true do
   334    320   		srv:poll()
   335    321   	end
   336    322   	srv:shutdown()
   337    323   
   338    324   	return 0
................................................................................
   345    331   end
   346    332   
   347    333   if bflag('dump-config','C') then
   348    334   	print(util.dump(config))
   349    335   	os.exit(0)
   350    336   end
   351    337   
   352         -local emit = print
   353         -if bflag('quiet','q') then emit = function() end end
   354         -
          338  +local holler = print
   355    339   local out = config.exe and 'parsav' or 'parsav.o'
   356    340   local linkargs = {}
          341  +
          342  +if bflag('quiet','q') then holler = function() end end
          343  +if bflag('asan','s') then linkargs[#linkargs+1] = '-fsanitize=address' end
          344  +if bflag('lsan','S') then linkargs[#linkargs+1] = '-fsanitize=leak' end
          345  +
   357    346   if config.posix then
   358    347   	linkargs[#linkargs+1] = '-pthread'
   359    348   end
   360    349   for _,p in pairs(config.pkg) do util.append(linkargs, p.linkargs) end
   361         -emit('linking with args',util.dump(linkargs))
          350  +holler('linking with args',util.dump(linkargs))
   362    351   terralib.saveobj(out, {
   363    352   		main = entry
   364    353   	},
   365    354   	linkargs,
   366    355   	config.tgttrip and terralib.newtarget {
   367    356   		Triple = config.tgttrip;
   368    357   		CPU = config.tgtcpu;
   369    358   		FloatABIHard = config.tgthf;
   370    359   	} or nil)

Modified schema.sql from [2da83887bf] to [6d12737279].

    13     13   	value text
    14     14   );
    15     15   
    16     16   insert into parsav_config (key,value) values
    17     17   	('bind',:'bind'),
    18     18   	('domain',:'domain'),
    19     19   	('auth-source',:'auth'),
    20         -	('administrator',:'admin');
           20  +	('administrator',:'admin'),
           21  +	('server-secret', encode(
           22  +			digest(int8send((2^63 * (random()*2 - 1))::bigint),
           23  +		'sha512'), 'base64'));
    21     24   
    22     25   -- note that valid ids should always > 0, as 0 is reserved for null
    23     26   -- on the client side, vastly simplifying code
    24     27   drop table if exists parsav_servers cascade;
    25     28   create table parsav_servers (
    26     29   	id bigint primary key default (1+random()*(2^63-1))::bigint,
    27     30   	domain text not null,

Modified srv.t from [350801ad24] to [aed7239c9c].

    24     24   	var fr = lib.file.open(befile, [lib.file.mode.read])
    25     25   	if fr.ok == false then
    26     26   		lib.bail('could not open configuration file ', befile)
    27     27   	end
    28     28   
    29     29   	var f = fr.val
    30     30   	var c: lib.mem.vec(lib.store.source) c:init(8)
    31         -	var text: lib.string.acc text:init(64)
           31  +	var text: lib.str.acc text:init(64)
    32     32   	do var buf: int8[64]
    33     33   		while true do
    34     34   			var ct = f:read(buf, [buf.type.N])
    35     35   			if ct == 0 then break end
    36     36   			text:push(buf, ct)
    37     37   		end
    38     38   	end
................................................................................
    92     92   	s.sources = c:crush()
    93     93   end
    94     94   
    95     95   --srv.methods.conf_set = terra(self: &srv, key: rawstring, val:rawstring)
    96     96   --	self.sources.ptr[0]:conf_set(key, val)
    97     97   --end
    98     98   
           99  +terra srv:actor_auth_how(ip: lib.store.inet, usn: rawstring)
          100  +	var cs: lib.store.credset cs:clear()
          101  +	for i=0,self.sources.ct do
          102  +		var set: lib.store.credset = self.sources.ptr[i]:actor_auth_how(ip, usn)
          103  +		cs = cs + set
          104  +	end
          105  +	return cs
          106  +end
    99    107   srv.metamethods.__methodmissing = macro(function(meth, self, ...)
   100         -	local primary, ptr, stat, simple = 0,1,2,3
          108  +	local primary, ptr, stat, simple, oid = 0,1,2,3,4
   101    109   	local tk, rt = primary
   102    110   	local expr = {...}
   103    111   	for _,f in pairs(lib.store.backend.entries) do
   104    112   		local fn = f.field or f[1]
   105    113   		local ft = f.type or f[2]
   106    114   		if fn == meth then
   107    115   			rt = ft.type.returntype
   108    116   			if rt == bool then tk = simple
          117  +			elseif rt.type == 'integer' then tk = oid
   109    118   			elseif rt.stat_basetype then tk = stat
   110    119   			elseif rt.ptr_basetype then tk = ptr end
   111    120   			break
   112    121   		end
   113    122   	end
   114    123   	
   115    124   	if tk == primary then
................................................................................
   116    125   		return `self.sources.ptr[0]:[meth]([expr])
   117    126   	else local ok, empty
   118    127   		local r = symbol(rt)
   119    128   		if tk == ptr then
   120    129   			ok = `r.ptr ~= nil
   121    130   			empty = `[rt]{ptr=nil,ct=0}
   122    131   		elseif tk == stat then
   123         -			ok = `r.ok ~= false
          132  +			ok = `r.ok == true
   124    133   			empty = `[rt]{ok=false,error=1}
   125    134   		elseif tk == simple then
   126    135   			ok = `r == true
   127    136   			empty = `false
          137  +		elseif tk == oid then
          138  +			ok = `r ~= 0
          139  +			empty = `0
   128    140   		end
   129    141   		return quote
   130    142   			var [r] = empty
   131    143   			for i=0,self.sources.ct do var src = self.sources.ptr + i
   132    144   				if src.handle ~= nil then
   133    145   					r = src:[meth]([expr])
   134    146   					if [ok] then break
................................................................................
   159    171   	elseif dbbind.ptr ~= nil then
   160    172   		bind = dbbind.ptr
   161    173   	else bind = '[::]:10917' end
   162    174   
   163    175   	lib.report('binding to ', bind)
   164    176   	lib.net.mg_mgr_init(&self.webmgr)
   165    177   	self.webcon = lib.net.mg_http_listen(&self.webmgr, bind, handle.http, nil)
   166         -	dbbind:free()
          178  +
   167    179   
          180  +	if dbbind.ptr ~= nil then dbbind:free() end
   168    181   end
   169    182   
   170    183   srv.methods.poll = terra(self: &srv)
   171    184   	lib.net.mg_mgr_poll(&self.webmgr,1000)
   172    185   end
   173    186   
   174    187   srv.methods.shutdown = terra(self: &srv)

Modified store.t from [ed1d490f12] to [2c2e954f5c].

     7      7   	};
     8      8   	notiftype = lib.enum {
     9      9   		'mention', 'like', 'rt', 'react'
    10     10   	};
    11     11   	relation = lib.enum {
    12     12   		'follow', 'mute', 'block'
    13     13   	};
           14  +	credset = lib.set {
           15  +		'pw', 'otp', 'challenge', 'trust'
           16  +	};
    14     17   }
    15     18   
    16         -local str = lib.mem.ptr(int8)
    17         -str:complete()
           19  +local str = rawstring --lib.mem.ptr(int8)
    18     20   
    19     21   struct m.source
    20     22   
    21     23   struct m.rights {
    22     24   	rank: uint16 -- lower = more powerful except 0 = regular user
    23     25   	-- creating staff automatically assigns rank immediately below you
    24     26   	quota: uint32 -- # of allowed tweets per day; 0 = no limit
................................................................................
    54     56   struct m.actor {
    55     57   	id: uint64
    56     58   	nym: str
    57     59   	handle: str
    58     60   	origin: uint64
    59     61   	bio: str
    60     62   	rights: m.rights
    61         -	key: str
           63  +	key: lib.mem.ptr(uint8)
           64  +
           65  +	xid: str
    62     66   
    63     67   	source: &m.source
    64     68   }
    65         -terra m.actor:free()
    66         -	self.nym:free()
    67         -	self.handle:free()
    68         -	self.bio:free()
    69         -	self.key:free()
    70         -end
    71     69   
    72     70   struct m.range {
    73     71   	time: bool
    74     72   	union {
    75     73   		from_time: m.timepoint
    76     74   		from_idx: uint64
    77     75   	}
................................................................................
   111    109   	kind: m.notiftype.t
   112    110   	when: uint64
   113    111   	union {
   114    112   		post: uint64
   115    113   		reaction: int8[8]
   116    114   	}
   117    115   }
          116  +
          117  +struct m.inet {
          118  +	pv: uint8 -- 0 = null, 4 = ipv4, 6 = ipv6
          119  +	union {
          120  +		v4: uint8[4]
          121  +		v6: uint8[16]
          122  +	}
          123  +	union {
          124  +		fixbits: uint8 -- for cidr
          125  +		port: uint16 -- for origin
          126  +	}
          127  +}
          128  +
          129  +terra m.inet:cidr_str()
          130  +	if self.pv == 4 then
          131  +		var maxsz = 3*4 + 3 + 1
          132  +	elseif self.pv == 6 then
          133  +		var bits = 128
          134  +		var bytes = bits / 8
          135  +		var hexchs = bytes * 2
          136  +		var segs = hexchs / 4
          137  +		var seps = segs - 1
          138  +		var maxsz = hexchs + seps + 1
          139  +	else return nil end
          140  +end
          141  +
          142  +struct m.auth {
          143  +	aid: uint64
          144  +	uid: uint64
          145  +	aname: str
          146  +	netmask: m.inet
          147  +	restrict: lib.mem.ptr(rawstring)
          148  +	blacklist: bool
          149  +}
          150  +
   118    151   
   119    152   -- backends only handle content on the local server
   120    153   struct m.backend { id: rawstring
   121    154   	open: &m.source -> &opaque
   122    155   	close: &m.source -> {}
   123    156   
   124    157   	conf_get: {&m.source, rawstring} -> lib.mem.ptr(int8)
   125    158   	conf_set: {&m.source, rawstring, rawstring} -> {}
   126    159   	conf_reset: {&m.source, rawstring} -> {}
   127    160   
   128    161   	actor_save: {&m.source, m.actor} -> bool
   129    162   	actor_create: {&m.source, m.actor} -> bool
   130         -	actor_fetch_xid: {&m.source, rawstring} -> lib.stat(m.actor)
   131         -	actor_fetch_uid: {&m.source, uint64} -> lib.stat(m.actor)
          163  +	actor_fetch_xid: {&m.source, rawstring} -> lib.mem.ptr(m.actor)
          164  +	actor_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.actor)
   132    165   	actor_notif_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.notif)
   133         -	actor_auth: {&m.source, rawstring, rawstring} -> lib.stat(m.actor)
   134         -	actor_enum: {&m.source} -> lib.mem.ptr(m.actor)
   135         -	actor_enum_local: {&m.source} -> lib.mem.ptr(m.actor)
          166  +	actor_enum: {&m.source} -> lib.mem.ptr(&m.actor)
          167  +	actor_enum_local: {&m.source} -> lib.mem.ptr(&m.actor)
          168  +
          169  +	actor_auth_how: {&m.source, m.inet, rawstring} -> m.credset
          170  +		-- returns a set of auth method categories that are available for a
          171  +		-- given user from a certain origin
          172  +			-- origin: inet
          173  +			-- handle: rawstring
          174  +	actor_auth_otp: {&m.source, m.inet, rawstring, rawstring} -> uint64
          175  +	actor_auth_pw: {&m.source, m.inet, rawstring, rawstring} -> uint64
          176  +		-- handles password-based logins against hashed passwords
          177  +			-- origin: inet
          178  +			-- handle: rawstring
          179  +			-- token:  rawstring
          180  +	actor_auth_tls:    {&m.source, m.inet, rawstring} -> uint64
          181  +		-- handles implicit authentication performed as part of an TLS connection
          182  +			-- origin: inet
          183  +			-- fingerprint: rawstring
          184  +	actor_auth_api:    {&m.source, m.inet, rawstring, rawstring} -> uint64
          185  +		-- handles API authentication
          186  +			-- origin: inet
          187  +			-- handle: rawstring
          188  +			-- key:    rawstring (X-API-Key)
          189  +	actor_auth_record_fetch: {&m.source, uint64} -> lib.mem.ptr(m.auth)
   136    190   
   137    191   	actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
   138    192   	actor_conf_int: cnf(intptr, lib.stat(intptr))
   139    193   
   140    194   	post_save: {&m.source, &m.post} -> bool
   141    195   	post_create: {&m.source, &m.post} -> bool
   142    196   	actor_post_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(m.post)

Added str.t version [4b8724b0aa].

            1  +-- vim: ft=terra
            2  +-- string.t: string classes
            3  +
            4  +local m = {
            5  +	sz = terralib.externfunction('strlen', rawstring -> intptr);
            6  +	cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
            7  +	ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int);
            8  +	cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
            9  +	ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
           10  +	dup = terralib.externfunction('strdup',rawstring -> rawstring);
           11  +	ndup = terralib.externfunction('strndup',{rawstring, intptr} -> rawstring);
           12  +	fmt = terralib.externfunction('asprintf',
           13  +		terralib.types.funcpointer({&rawstring,rawstring},{int},true));
           14  +	bfmt = terralib.externfunction('sprintf',
           15  +		terralib.types.funcpointer({rawstring,rawstring},{int},true));
           16  +}
           17  +
           18  +(lib.mem.ptr(int8)).metamethods.__cast = function(from,to,e)
           19  +	if from == &int8 then
           20  +		return `[lib.mem.ptr(int8)]{ptr = e, ct = m.sz(e)}
           21  +	elseif to == &int8 then
           22  +		return e.ptr
           23  +	end
           24  +end
           25  +
           26  +struct m.acc {
           27  +	buf: rawstring
           28  +	sz: intptr
           29  +	run: intptr
           30  +	space: intptr
           31  +}
           32  +
           33  +local terra biggest(a: intptr, b: intptr)
           34  +	if a > b then return a else return b end
           35  +end
           36  +
           37  +terra m.acc:init(run: intptr)
           38  +	lib.dbg('initializing string accumulator')
           39  +	self.buf = [rawstring](lib.mem.heapa_raw(run))
           40  +	self.run = run
           41  +	self.space = run
           42  +	self.sz = 0
           43  +	return self
           44  +end;
           45  +
           46  +terra m.acc:free()
           47  +	lib.dbg('freeing string accumulator')
           48  +	if self.buf ~= nil and self.space > 0 then
           49  +		lib.mem.heapf(self.buf)
           50  +	end
           51  +end;
           52  +
           53  +terra m.acc:crush()
           54  +	lib.dbg('crushing string accumulator')
           55  +	self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.sz))
           56  +	self.space = self.sz
           57  +	return self
           58  +end;
           59  +
           60  +terra m.acc:finalize()
           61  +	lib.dbg('finalizing string accumulator')
           62  +	self:crush()
           63  +	var pt: lib.mem.ptr(int8)
           64  +	pt.ptr = self.buf
           65  +	pt.ct = self.sz
           66  +	self.buf = nil
           67  +	self.sz = 0
           68  +	return pt
           69  +end;
           70  +
           71  +terra m.acc:push(str: rawstring, len: intptr)
           72  +	var llen = len
           73  +	if str == nil then return self end
           74  +	if str[len - 1] == 0xA then llen = llen - 1 end -- don't display newlines in debug output
           75  +	lib.dbg('pushing "',{str,llen},'" onto accumulator')
           76  +	if self.buf == nil then self:init(self.run) end
           77  +	if len == 0 then len = m.sz(str) end
           78  +	if len >= self.space - self.sz then
           79  +		self.space = self.space + biggest(self.run,len + 1)
           80  +		self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.space))
           81  +	end
           82  +	lib.mem.cpy(self.buf + self.sz, str, len)
           83  +	self.sz = self.sz + len
           84  +	self.buf[self.sz] = 0
           85  +	return self
           86  +end;
           87  +m.acc.methods.ppush = terra(self: &m.acc, str: lib.mem.ptr(int8))
           88  +	self:push(str.ptr, str.ct)            return self end;
           89  +m.acc.methods.merge = terra(self: &m.acc, str: lib.mem.ptr(int8))
           90  +	self:push(str.ptr, str.ct) str:free() return self end;
           91  +m.acc.methods.compose = macro(function(self, ...)
           92  +	local minlen = 0
           93  +	local pstrs = {}
           94  +	for i,v in ipairs{...} do
           95  +		if type(v) == 'table' then
           96  +			local gl = 16 -- guess wildly
           97  +			if v.tree and v.tree.type.convertible == 'tuple' then
           98  +				pstrs[#pstrs+1] = {str = `v._0, len = `v._1}
           99  +			elseif v.asvalue and type(v:asvalue()) == 'string' then
          100  +				local str = v:asvalue()
          101  +				pstrs[#pstrs+1] = {str = str, len = #str}
          102  +				gl = #str + 1
          103  +			elseif v.tree and v.tree.type.ptr_basetype == int8 then
          104  +				pstrs[#pstrs+1] = {str = `v.ptr, len = `v.ct}
          105  +			else pstrs[#pstrs+1] = {str = v, len = 0} end
          106  +			minlen = minlen + gl
          107  +		elseif type(v) == 'string' then 
          108  +			pstrs[#pstrs+1] = {str = v, len = #v}
          109  +			minlen = minlen + #v + 1
          110  +		else error('invalid type in compose expression') end
          111  +	end
          112  +	local call = `self:init(minlen)
          113  +	for i,v in ipairs(pstrs) do
          114  +		call = `[call]:push([v.str],[v.len])
          115  +	end
          116  +	return call
          117  +end)
          118  +m.acc.metamethods.__lshift = terralib.overloadedfunction('(<<)', {
          119  +	terra(self: &m.acc, str: rawstring)         return self: push(str,0) end;
          120  +	terra(self: &m.acc, str: lib.mem.ptr(int8)) return self:ppush(str  ) end;
          121  +})
          122  +
          123  +m.box = terralib.memoize(function(ty)
          124  +	local b = struct {
          125  +		obj: ty
          126  +		storage: int8[0]
          127  +	}
          128  +	b.name = string.format('bytebox<%s>', ty.name)
          129  +	b.methods.mk = terra(sz: intptr)
          130  +		return [&b](lib.mem.heapa_raw(sizeof(b) + sz))
          131  +	end
          132  +	terra b:free() lib.mem.heapf(self) end -- enhhhhh
          133  +	return b
          134  +end)
          135  +
          136  +m.encapsulate = function(ty, vals)
          137  +	local memreq_const = sizeof(ty)
          138  +	local ptr = symbol(&int8)
          139  +	local box = symbol(&m.box(ty))
          140  +	local memreq_exp = `0
          141  +	local copiers = {}
          142  +	for k,v in pairs(vals) do
          143  +		local ty = (`box.obj.[k]).tree.type
          144  +		local kp
          145  +		if ty.ptr_basetype then
          146  +			kp = quote [box].obj.[k] = [ty] { [ptr] = [&ty.ptr_basetype]([ptr]) } ; end
          147  +		else
          148  +			kp = quote [box].obj.[k] = [ty]([ptr]) ; end
          149  +		end
          150  +
          151  +		local cpy
          152  +		if type(v) ~= 'table' or #v ~= 2 then
          153  +			cpy = quote [kp] ; [ptr] = m.cpy(ptr, v) end
          154  +		end
          155  +		if type(v) == 'string' then
          156  +			memreq_const = memreq_const + #v + 1
          157  +		elseif type(v) == 'table' and v.tree and (v.tree.type.ptr_basetype == int8 or v.tree.type.ptr_basetype == uint8) then
          158  +			cpy = quote [kp]; [ptr] = [&int8](lib.mem.cpy([ptr], [v].ptr, [v].ct)) end
          159  +			if ty.ptr_basetype then
          160  +				cpy = quote [cpy]; [box].obj.[k].ct = [v].ct end
          161  +			end
          162  +		elseif type(v) == 'table' and v.asvalue and type(v:asvalue()) == 'string' then
          163  +			local str = tostring(v:asvalue())
          164  +			memreq_const = memreq_const + #str + 1
          165  +		elseif type(v) == 'table' and #v == 2 then
          166  +			local str,sz = v[1],v[2]
          167  +			if type(sz) == 'number' then
          168  +				memreq_const = memreq_const + sz
          169  +			elseif type(sz:asvalue()) == 'number' then
          170  +				memreq_const = memreq_const + sz:asvalue()
          171  +			else memreq_exp = `[sz] + [memreq_exp] end
          172  +
          173  +			cpy = quote [kp] ; [ptr] = [&int8](lib.mem.cpy([ptr], str, sz)) end
          174  +			if ty.ptr_basetype then
          175  +				cpy = quote [cpy]; [box].obj.[k].ct = sz end
          176  +			end
          177  +		else
          178  +			memreq_exp = `(m.sz(v) + 1) + [memreq_exp] -- make room for NUL
          179  +			if ty.ptr_basetype then
          180  +				cpy = quote [cpy]; [box].obj.[k].ct = m.sz(v) end
          181  +			end
          182  +		end
          183  +		copiers[#copiers + 1] = cpy
          184  +	end
          185  +
          186  +	return quote
          187  +		var sz: intptr = memreq_const + [memreq_exp]
          188  +		var [box] = [&m.box(ty)](lib.mem.heapa_raw(sz))
          189  +		var [ptr] = [box].storage
          190  +		[copiers]
          191  +	in [lib.mem.ptr(ty)] { ct = 1, ptr = &([box].obj) } end
          192  +end
          193  +
          194  +return m

Deleted string.t version [a0d1de486b].

     1         --- vim: ft=terra
     2         --- string.t: string classes
     3         -
     4         -local m = {}
     5         -
     6         -struct m.acc {
     7         -	buf: rawstring
     8         -	sz: intptr
     9         -	run: intptr
    10         -	space: intptr
    11         -}
    12         -
    13         -local terra biggest(a: intptr, b: intptr)
    14         -	if a > b then return a else return b end
    15         -end
    16         -
    17         -m.acc.methods = {
    18         -	init = terra(self: &m.acc, run: intptr)
    19         -		lib.dbg('initializing string accumulator')
    20         -		self.buf = [rawstring](lib.mem.heapa_raw(run))
    21         -		self.run = run
    22         -		self.space = run
    23         -		self.sz = 0
    24         -	end;
    25         -	free = terra(self: &m.acc)
    26         -		lib.dbg('freeing string accumulator')
    27         -		if self.buf ~= nil and self.space > 0 then
    28         -			lib.mem.heapf(self.buf)
    29         -		end
    30         -	end;
    31         -	crush = terra(self: &m.acc)
    32         -		lib.dbg('crushing string accumulator')
    33         -		self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.sz))
    34         -		self.space = self.sz
    35         -		return self
    36         -	end;
    37         -}
    38         -
    39         -m.acc.methods.finalize = terra(self: &m.acc)
    40         -	lib.dbg('finalizing string accumulator')
    41         -	self:crush()
    42         -	var pt: lib.mem.ptr(int8)
    43         -	pt.ptr = self.buf
    44         -	pt.ct = self.sz
    45         -	self.buf = nil
    46         -	self.sz = 0
    47         -	return pt
    48         -end;
    49         -m.acc.methods.push = terra(self: &m.acc, str: rawstring, len: intptr)
    50         -	lib.dbg('pushing "',str,'" onto accumulator')
    51         -	if self.buf == nil then self:init(self.run) end
    52         -	if len == 0 then len = lib.str.sz(str) end
    53         -	if len >= self.space - self.sz then
    54         -		self.space = self.space + biggest(self.run,len + 1)
    55         -		self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.space))
    56         -	end
    57         -	lib.mem.cpy(self.buf + self.sz, str, len)
    58         -	self.sz = self.sz + len
    59         -	self.buf[self.sz] = 0
    60         -	return self
    61         -end;
    62         -m.acc.methods.ppush = terra(self: &m.acc, str: lib.mem.ptr(int8))
    63         -	self:push(str.ptr, str.ct)            return self end;
    64         -m.acc.methods.merge = terra(self: &m.acc, str: lib.mem.ptr(int8))
    65         -	self:push(str.ptr, str.ct) str:free() return self end;
    66         -
    67         -return m