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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
46
47
48
49
50
51
52
























53
54
55
56
57
58
59
60
61
62




63
64
65
66
67
68






























69
70
71
72
73
74
75
76






















77
78
79

80
81

82
83
84
85
86
87
88
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
129
130
131
132
133
134
135
136
137
138

139
140
141
142










143
144
145
146
147





148
149
150



















































151
152
153
154
155
156
157
...
158
159
160
161
162
163
164






165
166
167
168
169
170
171
172
173
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201










202




















































































203
				where id = $1::bigint
		]];
	};

	actor_fetch_xid = {
		params = {rawstring}, sql = [[
			select a.id, a.nym, a.handle, a.origin,
			       a.bio, a.rank, a.quota, a.key,

				coalesce(s.domain,
				        (select value from parsav_config
							where key='domain' limit 1)) as domain

			from      parsav_actors  as a
			left join parsav_servers as s
................................................................................
				on a.origin = s.id

			where $1::text = (a.handle || '@' || domain) or
			      $1::text = ('@' || a.handle || '@' || domain) or
				  (a.origin is null and $1::text = ('@' || a.handle))
		]];
	};
























}

local struct pqr {
	sz: intptr
	res: &lib.pq.PGresult
}
terra pqr:free() if self.sz > 0 then lib.pq.PQclear(self.res) end end
terra pqr:null(row: intptr, col: intptr)
	return (lib.pq.PQgetisnull(self.res, row, col) == 1)
end




terra pqr:string(row: intptr, col: intptr)
	var v = lib.pq.PQgetvalue(self.res, row, col)
	var r: lib.mem.ptr(int8)
	r.ct = lib.str.sz(v)
	r.ptr = lib.str.ndup(v, r.ct)
	return r






























end
pqr.methods.int = macro(function(self, ty, row, col)
	return quote
		var i: ty:astype()
		var v = lib.pq.PQgetvalue(self.res, row, col)
		lib.math.netswap_ip(ty, v, &i)
	in i end
end)























local con = symbol(&lib.pq.PGconn)
local prep = {}

for k,q in pairs(queries) do
	local qt = (q.sql):gsub('%s+',' '):gsub('^%s*(.-)%s*$','%1')

	local stmt = 'parsavpg_' .. k
	prep[#prep + 1] = quote
		var res = lib.pq.PQprepare([con], stmt, qt, [#q.params], nil)
		defer lib.pq.PQclear(res)
		if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_COMMAND_OK then
			if res == nil then
				lib.bail('grievous error occurred preparing ',k,' statement')
................................................................................
			fixers[#fixers + 1] = quote
				--lib.io.fmt('uid=%llu(%llx)\n',[args[i]],[args[i]])
				[args[i]] = lib.math.netswap(ty, [args[i]])
			end
		end
	end

	q.exec = terra(src: &lib.store.source, [args])
		var params = arrayof([&int8], [casts])
		var params_sz = arrayof(int, [counters])
		var params_ft = arrayof(int, [ft])
		[fixers]
		var res = lib.pq.PQexecPrepared([&lib.pq.PGconn](src.handle), stmt,
			[#args], params, params_sz, params_ft, 1)
		if res == nil then
................................................................................
			return pqr {0, nil}
		else
			return pqr {ct, res}
		end
	end
end

local terra row_to_actor(r: &pqr, row: intptr): lib.store.actor
	var a = lib.store.actor {
		id = r:int(uint64, row, 0);

		nym = r:string(row, 1);
		handle = r:string(row, 2);
		bio = r:string(row, 4);
		key = r:string(row, 7);










		rights = lib.store.rights_default();
	}
	a.rights.rank = r:int(uint16, 0, 5);
	a.rights.quota = r:int(uint32, 0, 6);
	if r:null(0,3) then a.origin = 0





	else a.origin = r:int(uint64,0,3) end
	return a
end




















































local b = `lib.store.backend {
	id = "pgsql";
	open = [terra(src: &lib.store.source): &opaque
		lib.report('connecting to postgres database: ', src.string.ptr)
		var [con] = lib.pq.PQconnectdb(src.string.ptr)
		if lib.pq.PQstatus(con) ~= lib.pq.CONNECTION_OK then
................................................................................
			lib.warn('postgres backend connection failed')
			lib.pq.PQfinish(con)
			return nil
		end
		var res = lib.pq.PQexec(con, [[
			select pg_catalog.set_config('search_path', 'public', false)
		]])






		if res ~= nil then defer lib.pq.PQclear(res) end
		if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
			lib.warn('failed to secure postgres connection')
			lib.pq.PQfinish(con)
			return nil
		end

		[prep]
		return con
................................................................................
	end];
	close = [terra(src: &lib.store.source) lib.pq.PQfinish([&lib.pq.PGconn](src.handle)) end];

	conf_get = [terra(src: &lib.store.source, key: rawstring)
		var r = queries.conf_get.exec(src, key)
		if r.sz == 0 then return [lib.mem.ptr(int8)] { ptr = nil, ct = 0 } else
			defer r:free()
			return r:string(0,0)
		end
	end];
	conf_set = [terra(src: &lib.store.source, key: rawstring, val: rawstring)
		queries.conf_set.exec(src, key, val):free() end];
	conf_reset = [terra(src: &lib.store.source, key: rawstring)
		queries.conf_reset.exec(src, key):free() end];
	
	actor_fetch_uid = [terra(src: &lib.store.source, uid: uint64)
		var r = queries.actor_fetch_uid.exec(src, uid)
		if r.sz == 0 then
			return [lib.stat(lib.store.actor)] { ok = false, error = 1}
		else
			defer r:free()
			var a = [lib.stat(lib.store.actor)] { ok = true }
			a.val = row_to_actor(&r, 0)
			a.val.source = src
			return a
		end
	end];
}































































































return b







|







 







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










>
>
>
>
|

|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








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



>

<
>







 







|







 







|
|
|
>
|
|
|
|
>
>
>
>
>
>
>
>
>
>
|
<
|
|
|
>
>
>
>
>
|


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







 







>
>
>
>
>
>
|
|







 







|










|
<
|
<
|
|



|
>
>
>
>
>
>
>
>
>
>

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

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235

236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
...
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345

346

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
				where id = $1::bigint
		]];
	};

	actor_fetch_xid = {
		params = {rawstring}, sql = [[
			select a.id, a.nym, a.handle, a.origin,
			       a.bio, a.rank, a.quota, a.key, $1::text,

				coalesce(s.domain,
				        (select value from parsav_config
							where key='domain' limit 1)) as domain

			from      parsav_actors  as a
			left join parsav_servers as s
................................................................................
				on a.origin = s.id

			where $1::text = (a.handle || '@' || domain) or
			      $1::text = ('@' || a.handle || '@' || domain) or
				  (a.origin is null and $1::text = ('@' || a.handle))
		]];
	};

	actor_enum_local = {
		params = {}, sql = [[
			select id, nym, handle, origin,
			       bio, rank, quota, key,
				handle ||'@'||
				(select value from parsav_config
					where key='domain' limit 1) as xid
			from parsav_actors where origin is null
		]];
	};

	actor_enum = {
		params = {}, sql = [[
			select a.id, a.nym, a.handle, a.origin,
			       a.bio, a.rank, a.quota, a.key,
				a.handle ||'@'||
				coalesce(s.domain,
				        (select value from parsav_config
							where key='domain' limit 1)) as xid
			from parsav_actors a
			left join parsav_servers s on s.id = a.origin
		]];
	};
}

local struct pqr {
	sz: intptr
	res: &lib.pq.PGresult
}
terra pqr:free() if self.sz > 0 then lib.pq.PQclear(self.res) end end
terra pqr:null(row: intptr, col: intptr)
	return (lib.pq.PQgetisnull(self.res, row, col) == 1)
end
terra pqr:len(row: intptr, col: intptr)
	return lib.pq.PQgetlength(self.res, row, col)
end
terra pqr:cols() return lib.pq.PQnfields(self.res) end
terra pqr:string(row: intptr, col: intptr) -- not to be exported!!
	var v = lib.pq.PQgetvalue(self.res, row, col)
--	var r: lib.mem.ptr(int8)
--	r.ct = lib.str.sz(v)
--	r.ptr = v
	return v
end
terra pqr:bin(row: intptr, col: intptr) -- not to be exported!! DO NOT FREE
	return [lib.mem.ptr(uint8)] {
		ptr = [&uint8](lib.pq.PQgetvalue(self.res, row, col));
		ct = lib.pq.PQgetlength(self.res, row, col);
	}
end
terra pqr:String(row: intptr, col: intptr) -- suitable to be exported
	var s = [lib.mem.ptr(int8)] { ptr = lib.str.dup(self:string(row,col)) }
	s.ct = lib.pq.PQgetlength(self.res, row, col)
	return s
end
terra pqr:bool(row: intptr, col: intptr)
	var v = lib.pq.PQgetvalue(self.res, row, col)
	if @v == 0x01 then return true else return false end
end
terra pqr:cidr(row: intptr, col: intptr)
	var v = lib.pq.PQgetvalue(self.res, row, col)
	var i: lib.store.inet
	if v[0] == 0x02 then i.pv = 4
	elseif v[0] == 0x03 then i.pv = 6
	else lib.bail('invalid CIDR type in stream') end
	i.fixbits = v[1]
	if v[2] ~= 0x1 then lib.bail('expected CIDR but got inet from stream') end
	if i.pv == 4 and v[3] ~= 0x04 or i.pv == 6 and v[3] ~= 0x10 then
		lib.bail('CIDR failed length sanity check') end
	
	var sz: intptr if i.pv == 4 then sz = 4 else sz = 16 end
	for j=0,sz do i.v6[j] = v[4 + j] end -- 😬
	return i
end
pqr.methods.int = macro(function(self, ty, row, col)
	return quote
		var i: ty:astype()
		var v = lib.pq.PQgetvalue(self.res, row, col)
		lib.math.netswap_ip(ty, v, &i)
	in i end
end)

local pqt = {
	[lib.store.inet] = function(cidr)
		local tycode = cidr and 0x01 or 0x00
		return terra(i: lib.store.inet, buf: &uint8)
			var sz: intptr
			if i.pv == 4 then sz = 4 else sz = 16 end
			if buf == nil then buf = [&uint8](lib.mem.heapa_raw(sz + 4)) end
			if     i.pv == 4 then buf[0] = 0x02
			elseif i.pv == 6 then buf[0] = 0x03 end
			if cidr then -- our local 'inet' is not quite orthogonal to the
			             -- postgres inet type; tweak it to match (ignore port)
				buf[1] = i.fixbits
			elseif i.pv == 6 then buf[1] = 128
			else buf[1] = 32 end
			buf[2] = tycode
			buf[3] = sz
			for j=0,sz do buf[4 + j] = i.v6[j] end -- 😬
			return buf
		end
	end;
}

local con = symbol(&lib.pq.PGconn)
local prep = {}
local sqlsquash = function(s) return s:gsub('%s+',' '):gsub('^%s*(.-)%s*$','%1') end
for k,q in pairs(queries) do

	local qt = sqlsquash(q.sql)
	local stmt = 'parsavpg_' .. k
	prep[#prep + 1] = quote
		var res = lib.pq.PQprepare([con], stmt, qt, [#q.params], nil)
		defer lib.pq.PQclear(res)
		if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_COMMAND_OK then
			if res == nil then
				lib.bail('grievous error occurred preparing ',k,' statement')
................................................................................
			fixers[#fixers + 1] = quote
				--lib.io.fmt('uid=%llu(%llx)\n',[args[i]],[args[i]])
				[args[i]] = lib.math.netswap(ty, [args[i]])
			end
		end
	end

	terra q.exec(src: &lib.store.source, [args])
		var params = arrayof([&int8], [casts])
		var params_sz = arrayof(int, [counters])
		var params_ft = arrayof(int, [ft])
		[fixers]
		var res = lib.pq.PQexecPrepared([&lib.pq.PGconn](src.handle), stmt,
			[#args], params, params_sz, params_ft, 1)
		if res == nil then
................................................................................
			return pqr {0, nil}
		else
			return pqr {ct, res}
		end
	end
end

local terra row_to_actor(r: &pqr, row: intptr): lib.mem.ptr(lib.store.actor)
	var a: lib.mem.ptr(lib.store.actor)
	if r:cols() >= 8 then 
		a = [ lib.str.encapsulate(lib.store.actor, {
			nym = {`r:string(row, 1); `r:len(row,1) + 1};
			handle = {`r:string(row, 2); `r:len(row,2) + 1};
			bio = {`r:string(row, 4); `r:len(row,4) + 1};
			xid = {`r:string(row, 8); `r:len(row,8) + 1};
		}) ]
	else
		a = [ lib.str.encapsulate(lib.store.actor, {
			nym = {`r:string(row, 1); `r:len(row,1) + 1};
			handle = {`r:string(row, 2); `r:len(row,2) + 1};
			bio = {`r:string(row, 4); `r:len(row,4) + 1};
		}) ]
		a.ptr.xid = nil
	end
	a.ptr.id = r:int(uint64, row, 0);
	a.ptr.rights = lib.store.rights_default();

	a.ptr.rights.rank = r:int(uint16, row, 5);
	a.ptr.rights.quota = r:int(uint32, row, 6);
	if r:null(row,7) then
		a.ptr.key.ct = 0 a.ptr.key.ptr = nil
	else
		a.ptr.key = r:bin(row,7)
	end
	if r:null(row,3) then a.ptr.origin = 0
	else a.ptr.origin = r:int(uint64,row,3) end
	return a
end

local checksha = function(hnd, query, hash, origin, username, pw)
	local inet_buf = symbol(uint8[4 + 16])
	local validate = function(kind, cred, credlen)
		return quote 
			var osz: intptr if origin.pv == 4 then osz = 4 else osz = 16 end 
			var formats = arrayof([int], 1,1,1,1)
			var params = arrayof([&int8], username, kind,
				[&int8](&cred), [&int8](&inet_buf))
			var lens = arrayof(int, lib.str.sz(username), [#kind], credlen, osz + 4)
			var res = lib.pq.PQexecParams([&lib.pq.PGconn](hnd), query, 4, nil,
				params, lens, formats, 1)
			if res == nil then
				lib.bail('grievous failure checking pwhash')
			elseif lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
				lib.warn('pwhash query failed: ', lib.pq.PQresultErrorMessage(res), '\n', query)
			else
				var r = pqr {
					sz = lib.pq.PQntuples(res);
					res = res;
				}
				if r.sz > 0 then -- found a record! stop here
					var aid = r:int(uint64, 0,0)
					r:free()
					return aid
				end
			end
		end
	end
	
	local out = symbol(uint8[64])
	local vdrs = {}

		local alg = lib.md['MBEDTLS_MD_SHA' .. tostring(hash)]
		vdrs[#vdrs+1] = quote
			if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(alg),
				[&uint8](pw), lib.str.sz(pw), out) ~= 0 then
				lib.bail('hashing failure!')
			end
			[ validate(string.format('pw-sha%u', hash), out, hash / 8) ]
		end

	return quote
		lib.dbg(['searching for hashed password credentials in format SHA' .. tostring(hash)])
		var [inet_buf]
		[pqt[lib.store.inet](false)](origin, inet_buf)
		var [out]
		[vdrs]
		lib.dbg(['could not find password hash'])
	end
end

local b = `lib.store.backend {
	id = "pgsql";
	open = [terra(src: &lib.store.source): &opaque
		lib.report('connecting to postgres database: ', src.string.ptr)
		var [con] = lib.pq.PQconnectdb(src.string.ptr)
		if lib.pq.PQstatus(con) ~= lib.pq.CONNECTION_OK then
................................................................................
			lib.warn('postgres backend connection failed')
			lib.pq.PQfinish(con)
			return nil
		end
		var res = lib.pq.PQexec(con, [[
			select pg_catalog.set_config('search_path', 'public', false)
		]])
		if res == nil then
			lib.warn('critical failure to secure postgres connection')
			lib.pq.PQfinish(con)
			return nil
		end

		defer lib.pq.PQclear(res)
		if lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
			lib.warn('failed to secure postgres connection')
			lib.pq.PQfinish(con)
			return nil
		end

		[prep]
		return con
................................................................................
	end];
	close = [terra(src: &lib.store.source) lib.pq.PQfinish([&lib.pq.PGconn](src.handle)) end];

	conf_get = [terra(src: &lib.store.source, key: rawstring)
		var r = queries.conf_get.exec(src, key)
		if r.sz == 0 then return [lib.mem.ptr(int8)] { ptr = nil, ct = 0 } else
			defer r:free()
			return r:String(0,0)
		end
	end];
	conf_set = [terra(src: &lib.store.source, key: rawstring, val: rawstring)
		queries.conf_set.exec(src, key, val):free() end];
	conf_reset = [terra(src: &lib.store.source, key: rawstring)
		queries.conf_reset.exec(src, key):free() end];
	
	actor_fetch_uid = [terra(src: &lib.store.source, uid: uint64)
		var r = queries.actor_fetch_uid.exec(src, uid)
		if r.sz == 0 then
			return [lib.mem.ptr(lib.store.actor)] { ct = 0, ptr = nil }

		else defer r:free()

			var a = row_to_actor(&r, 0)
			a.ptr.source = src
			return a
		end
	end];

	actor_enum = [terra(src: &lib.store.source)
		var r = queries.actor_enum.exec(src)
		if r.sz == 0 then
			return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil }
		else defer r:free()
			var mem = lib.mem.heapa([&lib.store.actor], r.sz)
			for i=0,r.sz do mem.ptr[i] = row_to_actor(&r, i).ptr end
			return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr }
		end
	end];

	actor_enum_local = [terra(src: &lib.store.source)
		var r = queries.actor_enum_local.exec(src)
		if r.sz == 0 then
			return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil }
		else defer r:free()
			var mem = lib.mem.heapa([&lib.store.actor], r.sz)
			for i=0,r.sz do mem.ptr[i] = row_to_actor(&r, i).ptr end
			return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr }
		end
	end];

	actor_auth_how = [terra(
			src: &lib.store.source,
			ip: lib.store.inet,
			username: rawstring
		)
		var authview = src:conf_get('auth-source') defer authview:free()
		var a: lib.str.acc defer a:free()
		a:compose('with mts as (select a.kind from ',authview,[' ' .. sqlsquash [[as a
			left join parsav_actors as u on u.id = a.uid
			where (a.uid is null or u.handle = $1::text or (
					a.uid = 0 and a.name = $1::text
				)) and
				(a.netmask is null or a.netmask >> $2::inet) and
				blacklist = false)

			select
				(select count(*) from mts where kind like 'pw-%') > 0,
				(select count(*) from mts where kind like 'otp-%') > 0,
				(select count(*) from mts where kind like 'challenge-%') > 0,
				(select count(*) from mts where kind = 'trust') > 0 ]]]) -- cheat
		var cs: lib.store.credset cs:clear();
		var ipbuf: int8[20]
		;[pqt[lib.store.inet](false)](ip, [&uint8](&ipbuf))
		var ipbl: intptr if ip.pv == 4 then ipbl = 8 else ipbl = 20 end
		var params = arrayof(rawstring, username, [&int8](&ipbuf))
		var params_sz = arrayof(int, lib.str.sz(username), ipbl)
		var params_ft = arrayof(int, 1, 1)
		var res = lib.pq.PQexecParams([&lib.pq.PGconn](src.handle), a.buf, 2, nil,
			params, params_sz, params_ft, 1)
		if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
			if res == nil then
				lib.bail('grievous error occurred checking for auth methods')
			end
			lib.bail('could not get auth methods for user ',username,':\n',lib.pq.PQresultErrorMessage(res))
		end
		var r = pqr { res = res, sz = lib.pq.PQntuples(res) } 
		if r.sz == 0 then return cs end -- just in case
		(cs.pw << r:bool(0,0))
		(cs.otp << r:bool(0,1))
		(cs.challenge << r:bool(0,2))
		(cs.trust << r:bool(0,3))
		lib.pq.PQclear(res)
		return cs
	end];
	 
	actor_auth_pw = [terra(
			src: &lib.store.source,
			ip: lib.store.inet,
			username: rawstring,
			cred: rawstring
		)
		var authview = src:conf_get('auth-source') defer authview:free()
		var a: lib.str.acc defer a:free()
		a:compose('select a.aid from ',authview,[' ' .. sqlsquash [[as a
			left join parsav_actors as u on u.id = a.uid
			where (a.uid is null or u.handle = $1::text or (
					a.uid = 0 and a.name = $1::text
				)) and
				(a.kind = 'trust' or (a.kind = $2::text and a.cred = $3::bytea)) and
				(a.netmask is null or a.netmask >> $4::inet)
			order by blacklist desc limit 1]]])

		[ checksha(`src.handle, `a.buf, 256, ip, username, cred) ] -- most common
		[ checksha(`src.handle, `a.buf, 512, ip, username, cred) ] -- most secure
		[ checksha(`src.handle, `a.buf, 384, ip, username, cred) ] -- weird
		[ checksha(`src.handle, `a.buf, 224, ip, username, cred) ] -- weirdest

		-- TODO: check pbkdf2-hmac
		-- TODO: check OTP
		return 0
	end];
}

return b

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

5
6
7
8
9
10
11

12

13

14
15
16
17
18

19
20
21

22






23
24
25
26
27
28
29
30
31
32

33
34
35

36
37
38
39
40
41
42
43
44
45
		local helpstr = 'usage: parsav [-' .. flags .. '] [<arg>...]\n'
		options.entries = {
			{field = 'arglist', type = lib.mem.ptr(rawstring)}
		}
		local shortcases, longcases, init = {}, {}, {}
		local self = symbol(&options)
		local arg = symbol(rawstring)

		local skip = label()

		for o,desc in pairs(tbl) do

			options.entries[#options.entries + 1] = {
				field = o, type = bool
			}
			helpstr = helpstr .. string.format('    -%s --%s: %s\n',
				desc[1], o, desc[2])

		end
		for o,desc in pairs(tbl) do
			local flag = desc[1]

			init[#init + 1] = quote [self].[o] = false end






			shortcases[#shortcases + 1] = quote
				case [int8]([string.byte(flag)]) then [self].[o] = true end
			end
			longcases[#longcases + 1] = quote
				if lib.str.cmp([arg]+2, o) == 0 then
					[self].[o] = true
					goto [skip]
				end
			end
		end

		options.methods.parse = terra([self], argc: int, argv: &rawstring)
			[init]
			var parseopts = true

			self.arglist = lib.mem.heapa(rawstring, argc)
			var finalargc = 0
			for i=0,argc do
				var [arg] = argv[i]
				if arg[0] == ('-')[0] and parseopts then
					if arg[1] == ('-')[0] then -- long option
						if arg[2] == 0 then -- last option
							parseopts = false
						else [longcases] end
					else -- short options







>

>

>

|


<
>



>
|
>
>
>
>
>
>

|


|
<
<
|
|
<
>



>


|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37


38
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
		local helpstr = 'usage: parsav [-' .. flags .. '] [<arg>...]\n'
		options.entries = {
			{field = 'arglist', type = lib.mem.ptr(rawstring)}
		}
		local shortcases, longcases, init = {}, {}, {}
		local self = symbol(&options)
		local arg = symbol(rawstring)
		local idxo = symbol(uint)
		local skip = label()
		local sanitize = function(s) return s:gsub('_','-') end
		for o,desc in pairs(tbl) do
			local consume = desc[3] or 0
			options.entries[#options.entries + 1] = {
				field = o, type = (consume > 0) and uint or bool
			}
			helpstr = helpstr .. string.format('    -%s --%s: %s\n',

				desc[1], sanitize(o), desc[2])
		end
		for o,desc in pairs(tbl) do
			local flag = desc[1]
			local consume = desc[3] or 0
			init[#init + 1] = quote [self].[o] = [consume > 0 and 0 or false] end
			local ch if consume > 0 then ch = quote
				[self].[o] = idxo
				idxo = idxo + consume
			end else ch = quote
				[self].[o] = true
			end end
			shortcases[#shortcases + 1] = quote
				case [int8]([string.byte(flag)]) then [ch] end
			end
			longcases[#longcases + 1] = quote
				if lib.str.cmp([arg]+2, [sanitize(o)]) == 0 then [ch] goto [skip] end


			end
		end

		terra options:free() self.arglist:free() end
		options.methods.parse = terra([self], argc: int, argv: &rawstring)
			[init]
			var parseopts = true
			var [idxo] = 1
			self.arglist = lib.mem.heapa(rawstring, argc)
			var finalargc = 0
			for i=1,argc do
				var [arg] = argv[i]
				if arg[0] == ('-')[0] and parseopts then
					if arg[1] == ('-')[0] then -- long option
						if arg[2] == 0 then -- last option
							parseopts = false
						else [longcases] end
					else -- short options

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

10
11
12
13
14
15
16








17
18
19
20
21
22
23
..
26
27
28
29
30
31
32


33
34
35
36
37
38
39
	end
end
local posixes = {
	linux = true; osx = true;
	android = true; haiku = true;
}
local default_os = 'linux'








local conf = {
	pkg = {};
	dist      = default('parsav_dist', coalesce(
		os.getenv('NIX_PATH')  and 'nixos',
		os.getenv('NIX_STORE') and 'nixos',
	''));
	tgttrip   = default('parsav_arch_triple'); -- target triple, used in xcomp
................................................................................
	endian    = default('parsav_arch_endian', 'little');
	build     = {
		id = u.rndstr(6);
		release = u.ingest('release');
		when = os.date();
	};
	feat = {};


}
if u.ping '.fslckout' or u.ping '_FOSSIL_' then
	if u.ping '_FOSSIL_' then default_os = 'windows' end
	conf.build.branch = u.exec { 'fossil', 'branch', 'current' }
	conf.build.checkout = (u.exec { 'fossil', 'sql',
		[[select value from localdb.vvar where name = 'checkout-hash']]
	}):gsub("^'(.*)'$", '%1')







>
>
>
>
>
>
>
>







 







>
>







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

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

10
11
12
13
14
15
16

17
18








19
20
21
22
23
24
25
...
132
133
134
135
136
137
138
139




























140
	end;
	toobig = -lib.pk.MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE;
}
const.maxpemsz = math.floor((const.keybits / 8)*6.4) + 128 -- idk why this formula works but it basically seems to

local ctx = lib.pk.mbedtls_pk_context


local m = {
	pemfile = uint8[const.maxpemsz];








}
local callbacks = {}
if config.feat.randomizer == 'kern' then
	local rnd = terralib.externfunction('getrandom', {&opaque, intptr, uint} -> ptrdiff);
	terra callbacks.randomize(ctx: &opaque, dest: &uint8, sz: intptr): int
		return rnd(dest, sz, 0)
	end
................................................................................
		if lib.pk.mbedtls_pk_verify(pk, hk, hash, 0, [&uint8](sig), siglen) == 0 then
			return true, secl
		end
	end
	lib.dbg('all hash algorithms failed')
	return false, 0
end





























return m







>


>
>
>
>
>
>
>
>







 








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

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
	end;
	toobig = -lib.pk.MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE;
}
const.maxpemsz = math.floor((const.keybits / 8)*6.4) + 128 -- idk why this formula works but it basically seems to

local ctx = lib.pk.mbedtls_pk_context

local struct hashalg { id: uint8 bytes: intptr }
local m = {
	pemfile = uint8[const.maxpemsz];
	alg = {
		sha1 =   `hashalg {id = lib.md.MBEDTLS_MD_SHA1; bytes = 160/8};
		sha256 = `hashalg {id = lib.md.MBEDTLS_MD_SHA256; bytes = 256/8};
		sha512 = `hashalg {id = lib.md.MBEDTLS_MD_SHA512; bytes = 512/8};
		sha384 = `hashalg {id = lib.md.MBEDTLS_MD_SHA384; bytes = 384/8};
		sha224 = `hashalg {id = lib.md.MBEDTLS_MD_SHA224; bytes = 224/8};
		-- md5 = {id = lib.md.MBEDTLS_MD_MD5};-- !!!
	};
}
local callbacks = {}
if config.feat.randomizer == 'kern' then
	local rnd = terralib.externfunction('getrandom', {&opaque, intptr, uint} -> ptrdiff);
	terra callbacks.randomize(ctx: &opaque, dest: &uint8, sz: intptr): int
		return rnd(dest, sz, 0)
	end
................................................................................
		if lib.pk.mbedtls_pk_verify(pk, hk, hash, 0, [&uint8](sig), siglen) == 0 then
			return true, secl
		end
	end
	lib.dbg('all hash algorithms failed')
	return false, 0
end

terra m.hmac(alg: hashalg, key: lib.mem.ptr(uint8), txt: lib.mem.ptr(int8), buf: &uint8)
	lib.md.mbedtls_md_hmac(
			lib.md.mbedtls_md_info_from_type(alg.id), 
			key.ptr, key.ct,
			[&uint8](txt.ptr), txt.ct,
			buf) -- sz(buf) >= hash output size
end

terra m.hmaca(alg: hashalg, key: lib.mem.ptr(uint8), txt: lib.mem.ptr(int8))
	var buf = lib.mem.heapa(uint8, alg.bytes)
	m.hmac(alg, key, txt, buf.ptr)
	return buf
end

terra m.hotp(key: &(uint8[10]), counter: uint64)
	var hmac: uint8[20]
	var ctr = [lib.mem.ptr(int8)]{ptr = [&int8](&counter), ct = 8}
	m.hmac(m.alg.sha1,
		[lib.mem.ptr(uint8)]{ptr = [&uint8](key), ct = 10},
		ctr, hmac)
	
	var ofs = hmac[19] and 0x0F
	var p: uint8[4]
	for i=0,4 do p[i] = hmac[ofs + i] end

	return (@[&uint32](&p)) and 0x7FFFFFFF -- one hopes it's that easy
end

return m

Added license.de version [34fb6d78d2].











































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
OPEN-SOURCE-LIZENZ FÜR DIE EUROPÄISCHE UNION v. 1.2 
EUPL © EuropÀische Union 2007, 2016 
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). 
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: 
                      Lizenziert unter der EUPL 
oder in einer anderen Form zum Ausdruck bringt, dass er es unter der EUPL lizenzieren möchte. 

1.Begriffsbestimmungen 
FĂŒr diese Lizenz gelten folgende Begriffsbestimmungen:  
— „Lizenz“:diese Lizenz.  
— „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.  
— „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.  
— „Werk“:das Originalwerk oder seine Bearbeitungen.  
— „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.  
— „AusfĂŒhrbarer Code“:die — ĂŒblicherweise — kompilierte Form des Werks, die von einem Computer als Programm ausgefĂŒhrt werden soll.  
— „Lizenzgeber“:die natĂŒrliche oder juristische Person, die das Werk unter der Lizenz verbreitet oder zugĂ€nglich macht.  
— „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.  
— „Lizenznehmer“ („Sie“):jede natĂŒrliche oder juristische Person, die das Werk unter den Lizenzbedingungen nutzt.  
— „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.

2.Umfang der Lizenzrechte 
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: 
—  das Werk uneingeschrĂ€nkt zu nutzen, 
—  das Werk zu vervielfĂ€ltigen, 
—  das Werk zu verĂ€ndern und Bearbeitungen auf der Grundlage des Werks zu schaffen, 
—  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, 
—  das Werk oder VervielfĂ€ltigungen davon zu verbreiten, 
—  das Werk oder VervielfĂ€ltigungen davon zu vermieten oder zu verleihen, 
—  das Werk oder VervielfĂ€ltigungen davon weiter zu lizenzieren. 
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. 

3.ZugÀnglichmachung des Quellcodes 
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. 

4.EinschrÀnkungen des Urheberrechts
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. 

5.Pflichten des Lizenznehmers 
Die EinrĂ€umung der oben genannten Rechte ist an mehrere BeschrĂ€nkungen und Pflichten fĂŒr den Lizenznehmer gebunden: 

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.

„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. 

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.

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. 

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.

6.Urheber und Bearbeiter 
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. 
Jeder Bearbeiter gewĂ€hrleistet, dass er das Urheberrecht an den von ihm vorgenommenen Änderungen des Werks besitzt und befugt ist, diese Lizenz zu erteilen. 
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. 

7.GewÀhrleistungsausschluss 
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. 
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). 
Dieser GewĂ€hrleistungsausschluss ist wesentlicher Bestandteil der Lizenz und Bedingung fĂŒr die EinrĂ€umung von Rechten an dem Werk.

8.Haftungsausschluss/HaftungsbeschrÀnkung 
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. 

9.Zusatzvereinbarungen 
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. 

10.Annahme der Lizenz 
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. 

11.Informationspflichten
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.

12.Beendigung der Lizenz 
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. 
13.Sonstiges
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.

14.Gerichtsstand
Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: 
—  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; 
—  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. 

15.Anwendbares Recht
Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: 
—  Diese Lizenz unterliegt dem Recht des Mitgliedstaats der EuropĂ€ischen Union, in dem der Lizenzgeber seinen Sitz, Wohnsitz oder eingetragenen Sitz hat; 
—  diese Lizenz unterliegt dem belgischen Recht, wenn der Lizenzgeber keinen Sitz, Wohnsitz oder eingetragenen Sitz in einem Mitgliedstaat der EuropĂ€ischen Union hat.
   
Anlage 
„Kompatible Lizenzen“ nach Artikel 5 der EUPL sind: 
—  GNU General Public License (GPL) v. 2, v. 3 
—  GNU Affero General Public License (AGPL) v. 3 
—  Open Software License (OSL) v. 2.1, v. 3.0 
—  Eclipse Public License (EPL) v. 1.0 
—  CeCILL v. 2.0, v. 2.1 
—  Mozilla Public Licence (MPL) v. 2 
—  GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 
—  Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) fĂŒr andere Werke als Software 
—  European Union Public Licence (EUPL) v. 1.1, v. 1.2 
—  QuĂ©bec Free and Open-Source Licence — Reciprocity (LiLiQ-R) oder Strong Reciprocity (LiLiQ-R+)
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.
Alle sonstigen Änderungen oder ErgĂ€nzungen dieses Anhangs bedĂŒrfen der Ausarbeitung einer neuen Version der EUPL.    

Added license.en version [fc940c93f2].































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
                      EUROPEAN UNION PUBLIC LICENCE v. 1.2
                      EUPL © the European Union 2007, 2016

This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
below) which is provided under the terms of this Licence. Any use of the Work,
other than as authorised under this Licence is prohibited (to the extent such
use is covered by a right of the copyright holder of the Work).

The Work is provided under the terms of this Licence when the Licensor (as
defined below) has placed the following notice immediately following the
copyright notice for the Work:

        Licensed under the EUPL

or has expressed by any other means his willingness to license under the EUPL.

1. Definitions

In this Licence, the following terms have the following meaning:

- ‘The Licence’: this Licence.

- ‘The Original Work’: the work or software distributed or communicated by the
  Licensor under this Licence, available as Source Code and also as Executable
  Code as the case may be.

- ‘Derivative Works’: the works or software that could be created by the
  Licensee, based upon the Original Work or modifications thereof. This Licence
  does not define the extent of modification or dependence on the Original Work
  required in order to classify a work as a Derivative Work; this extent is
  determined by copyright law applicable in the country mentioned in Article 15.

- ‘The Work’: the Original Work or its Derivative Works.

- ‘The Source Code’: the human-readable form of the Work which is the most
  convenient for people to study and modify.

- ‘The Executable Code’: any code which has generally been compiled and which is
  meant to be interpreted by a computer as a program.

- ‘The Licensor’: the natural or legal person that distributes or communicates
  the Work under the Licence.

- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
  Licence, or otherwise contributes to the creation of a Derivative Work.

- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
  the Work under the terms of the Licence.

- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
  renting, distributing, communicating, transmitting, or otherwise making
  available, online or offline, copies of the Work or providing access to its
  essential functionalities at the disposal of any other natural or legal
  person.

2. Scope of the rights granted by the Licence

The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
sublicensable licence to do the following, for the duration of copyright vested
in the Original Work:

- use the Work in any circumstance and for all usage,
- reproduce the Work,
- modify the Work, and make Derivative Works based upon the Work,
- communicate to the public, including the right to make available or display
  the Work or copies thereof to the public and perform publicly, as the case may
  be, the Work,
- distribute the Work or copies thereof,
- lend and rent the Work or copies thereof,
- sublicense rights in the Work or copies thereof.

Those rights can be exercised on any media, supports and formats, whether now
known or later invented, as far as the applicable law permits so.

In the countries where moral rights apply, the Licensor waives his right to
exercise his moral right to the extent allowed by law in order to make effective
the licence of the economic rights here above listed.

The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
any patents held by the Licensor, to the extent necessary to make use of the
rights granted on the Work under this Licence.

3. Communication of the Source Code

The Licensor may provide the Work either in its Source Code form, or as
Executable Code. If the Work is provided as Executable Code, the Licensor
provides in addition a machine-readable copy of the Source Code of the Work
along with each copy of the Work that the Licensor distributes or indicates, in
a notice following the copyright notice attached to the Work, a repository where
the Source Code is easily and freely accessible for as long as the Licensor
continues to distribute or communicate the Work.

4. Limitations on copyright

Nothing in this Licence is intended to deprive the Licensee of the benefits from
any exception or limitation to the exclusive rights of the rights owners in the
Work, of the exhaustion of those rights or of other applicable limitations
thereto.

5. Obligations of the Licensee

The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:

Attribution right: The Licensee shall keep intact all copyright, patent or
trademarks notices and all notices that refer to the Licence and to the
disclaimer of warranties. The Licensee must include a copy of such notices and a
copy of the Licence with every copy of the Work he/she distributes or
communicates. The Licensee must cause any Derivative Work to carry prominent
notices stating that the Work has been modified and the date of modification.

Copyleft clause: If the Licensee distributes or communicates copies of the
Original Works or Derivative Works, this Distribution or Communication will be
done under the terms of this Licence or of a later version of this Licence
unless the Original Work is expressly distributed only under this version of the
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
(becoming Licensor) cannot offer or impose any additional terms or conditions on
the Work or Derivative Work that alter or restrict the terms of the Licence.

Compatibility clause: If the Licensee Distributes or Communicates Derivative
Works or copies thereof based upon both the Work and another work licensed under
a Compatible Licence, this Distribution or Communication can be done under the
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
Licence’ refers to the licences listed in the appendix attached to this Licence.
Should the Licensee's obligations under the Compatible Licence conflict with
his/her obligations under this Licence, the obligations of the Compatible
Licence shall prevail.

Provision of Source Code: When distributing or communicating copies of the Work,
the Licensee will provide a machine-readable copy of the Source Code or indicate
a repository where this Source will be easily and freely available for as long
as the Licensee continues to distribute or communicate the Work.

Legal Protection: This Licence does not grant permission to use the trade names,
trademarks, service marks, or names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the copyright notice.

6. Chain of Authorship

The original Licensor warrants that the copyright in the Original Work granted
hereunder is owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each Contributor warrants that the copyright in the modifications he/she brings
to the Work are owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each time You accept the Licence, the original Licensor and subsequent
Contributors grant You a licence to their contributions to the Work, under the
terms of this Licence.

7. Disclaimer of Warranty

The Work is a work in progress, which is continuously improved by numerous
Contributors. It is not a finished work and may therefore contain defects or
‘bugs’ inherent to this type of development.

For the above reason, the Work is provided under the Licence on an ‘as is’ basis
and without warranties of any kind concerning the Work, including without
limitation merchantability, fitness for a particular purpose, absence of defects
or errors, accuracy, non-infringement of intellectual property rights other than
copyright as stated in Article 6 of this Licence.

This disclaimer of warranty is an essential part of the Licence and a condition
for the grant of any rights to the Work.

8. Disclaimer of Liability

Except in the cases of wilful misconduct or damages directly caused to natural
persons, the Licensor will in no event be liable for any direct or indirect,
material or moral, damages of any kind, arising out of the Licence or of the use
of the Work, including without limitation, damages for loss of goodwill, work
stoppage, computer failure or malfunction, loss of data or any commercial
damage, even if the Licensor has been advised of the possibility of such damage.
However, the Licensor will be liable under statutory product liability laws as
far such laws apply to the Work.

9. Additional agreements

While distributing the Work, You may choose to conclude an additional agreement,
defining obligations or services consistent with this Licence. However, if
accepting obligations, You may act only on your own behalf and on your sole
responsibility, not on behalf of the original Licensor or any other Contributor,
and only if You agree to indemnify, defend, and hold each Contributor harmless
for any liability incurred by, or claims asserted against such Contributor by
the fact You have accepted any warranty or additional liability.

10. Acceptance of the Licence

The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
placed under the bottom of a window displaying the text of this Licence or by
affirming consent in any other similar way, in accordance with the rules of
applicable law. Clicking on that icon indicates your clear and irrevocable
acceptance of this Licence and all of its terms and conditions.

Similarly, you irrevocably accept this Licence and all of its terms and
conditions by exercising any rights granted to You by Article 2 of this Licence,
such as the use of the Work, the creation by You of a Derivative Work or the
Distribution or Communication by You of the Work or copies thereof.

11. Information to the public

In case of any Distribution or Communication of the Work by means of electronic
communication by You (for example, by offering to download the Work from a
remote location) the distribution channel or media (for example, a website) must
at least provide to the public the information requested by the applicable law
regarding the Licensor, the Licence and the way it may be accessible, concluded,
stored and reproduced by the Licensee.

12. Termination of the Licence

The Licence and the rights granted hereunder will terminate automatically upon
any breach by the Licensee of the terms of the Licence.

Such a termination will not terminate the licences of any person who has
received the Work from the Licensee under the Licence, provided such persons
remain in full compliance with the Licence.

13. Miscellaneous

Without prejudice of Article 9 above, the Licence represents the complete
agreement between the Parties as to the Work.

If any provision of the Licence is invalid or unenforceable under applicable
law, this will not affect the validity or enforceability of the Licence as a
whole. Such provision will be construed or reformed so as necessary to make it
valid and enforceable.

The European Commission may publish other linguistic versions or new versions of
this Licence or updated versions of the Appendix, so far this is required and
reasonable, without reducing the scope of the rights granted by the Licence. New
versions of the Licence will be published with a unique version number.

All linguistic versions of this Licence, approved by the European Commission,
have identical value. Parties can take advantage of the linguistic version of
their choice.

14. Jurisdiction

Without prejudice to specific agreement between parties,

- any litigation resulting from the interpretation of this License, arising
  between the European Union institutions, bodies, offices or agencies, as a
  Licensor, and any Licensee, will be subject to the jurisdiction of the Court
  of Justice of the European Union, as laid down in article 272 of the Treaty on
  the Functioning of the European Union,

- any litigation arising between other parties and resulting from the
  interpretation of this License, will be subject to the exclusive jurisdiction
  of the competent court where the Licensor resides or conducts its primary
  business.

15. Applicable Law

Without prejudice to specific agreement between parties,

- this Licence shall be governed by the law of the European Union Member State
  where the Licensor has his seat, resides or has his registered office,

- this licence shall be governed by Belgian law if the Licensor has no seat,
  residence or registered office inside a European Union Member State.

Appendix

‘Compatible Licences’ according to Article 5 EUPL are:

- GNU General Public License (GPL) v. 2, v. 3
- GNU Affero General Public License (AGPL) v. 3
- Open Software License (OSL) v. 2.1, v. 3.0
- Eclipse Public License (EPL) v. 1.0
- CeCILL v. 2.0, v. 2.1
- Mozilla Public Licence (MPL) v. 2
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
  works other than software
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
- QuĂ©bec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
  Reciprocity (LiLiQ-R+).

The European Commission may update this Appendix to later versions of the above
licences without producing a new version of the EUPL, as long as they provide
the rights granted in Article 2 of this Licence and protect the covered Source
Code from exclusive appropriation.

All other changes or additions to this Appendix require the production of a new
EUPL version.

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

78
79
80
81
82
83
84
85





































86
	for i = 0, len do
		var v, ok = m.shorthand.cval(s[i])
		if ok == false then return 0, false end
		val = (val * 64) + v
	end
	return val, true
end






































return m








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

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
	for i = 0, len do
		var v, ok = m.shorthand.cval(s[i])
		if ok == false then return 0, false end
		val = (val * 64) + v
	end
	return val, true
end

terra m.hexdigit(hb: uint8): int8
	var a = hb and 0x0F
	if a < 10 then return 0x30 + a
	else return 0x61 + (a-10) end
end

terra m.hexbyte(b: uint8): int8[2]
	return array(m.hexdigit((b and 0xF0) >> 4), m.hexdigit(b and 0x0F))
end

terra m.hexstr(src: &uint8, str: rawstring, sz: intptr)
	for i = 0, sz do
		var d = m.hexbyte(src[i])
		str[i*2] = d[0]
		str[i*2 + 1] = d[1]
	end
end

terra m.b32char(v: uint8): int8
	if v <= 25 then return 0x61 + v
	elseif v < 31 then return 0x32 + (v-26)
	else return 0 end
end

terra m.b32(v: uint64, buf: rawstring) -- 5 bytes -> 8 chars
	while v > 0 do
		var val = v % 32
		v = v / 32
		@buf = m.b32char(val)
		buf = buf + 1
	end
end

terra m.b32str(a: lib.mem.ptr(uint64))
	
end

return m

Added mem.t version [07d2cb4a7e].























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
-- vim: ft=terra
local m = {
	zero = macro(function(r)
		return quote
			for i = 0, [r.tree.type.N] do r[i] = 0 end
		end
	end);
	heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
	heapr_raw = terralib.externfunction('realloc', {&opaque, intptr} -> &opaque);
	heapf = terralib.externfunction('free', &opaque -> {});
	cpy = terralib.externfunction('mempcpy',{&opaque, &opaque, intptr} -> &opaque);
}

m.heapa = macro(function(ty, sz)
	local p = m.ptr(ty:astype())
	return `p {
		ptr = [&ty:astype()](m.heapa_raw(sizeof(ty) * sz));
		ct = sz;
	}
end)

m.ptr = terralib.memoize(function(ty)
	local t = terralib.types.newstruct(string.format('ptr<%s>', ty))
	t.entries = {
		{'ptr', &ty};
		{'ct', intptr};
	}
	t.ptr_basetype = ty
	local recurse = false
	if ty:isstruct() then
		if ty.methods.free then recurse = true end
	end
	t.metamethods.__not = macro(function(self)
		return `self.ptr
	end)
	t.methods = {
		free = terra(self: &t): bool
			[recurse and quote
				self.ptr:free()
			end or {}]
			if self.ct > 0 then
				m.heapf(self.ptr)
				self.ct = 0
				return true
			end
			return false
		end;
		init = terra(self: &t, newct: intptr): bool
			var nv = [&ty](m.heapa_raw(sizeof(ty) * newct))
			if nv ~= nil then
				self.ptr = nv
				self.ct = newct
				return true
			else return false end
		end;
		resize = terra(self: &t, newct: intptr): bool
			var nv: &ty
			if self.ct > 0
				then nv = [&ty](m.heapr_raw(self.ptr, sizeof(ty) * newct))
				else nv = [&ty](m.heapa_raw(sizeof(ty) * newct))
			end
			if nv ~= nil then
				self.ptr = nv
				self.ct = newct
				return true
			else return false end
		end;
	}
	return t
end)

m.vec = terralib.memoize(function(ty)
	local v = terralib.types.newstruct(string.format('vec<%s>', ty.name))
	v.entries = {
		{field = 'storage', type = m.ptr(ty)};
		{field = 'sz', type = intptr};
		{field = 'run', type = intptr};
	}
	local terra biggest(a: intptr, b: intptr)
		if a > b then return a else return b end
	end
	terra v:assure(n: intptr)
		if self.storage.ct < n then
			self.storage:resize(biggest(n, self.storage.ct + self.run))
		end
	end
	v.methods = {
		init = terra(self: &v, run: intptr): bool
			if not self.storage:init(run) then return false end
			self.run = run
			self.sz = 0
			return true
		end;
		new = terra(self: &v): &ty
			self:assure(self.sz + 1)
			self.sz = self.sz + 1
			return self.storage.ptr + (self.sz - 1)
		end;
		push = terra(self: &v, val: ty)
			self:assure(self.sz + 1)
			self.storage.ptr[self.sz] = val
			self.sz = self.sz + 1
		end;
		free = terra(self: &v) self.storage:free() end;
		last = terra(self: &v, idx: intptr): &ty
			if self.sz > idx then
				return self.storage.ptr + (self.sz - (idx+1))
			else lib.bail('vector underrun!') end
		end;
		crush = terra(self: &v)
			self.storage:resize(self.sz)
			return self.storage
		end;
	}
	v.metamethods.__apply = terra(self: &v, idx: intptr): &ty -- no index??
		if self.sz > idx then
			return self.storage.ptr + idx
		else lib.bail('vector overrun!') end
	end
	return v 
end)

return m

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

1
2
3
4





5
6
7

8
9

10
11
12
13
14
15
16
17
18

19
20






































































# parsav

**parsav** is a lightweight fediverse server






## dependencies

* libhttp

* json-c
* mbedtls

* postgresql-libs

## building

first, either install any missing dependencies as shared libraries, or build them as static libraries as described below:

* libhttp: run `$ make lib/libhttp/lib/libhttp.a`
* json-c (deps: `cmake`): run `$ make lib/json-c/libjson-c.a`
* mbedtls: run `$ make lib/mbedtls/lib/mbed{crypto,tls,x509}.a`


you can install static libraries for all dependencies with `$ make dep`, but this is recommended only if you have none of the above










































































>
>
>
>
>


<
>


>
|



|

<
<
<
>

<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
21



22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# parsav

**parsav** is a lightweight fediverse server

## backends
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.

* postgresql

## dependencies


* mongoose
* json-c
* mbedtls
* **postgresql backend:**
  * postgresql-libs 

## building

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.




postgresql-libs must be installed systemwide, as `parsav` does not currently provide for statically compiling and linking it


## configuring

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.

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.

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

    master   pgsql   host=localhost dbname=parsav
	tweets   pgsql   host=420.69.dread.cloud dbname=content

the form the configuration string takes depends on the specific backend.

### postgresql backend

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).

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.

`parsav_auth` has the following schema:

    create table parsav_auth (
		aid bigint primary key,
		uid bigint,
		newname text,
		kind text not null,
		cred bytea not null,
		restrict text[],
		netmask cidr,
		blacklist bool
    )

`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.

## authentication 
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.

`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`.

below is a full list of authentication types we intend to support. a checked box indicates the scheme has been implemented.

* ☑ pw-sha{512,384,256,224}: an ordinary password, hashed with the appropriate algorithm
* ☐ 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"`)
* ☐ pw-pbkdf2-hmac-sha{
}: a password hashed with the Password-Based Key Derivation Function 2 instead of plain SHA2
* ☐ 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`.
* ☐ 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
* ☐ tls-cert-fp: a fingerprint of a client certificate
* ☐ 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
* ☐ 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.
* ☐ 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.
* ☐ 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.
* ☑ trust: authentication always succeeds. only use in combination with netmask!!!

## license

parsav is released under the terms of the EUPL v1.2. copies of this license are included in the repository. dependencies are produced

## future direction

parsav needs more storage backends, as it currently supports only postgres. some possibilities, in order of priority, are:

* plain text/filesystem storage
* lmdb
* sqlite3
* generic odbc
* lua
* ldap?? possibly just for users
* cdb (for static content, maybe?)
* mariadb/mysql
* the various nosql horrors, e.g. redis, mongo, and so on

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

2
3
4
5
6
7
8





9
10
11
12
13
14
15
..
17
18
19
20
21
22
23
24
25
26
27
28
29


30
31
32
33
34
35
36
37
38
39
40































41
42
43
44
45
46
47
..
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93




94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
...
137
138
139
140
141
142
143
144
145
146
147
148
149

150
151
152
153


154
155
156



157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198






199
200
201
202
203

204
205
206
207
208
209







210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227

228
229
230






231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
...
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293
294
295
...
303
304
305
306
307
308
309
310

311
312
313


314
315
316
317
318

319
320
321
322
323
324
325
326
327
328
329


330






331


332
333
334
335
336
337
338
...
345
346
347
348
349
350
351
352
353
354
355
356





357
358
359
360
361
362
363
364
365
366
367
368
369
370

local util = dofile('common.lua')
local buildopts, buildargs = util.parseargs{...}
config = dofile('config.lua')

lib = {
	init = {};





	loadlib = function(name,hdr)
		local p = config.pkg[name]
		-- for _,v in pairs(p.dylibs) do
		-- 	terralib.linklibrary(p.libdir .. '/' .. v)
		-- end
		return terralib.includec(p.incdir .. '/' .. hdr)
	end;
................................................................................
		return macro(function(v,...)
			for ty,fn in pairs(tbl) do
				if v.tree.type == ty then return fn(v,...) end
			end
			return (tbl[false])(v,...)
		end)
	end;
	emit = function(...)
		local code = {}
		for i,v in ipairs{...} do
			if type(v) == 'string' or type(v) == 'number' then
				local str = tostring(v)
				code[#code+1] = `lib.io.send(2, str, [#str])


			elseif v.tree:is 'constant' then
				local str = tostring(v:asvalue())
				code[#code+1] = `lib.io.send(2, str, [#str])
			else
				code[#code+1] = quote var n = v in
					lib.io.send(2, n, lib.str.sz(n)) end
			end
		end
		code[#code+1] = `lib.io.send(2, '\n', 1)
		return code
	end;































	trn = macro(function(cond, i, e)
		return quote
			var c: bool = [cond]
			var r: i.tree.type
			if c == true then r = i else r = e end
		in r end
	end);
................................................................................
	io = {
		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
		recv = terralib.externfunction('read',  {int, rawstring, intptr} -> ptrdiff);
		say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
		fmt = terralib.externfunction('printf',
			terralib.types.funcpointer({rawstring},{int},true));
	};
	str = {
		sz = terralib.externfunction('strlen', rawstring -> intptr);
		cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
		ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int);
		cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
		ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
		ndup = terralib.externfunction('strndup',{rawstring, intptr} -> rawstring);
		fmt = terralib.externfunction('asprintf',
			terralib.types.funcpointer({&rawstring},{int},true));
	};
	copy = function(tbl)
		local new = {}
		for k,v in pairs(tbl) do new[k] = v end
		setmetatable(new, getmetatable(tbl))
		return new
	end;
	mem = {
		zero = macro(function(r)
			return quote
				for i = 0, [r.tree.type.N] do r[i] = 0 end
			end
		end);
		heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
		heapr_raw = terralib.externfunction('realloc', {&opaque, intptr} -> &opaque);
		heapf = terralib.externfunction('free', &opaque -> {});
		cpy = terralib.externfunction('mempcpy',{&opaque, &opaque, intptr} -> &opaque);
		heapa = macro(function(ty, sz)
			local p = lib.mem.ptr(ty:astype())
			return `p {
				ptr = [&ty:astype()](lib.mem.heapa_raw(sizeof(ty) * sz));
				ct = sz;
			}
		end)
	};
}





local noise = global(uint8,1)
local noise_header = function(code,txt,mod)
	if mod then
		return string.format('\27[%s;1m(parsav::%s %s)\27[m ', code,mod,txt)
	else
		return string.format('\27[%s;1m(parsav %s)\27[m ', code,txt)
	end
end
local defrep = function(level,n,code)
	return macro(function(...)
		local q = lib.emit(noise_header(code,n), ...)
		return quote
			if noise >= level then [q] end
		end
	end);
end
lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
	local q = lib.emit(noise_header('31','fatal'), ...)
	return quote
		[q]
		lib.proc.exit(1)
	end
end);
lib.stat = terralib.memoize(function(ty)
	local n = struct {
................................................................................
	elseif #tbl >= 2^8 then ty = uint16 end
	local o = { t = ty }
	for i, name in ipairs(tbl) do
		o[name] = i
	end
	return o
end
lib.mem.ptr = terralib.memoize(function(ty)
	local t = terralib.types.newstruct(string.format('ptr<%s>', ty))
	t.entries = {
		{'ptr', &ty};
		{'ct', intptr};
	}

	t.ptr_basetype = ty
	local recurse = false
	if ty:isstruct() then
		if ty.methods.free then recurse = true end


	end
	t.methods = {
		free = terra(self: &t): bool



			[recurse and quote
				self.ptr:free()
			end or {}]
			if self.ct > 0 then
				lib.mem.heapf(self.ptr)

				self.ct = 0
				return true
			end
			return false
		end;
		init = terra(self: &t, newct: intptr): bool
			var nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
			if nv ~= nil then
				self.ptr = nv
				self.ct = newct
				return true
			else return false end
		end;
		resize = terra(self: &t, newct: intptr): bool
			var nv: &ty
			if self.ct > 0
				then nv = [&ty](lib.mem.heapr_raw(self.ptr, sizeof(ty) * newct))
				else nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
			end
			if nv ~= nil then
				self.ptr = nv
				self.ct = newct
				return true
			else return false end
		end;
	}
	return t
end)
lib.mem.vec = terralib.memoize(function(ty)
	local v = terralib.types.newstruct(string.format('vec<%s>', ty.name))
	v.entries = {
		{field = 'storage', type = lib.mem.ptr(ty)};
		{field = 'sz', type = intptr};
		{field = 'run', type = intptr};
	}
	local terra biggest(a: intptr, b: intptr)
		if a > b then return a else return b end






	end
	terra v:assure(n: intptr)
		if self.storage.ct < n then
			self.storage:resize(biggest(n, self.storage.ct + self.run))
		end

	end
	v.methods = {
		init = terra(self: &v, run: intptr): bool
			if not self.storage:init(run) then return false end
			self.run = run
			self.sz = 0







			return true
		end;
		new = terra(self: &v): &ty
			self:assure(self.sz + 1)
			self.sz = self.sz + 1
			return self.storage.ptr + (self.sz - 1)
		end;
		push = terra(self: &v, val: ty)
			self:assure(self.sz + 1)
			self.storage.ptr[self.sz] = val
			self.sz = self.sz + 1
		end;
		free = terra(self: &v) self.storage:free() end;
		last = terra(self: &v, idx: intptr): &ty
			if self.sz > idx then
				return self.storage.ptr + (self.sz - (idx+1))
			else lib.bail('vector underrun!') end
		end;

		crush = terra(self: &v)
			self.storage:resize(self.sz)
			return self.storage






		end;
	}
	v.metamethods.__apply = terra(self: &v, idx: intptr): &ty -- no index??
		if self.sz > idx then
			return self.storage.ptr + idx
		else lib.bail('vector overrun!') end
	end
	return v 
end)

lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.file = terralib.loadfile('file.t')()

lib.math = terralib.loadfile('math.t')()
lib.crypt = terralib.loadfile('crypt.t')()
lib.http = terralib.loadfile('http.t')()
lib.tpl = terralib.loadfile('tpl.t')()
lib.string = terralib.loadfile('string.t')()
lib.store = terralib.loadfile('store.t')()


local be = {}
for _, b in pairs { 'pgsql' } do
	be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
end
lib.store.backends = global(`array([be]))

lib.cmdparse = terralib.loadfile('cmdparse.t')()
lib.srv = terralib.loadfile('srv.t')()

................................................................................

local pemdump = macro(function(pub, kp)
	local msg = (pub:asvalue() and ' * emitting public key\n') or ' * emitting private key\n'
	return quote
		var buf: lib.crypt.pemfile
		lib.mem.zero(buf)
		lib.crypt.pem(pub, &kp, buf)

		lib.io.send(1, msg, [#msg])
		lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
		lib.io.send(1, '\n', 1)
	end
end)

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end
end
................................................................................
	end
	noise = 1
end

local options = lib.cmdparse {
	version = {'V', 'display information about the binary build and exit'};
	quiet = {'q', 'do not print to standard out'};
	help = {'h', 'display this list'}

}

terra entry(argc: int, argv: &rawstring): int


	noise_init()
	[lib.init]

	-- shut mongoose the fuck up
	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)


	var mode: options
	mode:parse(argc,argv)
	if mode.version then
		version()
		return 0
	end
	if mode.help then
		lib.io.send(1,  [options.helptxt], [#options.helptxt])
		return 0
	end


	var srv: lib.srv






	srv:start('backend.conf')


	lib.report('listening for requests')
	while true do
		srv:poll()
	end
	srv:shutdown()

	return 0
................................................................................
end

if bflag('dump-config','C') then
	print(util.dump(config))
	os.exit(0)
end

local emit = print
if bflag('quiet','q') then emit = function() end end

local out = config.exe and 'parsav' or 'parsav.o'
local linkargs = {}





if config.posix then
	linkargs[#linkargs+1] = '-pthread'
end
for _,p in pairs(config.pkg) do util.append(linkargs, p.linkargs) end
emit('linking with args',util.dump(linkargs))
terralib.saveobj(out, {
		main = entry
	},
	linkargs,
	config.tgttrip and terralib.newtarget {
		Triple = config.tgttrip;
		CPU = config.tgtcpu;
		FloatABIHard = config.tgthf;
	} or nil)







>
>
>
>
>







 







|





>
>








|


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







 







<
|
<
<
<
<
<
<
<
<






<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
>
>
>
>











|









|







 







|
|
|
|
|
<
>
|
|
|
|
>
>
|
<
<
>
>
>
|
<
<
<
<
>
|
|
|
<
|
<
<
<
<
<
<
<
|
<
<
<
<
<
|
<
<
<
<
<
<
<
|
|
<
<
<
<
<
<
<
<
<
>
>
>
>
>
>
|
<
<
<

>
|
<
<
<
<
<
>
>
>
>
>
>
>
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
<
<
>
>
>
>
>
>
|
<
<
<
<
<

|
|








<
>
|
|
|
<
<
<
|
>

|







 







>
|
|
|







 







|
>



>
>





>

|
|
|
<
<
<
|
|
|
|
>
>
|
>
>
>
>
>
>
|
>
>







 







|
<
<


>
>
>
>
>




|









2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
..
90
91
92
93
94
95
96

97








98
99
100
101
102
103















104



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
...
152
153
154
155
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171


172
173
174
175




176
177
178
179

180







181





182







183
184









185
186
187
188
189
190
191



192
193
194





195
196
197
198
199
200
201
202
203
















204
205


206
207
208
209
210
211
212





213
214
215
216
217
218
219
220
221
222
223

224
225
226
227



228
229
230
231
232
233
234
235
236
237
238
...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
331
332
333
334
335
336
337
338


339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359

local util = dofile('common.lua')
local buildopts, buildargs = util.parseargs{...}
config = dofile('config.lua')

lib = {
	init = {};
	load = function(lst)
		for _, l in pairs(lst) do
			lib[l] = terralib.loadfile(l .. '.t')()
		end
	end;
	loadlib = function(name,hdr)
		local p = config.pkg[name]
		-- for _,v in pairs(p.dylibs) do
		-- 	terralib.linklibrary(p.libdir .. '/' .. v)
		-- end
		return terralib.includec(p.incdir .. '/' .. hdr)
	end;
................................................................................
		return macro(function(v,...)
			for ty,fn in pairs(tbl) do
				if v.tree.type == ty then return fn(v,...) end
			end
			return (tbl[false])(v,...)
		end)
	end;
	emit_unitary = function(fd,...)
		local code = {}
		for i,v in ipairs{...} do
			if type(v) == 'string' or type(v) == 'number' then
				local str = tostring(v)
				code[#code+1] = `lib.io.send(2, str, [#str])
			elseif type(v) == 'table' and #v == 2 then
				code[#code+1] = `lib.io.send(2, [v[1]], [v[2]])
			elseif v.tree:is 'constant' then
				local str = tostring(v:asvalue())
				code[#code+1] = `lib.io.send(2, str, [#str])
			else
				code[#code+1] = quote var n = v in
					lib.io.send(2, n, lib.str.sz(n)) end
			end
		end
		code[#code+1] = `lib.io.send(fd, '\n', 1)
		return code
	end;
	emitv = function(fd,...)
		local vec = {}
		local defs = {}
		for i,v in ipairs{...} do
			local str, ct
			if type(v) == 'table' and v.tree and not (v.tree:is 'constant') then
				if v.tree.type.convertible == 'tuple' then
					str = `v._0
					ct = `v._1
				else
					local n = symbol(v.tree.type)
					defs[#defs + 1] = quote var [n] = v end
					str = n
					ct = `lib.str.sz(n)
				end
			else
				if type(v) == 'string' or type(v) == 'number' then
					str = tostring(v) 
				else--if v.tree:is 'constant' then
					str = tostring(v:asvalue())
				end
				ct = ct or #str
			end
			vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque](str), iov_len = ct}
		end
		vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque]('\n'), iov_len = 1}
		return quote
			[defs]
			var strs = array( [vec] )
		in lib.uio.writev(fd, strs, [#vec]) end
	end;
	trn = macro(function(cond, i, e)
		return quote
			var c: bool = [cond]
			var r: i.tree.type
			if c == true then r = i else r = e end
		in r end
	end);
................................................................................
	io = {
		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
		recv = terralib.externfunction('read',  {int, rawstring, intptr} -> ptrdiff);
		say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
		fmt = terralib.externfunction('printf',
			terralib.types.funcpointer({rawstring},{int},true));
	};

	str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };








	copy = function(tbl)
		local new = {}
		for k,v in pairs(tbl) do new[k] = v end
		setmetatable(new, getmetatable(tbl))
		return new
	end;















}



if config.posix then
	lib.uio = terralib.includec 'sys/uio.h';
	lib.emit = lib.emitv -- use more efficient call where available
else lib.emit = lib.emit_unitary end

local noise = global(uint8,1)
local noise_header = function(code,txt,mod)
	if mod then
		return string.format('\27[%s;1m(parsav::%s %s)\27[m ', code,mod,txt)
	else
		return string.format('\27[%s;1m(parsav %s)\27[m ', code,txt)
	end
end
local defrep = function(level,n,code)
	return macro(function(...)
		local q = lib.emit(2, noise_header(code,n), ...)
		return quote
			if noise >= level then [q] end
		end
	end);
end
lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
	local q = lib.emit(2, noise_header('31','fatal'), ...)
	return quote
		[q]
		lib.proc.exit(1)
	end
end);
lib.stat = terralib.memoize(function(ty)
	local n = struct {
................................................................................
	elseif #tbl >= 2^8 then ty = uint16 end
	local o = { t = ty }
	for i, name in ipairs(tbl) do
		o[name] = i
	end
	return o
end
lib.set = function(tbl)
	local bytes = math.ceil(#tbl / 8)
	local o = {}
	for i, name in ipairs(tbl) do o[name] = i end
	local struct set { _store: uint8[bytes] }

	local struct bit { _v: intptr _set: &set}
	terra set:clear() for i=0,bytes do self._store[i] = 0 end end
	set.members = tbl
	set.name = string.format('set<%s>', table.concat(tbl, '|'))
	set.metamethods.__entrymissing = macro(function(val, obj)
		if o[val] == nil then error('value ' .. val .. ' not in set') end
		return `bit { _v=[o[val] - 1], _set = &obj }
	end)


	set.methods.dump = macro(function(self)
		local q = quote lib.io.say('dumping set:\n') end
		for i,v in ipairs(tbl) do
			q = quote




				[q]
				if [bool](self.[v])
					then lib.io.say([' - ' .. v .. ': true\n'])
					else lib.io.say([' - ' .. v .. ': false\n'])

				end







			end





		end







		return q
	end)









	set.metamethods.__add = macro(function(self,other)
		local new = symbol(set)
		local q = quote var [new] new:clear() end
		for i = 0, bytes - 1 do
			q = quote [q]
				new._store[i] = self._store[i] or other._store[i]
			end



		end
		return quote [q] in new end
	end)





	bit.metamethods.__cast = function(from,to,e)
		local q = quote var s = e
			in (s._set._store[s._v/8] and (1 << s._v % 8)) end
		if to == bit then error('casting to bit is not meaningful')
		elseif to == bool then return `([q] ~= 0)
		elseif to:isintegral() then return q
		elseif from == bit then error('cannot cast bit to ' .. tostring(to))
		else return nil end
	end
















	bit.metamethods.__apply = terra(self: &bit): bool return @self end
	bit.metamethods.__lshift = terra(self: &bit, hl: bool)


		var byte = self._v / 8
		var bit = self._v % 8
		if hl then
			self._set._store[byte] = self._set._store[byte] or (1 << bit)
		else
			self._set._store[byte] = self._set._store[byte] and not (1 << bit)
		end





	end
	return set
end

lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')


lib.load {
	'mem', 'str', 'file', 'math', 'crypt';
	'http', 'tpl', 'store';



}

local be = {}
for _, b in pairs(config.backends) do
	be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
end
lib.store.backends = global(`array([be]))

lib.cmdparse = terralib.loadfile('cmdparse.t')()
lib.srv = terralib.loadfile('srv.t')()

................................................................................

local pemdump = macro(function(pub, kp)
	local msg = (pub:asvalue() and ' * emitting public key\n') or ' * emitting private key\n'
	return quote
		var buf: lib.crypt.pemfile
		lib.mem.zero(buf)
		lib.crypt.pem(pub, &kp, buf)
		lib.emit(msg, buf, '\n')
		--lib.io.send(1, msg, [#msg])
		--lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
		--lib.io.send(1, '\n', 1)
	end
end)

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end
end
................................................................................
	end
	noise = 1
end

local options = lib.cmdparse {
	version = {'V', 'display information about the binary build and exit'};
	quiet = {'q', 'do not print to standard out'};
	help = {'h', 'display this list'};
	backend_file = {'b', 'init from specified backend file', 1};
}

terra entry(argc: int, argv: &rawstring): int
	if argc < 1 then lib.bail('bad invocation!') end

	noise_init()
	[lib.init]

	-- shut mongoose the fuck up
	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)
	var srv: lib.srv

	do var mode: options
		mode:parse(argc,argv) defer mode:free()
		if mode.version then version() return 0 end



		if mode.help then
			lib.io.send(1,  [options.helptxt], [#options.helptxt])
			return 0
		end
		var cnf: rawstring
		if mode.backend_file ~= 0
			then if mode.arglist.ct >= mode.backend_file
					then cnf = mode.arglist.ptr[mode.backend_file - 1]
					else lib.bail('bad invocation, backend file not specified') end
			else cnf = lib.proc.getenv('parsav_backend_file')
		end
		if cnf == nil then cnf = "backend.conf" end

		srv:start(cnf)
	end

	lib.report('listening for requests')
	while true do
		srv:poll()
	end
	srv:shutdown()

	return 0
................................................................................
end

if bflag('dump-config','C') then
	print(util.dump(config))
	os.exit(0)
end

local holler = print


local out = config.exe and 'parsav' or 'parsav.o'
local linkargs = {}

if bflag('quiet','q') then holler = function() end end
if bflag('asan','s') then linkargs[#linkargs+1] = '-fsanitize=address' end
if bflag('lsan','S') then linkargs[#linkargs+1] = '-fsanitize=leak' end

if config.posix then
	linkargs[#linkargs+1] = '-pthread'
end
for _,p in pairs(config.pkg) do util.append(linkargs, p.linkargs) end
holler('linking with args',util.dump(linkargs))
terralib.saveobj(out, {
		main = entry
	},
	linkargs,
	config.tgttrip and terralib.newtarget {
		Triple = config.tgttrip;
		CPU = config.tgtcpu;
		FloatABIHard = config.tgthf;
	} or nil)

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

13
14
15
16
17
18
19
20



21
22
23
24
25
26
27
	value text
);

insert into parsav_config (key,value) values
	('bind',:'bind'),
	('domain',:'domain'),
	('auth-source',:'auth'),
	('administrator',:'admin');




-- note that valid ids should always > 0, as 0 is reserved for null
-- on the client side, vastly simplifying code
drop table if exists parsav_servers cascade;
create table parsav_servers (
	id bigint primary key default (1+random()*(2^63-1))::bigint,
	domain text not null,







|
>
>
>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
	value text
);

insert into parsav_config (key,value) values
	('bind',:'bind'),
	('domain',:'domain'),
	('auth-source',:'auth'),
	('administrator',:'admin'),
	('server-secret', encode(
			digest(int8send((2^63 * (random()*2 - 1))::bigint),
		'sha512'), 'base64'));

-- note that valid ids should always > 0, as 0 is reserved for null
-- on the client side, vastly simplifying code
drop table if exists parsav_servers cascade;
create table parsav_servers (
	id bigint primary key default (1+random()*(2^63-1))::bigint,
	domain text not null,

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

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
92
93
94
95
96
97
98








99
100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
...
116
117
118
119
120
121
122
123
124
125
126
127



128
129
130
131
132
133
134
...
159
160
161
162
163
164
165
166
167


168
169
170
171
172
173
174
	var fr = lib.file.open(befile, [lib.file.mode.read])
	if fr.ok == false then
		lib.bail('could not open configuration file ', befile)
	end

	var f = fr.val
	var c: lib.mem.vec(lib.store.source) c:init(8)
	var text: lib.string.acc text:init(64)
	do var buf: int8[64]
		while true do
			var ct = f:read(buf, [buf.type.N])
			if ct == 0 then break end
			text:push(buf, ct)
		end
	end
................................................................................
	s.sources = c:crush()
end

--srv.methods.conf_set = terra(self: &srv, key: rawstring, val:rawstring)
--	self.sources.ptr[0]:conf_set(key, val)
--end









srv.metamethods.__methodmissing = macro(function(meth, self, ...)
	local primary, ptr, stat, simple = 0,1,2,3
	local tk, rt = primary
	local expr = {...}
	for _,f in pairs(lib.store.backend.entries) do
		local fn = f.field or f[1]
		local ft = f.type or f[2]
		if fn == meth then
			rt = ft.type.returntype
			if rt == bool then tk = simple

			elseif rt.stat_basetype then tk = stat
			elseif rt.ptr_basetype then tk = ptr end
			break
		end
	end
	
	if tk == primary then
................................................................................
		return `self.sources.ptr[0]:[meth]([expr])
	else local ok, empty
		local r = symbol(rt)
		if tk == ptr then
			ok = `r.ptr ~= nil
			empty = `[rt]{ptr=nil,ct=0}
		elseif tk == stat then
			ok = `r.ok ~= false
			empty = `[rt]{ok=false,error=1}
		elseif tk == simple then
			ok = `r == true
			empty = `false



		end
		return quote
			var [r] = empty
			for i=0,self.sources.ct do var src = self.sources.ptr + i
				if src.handle ~= nil then
					r = src:[meth]([expr])
					if [ok] then break
................................................................................
	elseif dbbind.ptr ~= nil then
		bind = dbbind.ptr
	else bind = '[::]:10917' end

	lib.report('binding to ', bind)
	lib.net.mg_mgr_init(&self.webmgr)
	self.webcon = lib.net.mg_http_listen(&self.webmgr, bind, handle.http, nil)
	dbbind:free()



end

srv.methods.poll = terra(self: &srv)
	lib.net.mg_mgr_poll(&self.webmgr,1000)
end

srv.methods.shutdown = terra(self: &srv)







|







 







>
>
>
>
>
>
>
>

|








>







 







|




>
>
>







 







<

>
>







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
...
171
172
173
174
175
176
177

178
179
180
181
182
183
184
185
186
187
	var fr = lib.file.open(befile, [lib.file.mode.read])
	if fr.ok == false then
		lib.bail('could not open configuration file ', befile)
	end

	var f = fr.val
	var c: lib.mem.vec(lib.store.source) c:init(8)
	var text: lib.str.acc text:init(64)
	do var buf: int8[64]
		while true do
			var ct = f:read(buf, [buf.type.N])
			if ct == 0 then break end
			text:push(buf, ct)
		end
	end
................................................................................
	s.sources = c:crush()
end

--srv.methods.conf_set = terra(self: &srv, key: rawstring, val:rawstring)
--	self.sources.ptr[0]:conf_set(key, val)
--end

terra srv:actor_auth_how(ip: lib.store.inet, usn: rawstring)
	var cs: lib.store.credset cs:clear()
	for i=0,self.sources.ct do
		var set: lib.store.credset = self.sources.ptr[i]:actor_auth_how(ip, usn)
		cs = cs + set
	end
	return cs
end
srv.metamethods.__methodmissing = macro(function(meth, self, ...)
	local primary, ptr, stat, simple, oid = 0,1,2,3,4
	local tk, rt = primary
	local expr = {...}
	for _,f in pairs(lib.store.backend.entries) do
		local fn = f.field or f[1]
		local ft = f.type or f[2]
		if fn == meth then
			rt = ft.type.returntype
			if rt == bool then tk = simple
			elseif rt.type == 'integer' then tk = oid
			elseif rt.stat_basetype then tk = stat
			elseif rt.ptr_basetype then tk = ptr end
			break
		end
	end
	
	if tk == primary then
................................................................................
		return `self.sources.ptr[0]:[meth]([expr])
	else local ok, empty
		local r = symbol(rt)
		if tk == ptr then
			ok = `r.ptr ~= nil
			empty = `[rt]{ptr=nil,ct=0}
		elseif tk == stat then
			ok = `r.ok == true
			empty = `[rt]{ok=false,error=1}
		elseif tk == simple then
			ok = `r == true
			empty = `false
		elseif tk == oid then
			ok = `r ~= 0
			empty = `0
		end
		return quote
			var [r] = empty
			for i=0,self.sources.ct do var src = self.sources.ptr + i
				if src.handle ~= nil then
					r = src:[meth]([expr])
					if [ok] then break
................................................................................
	elseif dbbind.ptr ~= nil then
		bind = dbbind.ptr
	else bind = '[::]:10917' end

	lib.report('binding to ', bind)
	lib.net.mg_mgr_init(&self.webmgr)
	self.webcon = lib.net.mg_http_listen(&self.webmgr, bind, handle.http, nil)



	if dbbind.ptr ~= nil then dbbind:free() end
end

srv.methods.poll = terra(self: &srv)
	lib.net.mg_mgr_poll(&self.webmgr,1000)
end

srv.methods.shutdown = terra(self: &srv)

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

7
8
9
10
11
12
13


14
15

16
17
18
19
20
21
22
23
24
..
54
55
56
57
58
59
60


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
...
111
112
113
114
115
116
117



































118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135






















136
137
138
139
140
141
142
	};
	notiftype = lib.enum {
		'mention', 'like', 'rt', 'react'
	};
	relation = lib.enum {
		'follow', 'mute', 'block'
	};


}


local str = lib.mem.ptr(int8)
str:complete()

struct m.source

struct m.rights {
	rank: uint16 -- lower = more powerful except 0 = regular user
	-- creating staff automatically assigns rank immediately below you
	quota: uint32 -- # of allowed tweets per day; 0 = no limit
................................................................................
struct m.actor {
	id: uint64
	nym: str
	handle: str
	origin: uint64
	bio: str
	rights: m.rights


	key: str

	source: &m.source
}
terra m.actor:free()
	self.nym:free()
	self.handle:free()
	self.bio:free()
	self.key:free()
end

struct m.range {
	time: bool
	union {
		from_time: m.timepoint
		from_idx: uint64
	}
................................................................................
	kind: m.notiftype.t
	when: uint64
	union {
		post: uint64
		reaction: int8[8]
	}
}




































-- backends only handle content on the local server
struct m.backend { id: rawstring
	open: &m.source -> &opaque
	close: &m.source -> {}

	conf_get: {&m.source, rawstring} -> lib.mem.ptr(int8)
	conf_set: {&m.source, rawstring, rawstring} -> {}
	conf_reset: {&m.source, rawstring} -> {}

	actor_save: {&m.source, m.actor} -> bool
	actor_create: {&m.source, m.actor} -> bool
	actor_fetch_xid: {&m.source, rawstring} -> lib.stat(m.actor)
	actor_fetch_uid: {&m.source, uint64} -> lib.stat(m.actor)
	actor_notif_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.notif)
	actor_auth: {&m.source, rawstring, rawstring} -> lib.stat(m.actor)
	actor_enum: {&m.source} -> lib.mem.ptr(m.actor)
	actor_enum_local: {&m.source} -> lib.mem.ptr(m.actor)























	actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
	actor_conf_int: cnf(intptr, lib.stat(intptr))

	post_save: {&m.source, &m.post} -> bool
	post_create: {&m.source, &m.post} -> bool
	actor_post_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(m.post)







>
>
|
|
>
|
<







 







>
>
|



<
<
<
<
<
<







 







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












|
|

<
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







7
8
9
10
11
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
..
56
57
58
59
60
61
62
63
64
65
66
67
68






69
70
71
72
73
74
75
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
	};
	notiftype = lib.enum {
		'mention', 'like', 'rt', 'react'
	};
	relation = lib.enum {
		'follow', 'mute', 'block'
	};
	credset = lib.set {
		'pw', 'otp', 'challenge', 'trust'
	};
}

local str = rawstring --lib.mem.ptr(int8)


struct m.source

struct m.rights {
	rank: uint16 -- lower = more powerful except 0 = regular user
	-- creating staff automatically assigns rank immediately below you
	quota: uint32 -- # of allowed tweets per day; 0 = no limit
................................................................................
struct m.actor {
	id: uint64
	nym: str
	handle: str
	origin: uint64
	bio: str
	rights: m.rights
	key: lib.mem.ptr(uint8)

	xid: str

	source: &m.source
}







struct m.range {
	time: bool
	union {
		from_time: m.timepoint
		from_idx: uint64
	}
................................................................................
	kind: m.notiftype.t
	when: uint64
	union {
		post: uint64
		reaction: int8[8]
	}
}

struct m.inet {
	pv: uint8 -- 0 = null, 4 = ipv4, 6 = ipv6
	union {
		v4: uint8[4]
		v6: uint8[16]
	}
	union {
		fixbits: uint8 -- for cidr
		port: uint16 -- for origin
	}
}

terra m.inet:cidr_str()
	if self.pv == 4 then
		var maxsz = 3*4 + 3 + 1
	elseif self.pv == 6 then
		var bits = 128
		var bytes = bits / 8
		var hexchs = bytes * 2
		var segs = hexchs / 4
		var seps = segs - 1
		var maxsz = hexchs + seps + 1
	else return nil end
end

struct m.auth {
	aid: uint64
	uid: uint64
	aname: str
	netmask: m.inet
	restrict: lib.mem.ptr(rawstring)
	blacklist: bool
}


-- backends only handle content on the local server
struct m.backend { id: rawstring
	open: &m.source -> &opaque
	close: &m.source -> {}

	conf_get: {&m.source, rawstring} -> lib.mem.ptr(int8)
	conf_set: {&m.source, rawstring, rawstring} -> {}
	conf_reset: {&m.source, rawstring} -> {}

	actor_save: {&m.source, m.actor} -> bool
	actor_create: {&m.source, m.actor} -> bool
	actor_fetch_xid: {&m.source, rawstring} -> lib.mem.ptr(m.actor)
	actor_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.actor)
	actor_notif_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.notif)

	actor_enum: {&m.source} -> lib.mem.ptr(&m.actor)
	actor_enum_local: {&m.source} -> lib.mem.ptr(&m.actor)

	actor_auth_how: {&m.source, m.inet, rawstring} -> m.credset
		-- returns a set of auth method categories that are available for a
		-- given user from a certain origin
			-- origin: inet
			-- handle: rawstring
	actor_auth_otp: {&m.source, m.inet, rawstring, rawstring} -> uint64
	actor_auth_pw: {&m.source, m.inet, rawstring, rawstring} -> uint64
		-- handles password-based logins against hashed passwords
			-- origin: inet
			-- handle: rawstring
			-- token:  rawstring
	actor_auth_tls:    {&m.source, m.inet, rawstring} -> uint64
		-- handles implicit authentication performed as part of an TLS connection
			-- origin: inet
			-- fingerprint: rawstring
	actor_auth_api:    {&m.source, m.inet, rawstring, rawstring} -> uint64
		-- handles API authentication
			-- origin: inet
			-- handle: rawstring
			-- key:    rawstring (X-API-Key)
	actor_auth_record_fetch: {&m.source, uint64} -> lib.mem.ptr(m.auth)

	actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
	actor_conf_int: cnf(intptr, lib.stat(intptr))

	post_save: {&m.source, &m.post} -> bool
	post_create: {&m.source, &m.post} -> bool
	actor_post_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(m.post)

Added str.t version [4b8724b0aa].





































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
-- vim: ft=terra
-- string.t: string classes

local m = {
	sz = terralib.externfunction('strlen', rawstring -> intptr);
	cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
	ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int);
	cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
	ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
	dup = terralib.externfunction('strdup',rawstring -> rawstring);
	ndup = terralib.externfunction('strndup',{rawstring, intptr} -> rawstring);
	fmt = terralib.externfunction('asprintf',
		terralib.types.funcpointer({&rawstring,rawstring},{int},true));
	bfmt = terralib.externfunction('sprintf',
		terralib.types.funcpointer({rawstring,rawstring},{int},true));
}

(lib.mem.ptr(int8)).metamethods.__cast = function(from,to,e)
	if from == &int8 then
		return `[lib.mem.ptr(int8)]{ptr = e, ct = m.sz(e)}
	elseif to == &int8 then
		return e.ptr
	end
end

struct m.acc {
	buf: rawstring
	sz: intptr
	run: intptr
	space: intptr
}

local terra biggest(a: intptr, b: intptr)
	if a > b then return a else return b end
end

terra m.acc:init(run: intptr)
	lib.dbg('initializing string accumulator')
	self.buf = [rawstring](lib.mem.heapa_raw(run))
	self.run = run
	self.space = run
	self.sz = 0
	return self
end;

terra m.acc:free()
	lib.dbg('freeing string accumulator')
	if self.buf ~= nil and self.space > 0 then
		lib.mem.heapf(self.buf)
	end
end;

terra m.acc:crush()
	lib.dbg('crushing string accumulator')
	self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.sz))
	self.space = self.sz
	return self
end;

terra m.acc:finalize()
	lib.dbg('finalizing string accumulator')
	self:crush()
	var pt: lib.mem.ptr(int8)
	pt.ptr = self.buf
	pt.ct = self.sz
	self.buf = nil
	self.sz = 0
	return pt
end;

terra m.acc:push(str: rawstring, len: intptr)
	var llen = len
	if str == nil then return self end
	if str[len - 1] == 0xA then llen = llen - 1 end -- don't display newlines in debug output
	lib.dbg('pushing "',{str,llen},'" onto accumulator')
	if self.buf == nil then self:init(self.run) end
	if len == 0 then len = m.sz(str) end
	if len >= self.space - self.sz then
		self.space = self.space + biggest(self.run,len + 1)
		self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.space))
	end
	lib.mem.cpy(self.buf + self.sz, str, len)
	self.sz = self.sz + len
	self.buf[self.sz] = 0
	return self
end;
m.acc.methods.ppush = terra(self: &m.acc, str: lib.mem.ptr(int8))
	self:push(str.ptr, str.ct)            return self end;
m.acc.methods.merge = terra(self: &m.acc, str: lib.mem.ptr(int8))
	self:push(str.ptr, str.ct) str:free() return self end;
m.acc.methods.compose = macro(function(self, ...)
	local minlen = 0
	local pstrs = {}
	for i,v in ipairs{...} do
		if type(v) == 'table' then
			local gl = 16 -- guess wildly
			if v.tree and v.tree.type.convertible == 'tuple' then
				pstrs[#pstrs+1] = {str = `v._0, len = `v._1}
			elseif v.asvalue and type(v:asvalue()) == 'string' then
				local str = v:asvalue()
				pstrs[#pstrs+1] = {str = str, len = #str}
				gl = #str + 1
			elseif v.tree and v.tree.type.ptr_basetype == int8 then
				pstrs[#pstrs+1] = {str = `v.ptr, len = `v.ct}
			else pstrs[#pstrs+1] = {str = v, len = 0} end
			minlen = minlen + gl
		elseif type(v) == 'string' then 
			pstrs[#pstrs+1] = {str = v, len = #v}
			minlen = minlen + #v + 1
		else error('invalid type in compose expression') end
	end
	local call = `self:init(minlen)
	for i,v in ipairs(pstrs) do
		call = `[call]:push([v.str],[v.len])
	end
	return call
end)
m.acc.metamethods.__lshift = terralib.overloadedfunction('(<<)', {
	terra(self: &m.acc, str: rawstring)         return self: push(str,0) end;
	terra(self: &m.acc, str: lib.mem.ptr(int8)) return self:ppush(str  ) end;
})

m.box = terralib.memoize(function(ty)
	local b = struct {
		obj: ty
		storage: int8[0]
	}
	b.name = string.format('bytebox<%s>', ty.name)
	b.methods.mk = terra(sz: intptr)
		return [&b](lib.mem.heapa_raw(sizeof(b) + sz))
	end
	terra b:free() lib.mem.heapf(self) end -- enhhhhh
	return b
end)

m.encapsulate = function(ty, vals)
	local memreq_const = sizeof(ty)
	local ptr = symbol(&int8)
	local box = symbol(&m.box(ty))
	local memreq_exp = `0
	local copiers = {}
	for k,v in pairs(vals) do
		local ty = (`box.obj.[k]).tree.type
		local kp
		if ty.ptr_basetype then
			kp = quote [box].obj.[k] = [ty] { [ptr] = [&ty.ptr_basetype]([ptr]) } ; end
		else
			kp = quote [box].obj.[k] = [ty]([ptr]) ; end
		end

		local cpy
		if type(v) ~= 'table' or #v ~= 2 then
			cpy = quote [kp] ; [ptr] = m.cpy(ptr, v) end
		end
		if type(v) == 'string' then
			memreq_const = memreq_const + #v + 1
		elseif type(v) == 'table' and v.tree and (v.tree.type.ptr_basetype == int8 or v.tree.type.ptr_basetype == uint8) then
			cpy = quote [kp]; [ptr] = [&int8](lib.mem.cpy([ptr], [v].ptr, [v].ct)) end
			if ty.ptr_basetype then
				cpy = quote [cpy]; [box].obj.[k].ct = [v].ct end
			end
		elseif type(v) == 'table' and v.asvalue and type(v:asvalue()) == 'string' then
			local str = tostring(v:asvalue())
			memreq_const = memreq_const + #str + 1
		elseif type(v) == 'table' and #v == 2 then
			local str,sz = v[1],v[2]
			if type(sz) == 'number' then
				memreq_const = memreq_const + sz
			elseif type(sz:asvalue()) == 'number' then
				memreq_const = memreq_const + sz:asvalue()
			else memreq_exp = `[sz] + [memreq_exp] end

			cpy = quote [kp] ; [ptr] = [&int8](lib.mem.cpy([ptr], str, sz)) end
			if ty.ptr_basetype then
				cpy = quote [cpy]; [box].obj.[k].ct = sz end
			end
		else
			memreq_exp = `(m.sz(v) + 1) + [memreq_exp] -- make room for NUL
			if ty.ptr_basetype then
				cpy = quote [cpy]; [box].obj.[k].ct = m.sz(v) end
			end
		end
		copiers[#copiers + 1] = cpy
	end

	return quote
		var sz: intptr = memreq_const + [memreq_exp]
		var [box] = [&m.box(ty)](lib.mem.heapa_raw(sz))
		var [ptr] = [box].storage
		[copiers]
	in [lib.mem.ptr(ty)] { ct = 1, ptr = &([box].obj) } end
end

return m

Deleted string.t version [a0d1de486b].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
-- vim: ft=terra
-- string.t: string classes

local m = {}

struct m.acc {
	buf: rawstring
	sz: intptr
	run: intptr
	space: intptr
}

local terra biggest(a: intptr, b: intptr)
	if a > b then return a else return b end
end

m.acc.methods = {
	init = terra(self: &m.acc, run: intptr)
		lib.dbg('initializing string accumulator')
		self.buf = [rawstring](lib.mem.heapa_raw(run))
		self.run = run
		self.space = run
		self.sz = 0
	end;
	free = terra(self: &m.acc)
		lib.dbg('freeing string accumulator')
		if self.buf ~= nil and self.space > 0 then
			lib.mem.heapf(self.buf)
		end
	end;
	crush = terra(self: &m.acc)
		lib.dbg('crushing string accumulator')
		self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.sz))
		self.space = self.sz
		return self
	end;
}

m.acc.methods.finalize = terra(self: &m.acc)
	lib.dbg('finalizing string accumulator')
	self:crush()
	var pt: lib.mem.ptr(int8)
	pt.ptr = self.buf
	pt.ct = self.sz
	self.buf = nil
	self.sz = 0
	return pt
end;
m.acc.methods.push = terra(self: &m.acc, str: rawstring, len: intptr)
	lib.dbg('pushing "',str,'" onto accumulator')
	if self.buf == nil then self:init(self.run) end
	if len == 0 then len = lib.str.sz(str) end
	if len >= self.space - self.sz then
		self.space = self.space + biggest(self.run,len + 1)
		self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.space))
	end
	lib.mem.cpy(self.buf + self.sz, str, len)
	self.sz = self.sz + len
	self.buf[self.sz] = 0
	return self
end;
m.acc.methods.ppush = terra(self: &m.acc, str: lib.mem.ptr(int8))
	self:push(str.ptr, str.ct)            return self end;
m.acc.methods.merge = terra(self: &m.acc, str: lib.mem.ptr(int8))
	self:push(str.ptr, str.ct) str:free() return self end;

return m
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<