| Comment: | milestone |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
419d1a1ebe4ca565be0130e83bed0022 |
| User & Date: | lexi on 2020-12-22 23:01:30 |
| Other Links: | manifest | tags |
|
2020-12-25
| ||
| 03:59 | big ol iteration check-in: 5b3a03ad34 user: lexi tags: trunk | |
|
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 | |
Modified backend/pgsql.t from [2e402a5b93] to [17bd63f8c3].
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 .. 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 .. 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 ... 176 177 178 179 180 181 182 183 184 185 186 187 188 189 ... 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 ... 345 346 347 348 349 350 351 352 353 354 355 356 357 358 ... 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 |
bio, rank, quota, key
from parsav_actors
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,
................................................................................
]];
};
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
................................................................................
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
................................................................................
local args, casts, counters, fixers, ft, yield = {}, {}, {}, {}, {}, {}
for i, ty in ipairs(q.params) do
args[i] = symbol(ty)
ft[i] = `1
if ty == rawstring then
counters[i] = `lib.trn([args[i]] == nil, 0, lib.str.sz([args[i]]))
casts[i] = `[&int8]([args[i]])
elseif ty:isintegral() then
counters[i] = ty.bytes
casts[i] = `[&int8](&[args[i]])
fixers[#fixers + 1] = quote
--lib.io.fmt('uid=%llu(%llx)\n',[args[i]],[args[i]])
[args[i]] = lib.math.netswap(ty, [args[i]])
end
................................................................................
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);
................................................................................
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)
................................................................................
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
|
| | > > > > | | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > < | | | > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > < | < < | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 .. 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 ... 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 ... 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 ... 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 ... 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 ... 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 |
bio, rank, quota, key
from parsav_actors
where id = $1::bigint
]];
};
actor_fetch_xid = {
params = {lib.mem.ptr(int8)}, sql = [[
select a.id, a.nym, a.handle, a.origin,
a.bio, a.rank, a.quota, a.key,
coalesce(a.handle || '@' || s.domain,
'@' || a.handle) as xid,
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 or
$1::text = ('@' || a.handle))
]];
};
actor_enum_local = {
params = {}, sql = [[
select id, nym, handle, origin,
bio, rank, quota, key,
................................................................................
]];
};
actor_enum = {
params = {}, sql = [[
select a.id, a.nym, a.handle, a.origin,
a.bio, a.rank, a.quota, a.key,
coalesce(a.handle || '@' || s.domain,
'@' || a.handle) as xid
from parsav_actors a
left join parsav_servers s on s.id = a.origin
]];
};
actor_auth_how = {
params = {rawstring, lib.store.inet}, sql = [[
with mts as (select a.kind from parsav_auth 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
};
actor_session_fetch = {
params = {uint64, lib.store.inet}, sql = [[
select a.id, a.nym, a.handle, a.origin,
a.bio, a.rank, a.quota, a.key,
coalesce(a.handle || '@' || s.domain,
'@' || a.handle) as xid,
au.restrict,
array['post' ] <@ au.restrict as can_post,
array['edit' ] <@ au.restrict as can_edit,
array['acct' ] <@ au.restrict as can_acct,
array['upload'] <@ au.restrict as can_upload,
array['censor'] <@ au.restrict as can_censor,
array['admin' ] <@ au.restrict as can_admin
from parsav_auth au
left join parsav_actors a on au.uid = a.id
left join parsav_servers s on a.origin = s.id
where au.aid = $1::bigint and au.blacklist = false and
(au.netmask is null or au.netmask >> $2::inet)
]];
};
}
local struct pqr {
sz: intptr
res: &lib.pq.PGresult
}
terra pqr:free() if self.sz > 0 then lib.pq.PQclear(self.res) end end
................................................................................
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!!
if self:null(row,col) then return nil end
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
if self:null(row,col) then return [lib.mem.ptr(int8)] {ptr=nil,ct=0} end
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
................................................................................
local args, casts, counters, fixers, ft, yield = {}, {}, {}, {}, {}, {}
for i, ty in ipairs(q.params) do
args[i] = symbol(ty)
ft[i] = `1
if ty == rawstring then
counters[i] = `lib.trn([args[i]] == nil, 0, lib.str.sz([args[i]]))
casts[i] = `[&int8]([args[i]])
elseif ty == lib.store.inet then -- assume not CIDR
counters[i] = `lib.trn([args[i]].pv == 4,4,16)+4
casts[i] = quote
var ipbuf: int8[20]
;[pqt[lib.store.inet](false)]([args[i]], [&uint8](&ipbuf))
in &ipbuf[0] end
elseif ty.ptr_basetype == int8 or ty.ptr_basetype == uint8 then
counters[i] = `[args[i]].ct
casts[i] = `[&int8]([args[i]].ptr)
elseif ty:isintegral() then
counters[i] = ty.bytes
casts[i] = `[&int8](&[args[i]])
fixers[#fixers + 1] = quote
--lib.io.fmt('uid=%llu(%llx)\n',[args[i]],[args[i]])
[args[i]] = lib.math.netswap(ty, [args[i]])
end
................................................................................
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};
bio = {`r:string(row,4), `r:len(row,4)+1};
handle = {`r:string(row, 2); `r:len(row,2) + 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};
bio = {`r:string(row,4), `r:len(row,4)+1};
handle = {`r:string(row, 2); `r:len(row,2) + 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);
................................................................................
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_fetch_xid = [terra(src: &lib.store.source, xid: lib.mem.ptr(int8))
var r = queries.actor_fetch_xid.exec(src, xid)
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)
................................................................................
end];
actor_auth_how = [terra(
src: &lib.store.source,
ip: lib.store.inet,
username: rawstring
)
var cs: lib.store.credset cs:clear();
var r = queries.actor_auth_how.exec(src, username, ip)
if r.sz == 0 then return cs end -- just in case
defer r:free()
(cs.pw << r:bool(0,0))
(cs.otp << r:bool(0,1))
(cs.challenge << r:bool(0,2))
(cs.trust << r:bool(0,3))
return cs
end];
actor_auth_pw = [terra(
src: &lib.store.source,
ip: lib.store.inet,
username: rawstring,
cred: rawstring
)
var q = [[select a.aid from parsav_auth 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, q, 256, ip, username, cred) ] -- most common
[ checksha(`src.handle, q, 512, ip, username, cred) ] -- most secure
[ checksha(`src.handle, q, 384, ip, username, cred) ] -- weird
[ checksha(`src.handle, q, 224, ip, username, cred) ] -- weirdest
-- TODO: check pbkdf2-hmac
-- TODO: check OTP
return 0
end];
actor_session_fetch = [terra(
src: &lib.store.source,
aid: uint64,
ip : lib.store.inet
): { lib.stat(lib.store.auth), lib.mem.ptr(lib.store.actor) }
var r = queries.actor_session_fetch.exec(src, aid, ip)
if r.sz == 0 then goto fail end
do defer r:free()
if r:null(0,0) then goto fail end
var a = row_to_actor(&r, 0)
a.ptr.source = src
var au = [lib.stat(lib.store.auth)] { ok = true }
au.val.aid = aid
au.val.uid = a.ptr.id
if not r:null(0,10) then -- restricted?
au.val.privs:clear()
(au.val.privs.post << r:bool(0,11))
(au.val.privs.edit << r:bool(0,12))
(au.val.privs.acct << r:bool(0,13))
(au.val.privs.upload << r:bool(0,14))
(au.val.privs.censor << r:bool(0,15))
(au.val.privs.admin << r:bool(0,16))
else au.val.privs:fill() end
return au, a
end
::fail:: return [lib.stat (lib.store.auth) ] { ok = false },
[lib.mem.ptr(lib.store.actor)] { ptr = nil, ct = 0 }
end];
}
return b
|
Modified cmdparse.t from [bad20dd1d0] to [50677a3c0c].
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 |
return function(tbl)
local options = terralib.types.newstruct('options') do
local flags = '' for _,d in pairs(tbl) do flags = flags .. d[1] end
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
var j = 1
while arg[j] ~= 0 do
switch arg[j] do [shortcases] end
j = j + 1
end
end
else
self.arglist.ptr[finalargc] = arg
finalargc = finalargc + 1
end
::[skip]::
end
if finalargc == 0 then self.arglist:free()
else self.arglist:resize(finalargc) end
end
options.helptxt = helpstr
end
return options
end
|
| | > > > | | | > | < > > > > > > > > | | | | | > | | < | > |
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 |
return function(tbl)
local options = terralib.types.newstruct('options') do
local flags = '' for _,d in pairs(tbl) do flags = flags .. d[1] end
local helpstr = 'usage: parsav [-' .. flags .. '] [<arg>...]\n'
options.entries = {
{field = 'arglist', type = lib.mem.ptr(rawstring)}
}
local shortcases, longcases, init, verifiers = {}, {}, {}, {}
local self = symbol(&options)
local arg = symbol(rawstring)
local idx = symbol(uint)
local argv = symbol(&rawstring)
local argc = symbol(int)
local optstack = symbol(intptr)
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 &rawstring 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 `nil) or false] end
local ch if consume > 0 then
ch = quote
[self].[o] = argv+(idx+1+optstack)
optstack = optstack + consume
end
verifiers[#verifiers+1] = quote
var terminus = argv + argc
if [self].[o] ~= nil and [self].[o] >= terminus then
lib.bail(['missing argument for command line option ' .. sanitize(o)])
end
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], [argv])
[init]
var parseopts = true
var [optstack] = 0
self.arglist = lib.mem.heapa(rawstring, argc)
var finalargc = 0
for [idx]=1,argc do
var [arg] = argv[idx]
if optstack > 0 then optstack = optstack - 1 goto [skip] end
if arg[0] == @'-' and parseopts then
if arg[1] == @'-' then -- long option
if arg[2] == 0 then -- last option
parseopts = false
else [longcases] end
else -- short options
var j = 1 while arg[j] ~= 0 do
switch arg[j] do [shortcases] end
j = j + 1
end
end
else
self.arglist.ptr[finalargc] = arg
finalargc = finalargc + 1
end
::[skip]::
end
[verifiers]
if finalargc == 0 then self.arglist:free()
else self.arglist:resize(finalargc) end
end
options.helptxt = helpstr
end
return options
end
|
Modified config.lua from [53779583db] to [7cb566b503].
36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
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')
|
> > > |
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
id = u.rndstr(6);
release = u.ingest('release');
when = os.date();
};
feat = {};
backends = defaultlist('parsav_backends', 'pgsql');
braingeniousmode = false;
embeds = {
{'style.css', 'text/css'};
};
}
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 [709e2a6426] to [48369b50e0].
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
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
elseif config.feat.randomizer == 'devfs' then
|
> > > > > > > > | | | | | | | | < |
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 |
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];
algsz = {
sha1 = 160/8;
sha256 = 256/8;
sha512 = 512/8;
sha384 = 384/8;
sha224 = 224/8;
}
}
m.alg = {
sha1 = `hashalg {id = lib.md.MBEDTLS_MD_SHA1; bytes = m.algsz.sha1};
sha256 = `hashalg {id = lib.md.MBEDTLS_MD_SHA256; bytes = m.algsz.sha256};
sha512 = `hashalg {id = lib.md.MBEDTLS_MD_SHA512; bytes = m.algsz.sha512};
sha384 = `hashalg {id = lib.md.MBEDTLS_MD_SHA384; bytes = m.algsz.sha384};
sha224 = `hashalg {id = lib.md.MBEDTLS_MD_SHA224; bytes = m.algsz.sha224};
-- 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
elseif config.feat.randomizer == 'devfs' then
|
Modified file.t from [fc7770c3f7] to [a196c3def2].
1
2
3
4
5
6
7
8
9
10
11
12
..
61
62
63
64
65
66
67
68
69
|
-- vim: ft=terra
-- TODO: add support for windows IO calls
local handle_type = int
local posix = terralib.includec 'fcntl.h'
local unistd = terralib.includec 'unistd.h'
struct file {
handle: handle_type
read: bool
write: bool
}
................................................................................
elseif wh == [file.seek.eof] then
whence = unistd.SEEK_END
else lib.bail('invalid seek mode') end
return unistd.lseek(self.handle, ofs, whence)
end;
}
return file
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
..
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
|
-- vim: ft=terra -- TODO: add support for windows IO calls local handle_type = int local posix = terralib.includec 'fcntl.h' local unistd = terralib.includec 'unistd.h' local mm = terralib.includec 'sys/mman.h' struct file { handle: handle_type read: bool write: bool } ................................................................................ elseif wh == [file.seek.eof] then whence = unistd.SEEK_END else lib.bail('invalid seek mode') end return unistd.lseek(self.handle, ofs, whence) end; } terra file:len(): intptr var cur = self:seek(0, [file.seek.ofs]) var sz = self:seek(0, [file.seek.eof]) self:seek(cur, [file.seek.abs]) return sz end local struct mapping { addr: &opaque sz: intptr } terra mapping:unmap() lib.dbg('releasing file mapping') return mm.munmap(self.addr, self.sz) end -- provide for syncing mechanism? terra file:mapin(ofs: intptr, sz: intptr) var prot = 0 if self.read then prot = mm.PROT_READ end if self.write then prot = prot or mm.PROT_WRITE end if sz == 0 then sz = self:len() end lib.dbg('mapping file into memory') return mapping { addr = mm.mmap(nil, sz, prot, mm.MAP_PRIVATE, self.handle, ofs); sz = sz; } end return file |
Modified http.t from [6d6c40fc94] to [e5e590a634].
1 2 3 4 5 6 7 8 9 10 11 .. 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 .. 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
-- vim: ft=terra
local m = {}
local util = dofile('common.lua')
struct m.header {
key: rawstring
value: rawstring
}
struct m.page {
respcode: uint16
body: lib.mem.ptr(int8)
................................................................................
local resps = {
[200] = 'OK';
[201] = 'Created';
[301] = 'Moved Permanently';
[302] = 'Found';
[303] = 'See Other';
[307] = 'Temporary Redirect';
[404] = 'Not Found';
[401] = 'Unauthorized';
[403] = 'Forbidden';
[418] = 'I\'m a teapot';
[405] = 'Method Not Allowed';
[500] = 'Internal Server Error';
}
local resptext = symbol(rawstring)
local resplen = symbol(intptr)
local respbranches = {}
................................................................................
end
end
m.codestr = terra(code: uint16)
var [resptext] var [resplen]
switch code do [respbranches] end
return resptext, resplen
end
m.page.methods = {
free = terra(self: &m.page)
self.body:free()
self.headers:free()
end;
send = terra(self: &m.page, con: &lib.net.mg_connection)
var code: rawstring
|
> > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .. 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 .. 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 |
-- vim: ft=terra
local m = {}
local util = dofile('common.lua')
m.method = lib.enum { 'get', 'post', 'head', 'options', 'put', 'delete' }
m.findheader = terralib.externfunction('mg_http_get_header', {&lib.net.mg_http_message, rawstring} -> &lib.mem.ref(int8)) -- unfortunately necessary to access this function, as its return type conflicts with a function name
struct m.header {
key: rawstring
value: rawstring
}
struct m.page {
respcode: uint16
body: lib.mem.ptr(int8)
................................................................................
local resps = {
[200] = 'OK';
[201] = 'Created';
[301] = 'Moved Permanently';
[302] = 'Found';
[303] = 'See Other';
[307] = 'Temporary Redirect';
[400] = 'Bad Request';
[401] = 'Unauthorized';
[402] = 'Payment Required';
[403] = 'Forbidden';
[404] = 'Not Found';
[405] = 'Method Not Allowed';
[406] = 'Not Acceptable';
[418] = 'I\'m a teapot';
[405] = 'Method Not Allowed';
[500] = 'Internal Server Error';
}
local resptext = symbol(rawstring)
local resplen = symbol(intptr)
local respbranches = {}
................................................................................
end
end
m.codestr = terra(code: uint16)
var [resptext] var [resplen]
switch code do [respbranches] end
return resptext, resplen
end
terra m.hier(uri: lib.mem.ptr(int8)): lib.mem.ptr(lib.mem.ref(int8))
if uri.ct == 0 then return [lib.mem.ptr(lib.mem.ref(int8))] { ptr = nil, ct = 0 } end
var sz = 1
var start = 0 if uri.ptr[0] == @'/' then start = 1 end
for i = start, uri.ct do if uri.ptr[i] == @'/' then sz = sz + 1 end end
var lst = lib.mem.heapa([lib.mem.ref(int8)], sz)
if sz == 0 then
lst.ptr[0].ptr = uri.ptr
lst.ptr[0].ct = uri.ct
return lst
end
var idx: intptr = 0
var len: intptr = 0
lst.ptr[0].ptr = uri.ptr
for i = 0, uri.ct do
if uri.ptr[i] == @'/' then
if len == 0 then
lst.ptr[idx].ptr = lst.ptr[idx].ptr + 1
goto skip
end
lst.ptr[idx].ct = len
idx = idx + 1
lst.ptr[idx].ptr = uri.ptr + i + 1
len = 0
else
len = len + 1
end
::skip::end
lst.ptr[idx].ct = len
return lst
end
m.page.methods = {
free = terra(self: &m.page)
self.body:free()
self.headers:free()
end;
send = terra(self: &m.page, con: &lib.net.mg_connection)
var code: rawstring
|
Modified makefile from [40c8adaeb1] to [2d2acfe121].
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
mkdir $@ # generate a shim static library so mongoose cooperates # with the build apparatus. note that parsav is designed # to be fronted by a real web server like nginx if SSL # is to be used, so we don't turn on SSL in mongoose lib/mongoose/libmongoose.a: lib/mongoose lib/mongoose/mongoose.c lib/mongoose/mongoose.h $(CC) -c $</mongoose.c -o lib/mongoose/mongoose.o \ -DMG_ENABLE_THREADS \ -DMG_ENABLE_IPV6 \ -DMG_ENABLE_HTTP_WEBDAV \ -DMG_ENABLE_HTTP_WEBSOCKET=0 ar rcs $@ lib/mongoose/*.o ranlib $@ lib/json-c/Makefile: lib/json-c lib/json-c/CMakeLists.txt cd lib/json-c && cmake . lib/json-c/libjson-c.a: lib/json-c/Makefile |
| | | |
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
mkdir $@ # generate a shim static library so mongoose cooperates # with the build apparatus. note that parsav is designed # to be fronted by a real web server like nginx if SSL # is to be used, so we don't turn on SSL in mongoose lib/mongoose/libmongoose.a: lib/mongoose lib/mongoose/mongoose.c lib/mongoose/mongoose.h $(CC) -c $</mongoose.c -o lib/mongoose/mongoose.o \ -DMG_ENABLE_THREADS=1 \ -DMG_ENABLE_IPV6=1 \ -DMG_ENABLE_HTTP_WEBDAV=1 \ -DMG_ENABLE_HTTP_WEBSOCKET=0 ar rcs $@ lib/mongoose/*.o ranlib $@ lib/json-c/Makefile: lib/json-c lib/json-c/CMakeLists.txt cd lib/json-c && cmake . lib/json-c/libjson-c.a: lib/json-c/Makefile |
Modified math.t from [f642fd73ba] to [f661c3d77e].
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
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.truncate64(val: &uint8, len: intptr): uint64 var r: uint64 = 0 for i=0, len do r = r << 3 r = r + @val val = val + 1 end return r end m.biggest = macro(function(a,b) local ty = a.tree.type if b.tree.type.bytes > ty.bytes then ty = b.tree.type end return quote var _a = [a] var _b = [b] var r: ty if _a > _b then r = _a else r = _b end in r end end) m.smallest = macro(function(a,b) local ty = a.tree.type if b.tree.type.bytes < ty.bytes then ty = b.tree.type end return quote var _a = [a] var _b = [b] var r: ty if _a < _b then r = _a else r = _b end in r end 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 |
Modified mem.t from [07d2cb4a7e] to [e41dd1991a].
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 |
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};
|
| | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > |
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 |
local p = m.ptr(ty:astype())
return `p {
ptr = [&ty:astype()](m.heapa_raw(sizeof(ty) * sz));
ct = sz;
}
end)
local function mkptr(ty, dyn)
local t = terralib.types.newstruct(string.format('%s<%s>', dyn and 'ptr' or 'ref', 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)
if dyn then
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;
}
terra t:ensure(n: intptr)
if self.ptr == nil then
if not self:init(n) then return t {ptr=nil,ct=0} end
end
if self.ct >= n then return @self end
self:resize(n)
return @self
end
end
terra t:advance(n: intptr)
self.ptr = self.ptr + n
self.ct = self.ct - n
return self.ptr
end
if not ty:isstruct() then
terra t:cmp_raw(other: &ty)
for i=0, self.ct do
if self.ptr[i] ~= other[i] then return false end
end
return true
end
terra t:cmp(other: t)
if other.ct ~= self.ct then return false end
return self:cmp_raw(other.ptr)
end
end
return t
end
m.ptr = terralib.memoize(function(ty) return mkptr(ty, true) end)
m.ref = terralib.memoize(function(ty) return mkptr(ty, false) 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};
|
Modified parsav.t from [fcc06cebed] to [41c6f93980].
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 .. 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 .. 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 .. 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 .. 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 ... 159 160 161 162 163 164 165 166 167 168 169 170 171 172 ... 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 ... 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 |
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 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]])
................................................................................
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
................................................................................
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);
proc = {
exit = terralib.externfunction('exit', int -> {});
getenv = terralib.externfunction('getenv', rawstring -> rawstring);
};
io = {
send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
................................................................................
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 {
ok: bool
union {
................................................................................
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)
................................................................................
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')()
do local collate = function(path,f, ...)
return loadfile(path..'/'..f..'.lua')(path, ...)
end
data = {
view = collate('view','load');
} end
-- slightly silly -- because we're using plain lua to gather up
-- the template sources, they have to be actually turned into
-- templates in the terra code, so we "mutate" them here
for k,v in pairs(data.view) do
local t = lib.tpl.mk { body = v, id = 'view/'..k }
data.view[k] = t
end
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
terra noise_init()
var n = lib.proc.getenv('parsav_noise')
if n ~= nil then
if n[0] >= 0x30 and n[0] <= 0x39 and n[1] == 0 then
noise = n[0] - 0x30
return
end
end
................................................................................
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
|
> > > > > > > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > | < | < | | > | | < > > > > > > > | | | | | < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | < < |
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 .. 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 .. 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 .. 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 ... 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 ... 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 ... 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 ... 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 |
local buildopts, buildargs = util.parseargs{...}
config = dofile('config.lua')
lib = {
init = {};
load = function(lst)
for _, l in pairs(lst) do
local path = {}
for m in l:gmatch('([^:]+)') do path[#path+1]=m end
local tgt = lib
for i=1,#path-1 do
if tgt[path[i]] == nil then tgt[path[i]] = {} end
tgt = tgt[path[i]]
end
tgt[path[#path]] = terralib.loadfile(l:gsub(':','/') .. '.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 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(nl,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]])
................................................................................
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
if nl then code[#code+1] = `lib.io.send(fd, '\n', 1) end
return code
end;
emitv = function(nl,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
................................................................................
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
if nl then vec[#vec + 1] = `[lib.uio.iovec]{iov_base = [&opaque]('\n'), iov_len = 1} end
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);
coalesce = macro(function(...)
local args = {...}
local ty = args[1].tree.type
local val = symbol(ty)
local empty if ty.type == 'integer'
then empty = `0
else empty = `nil
end
local exp = quote val = [empty] end
for i=#args, 1, -1 do
local v = args[i]
exp = quote
if [v] ~= [empty]
then val = v
else [exp]
end
end
end
local q = quote
var [val]
[exp]
in val end
return q
end);
proc = {
exit = terralib.externfunction('exit', int -> {});
getenv = terralib.externfunction('getenv', rawstring -> rawstring);
};
io = {
send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
................................................................................
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;
osclock = terralib.includec 'time.h';
}
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 starttime = global(lib.osclock.time_t)
local lastnoisetime = global(lib.osclock.time_t)
local noise = global(uint8,1)
local noise_header = function(code,txt,mod)
if mod then
return string.format('\27[%s;1m(%s %s)\27[m ', code,mod,txt)
else
return string.format('\27[%s;1m(%s)\27[m ', code,txt)
end
end
local terra timehdr()
var now = lib.osclock.time(nil)
var diff = now - lastnoisetime
if diff > 30 then -- print cur time
lastnoisetime = now
var curtime: int8[26]
lib.osclock.ctime_r(&now, &curtime[0])
for i=0,26 do if curtime[i] == @'\n' then curtime[i] = 0 break end end -- :/
[ lib.emit(false, 2, '\27[1m[', `&curtime[0], ']\27[;36m\n +00 ') ]
else -- print time since last msg
var dfs = arrayof(int8, 0x30 + diff/10, 0x30 + diff%10, 0x20, 0)
[ lib.emit(false, 2, ' \27[36m+', `&dfs[0]) ]
end
end
local defrep = function(level,n,code)
return macro(function(...)
local q = lib.emit(true, 2, noise_header(code,n), ...)
return quote if noise >= level then timehdr(); [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(true, 2, noise_header('31','fatal'), ...)
return quote
timehdr(); [q]
lib.proc.exit(1)
end
end);
lib.stat = terralib.memoize(function(ty)
local n = struct {
ok: bool
union {
................................................................................
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
terra set:fill() for i=0,bytes do self._store[i] = 0xFF 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)
................................................................................
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', 'math', 'str', 'file', 'crypt';
'http', 'session', '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')()
do local collate = function(path,f, ...)
return loadfile(path..'/'..f..'.lua')(path, ...)
end
data = {
view = collate('view','load');
static = {};
stmap = global(lib.mem.ref(int8)[#config.embeds]); -- array of pointers to static content
} end
for i,e in ipairs(config.embeds) do local v = e[1]
local fh = io.open('static/' .. v,'r')
if fh == nil then error('static file ' .. v .. ' missing') end
data.static[v] = fh:read('*a') fh:close()
end
-- slightly silly -- because we're using plain lua to gather up
-- the template sources, they have to be actually turned into
-- templates in the terra code, so we "mutate" them here
for k,v in pairs(data.view) do
local t = lib.tpl.mk { body = v, id = 'view/'..k }
data.view[k] = t
end
lib.load {
'srv';
'render:profile';
'render:userpage';
'route';
}
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
terra noise_init()
starttime = lib.osclock.time(nil)
lastnoisetime = 0
var n = lib.proc.getenv('parsav_noise')
if n ~= nil then
if n[0] >= 0x30 and n[0] <= 0x39 and n[1] == 0 then
noise = n[0] - 0x30
return
end
end
................................................................................
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};
static_dir = {'S', 'directory with overrides for static content', 1};
builtin_data = {'B', 'do not load static content overrides at runtime under any circumstances'};
}
local static_setup = quote end
local mapin = quote end
local odir = symbol(rawstring)
local pathbuf = symbol(lib.str.acc)
for i,e in ipairs(config.embeds) do local v = e[1]
local d = data.static[v]
static_setup = quote [static_setup]
([data.stmap])[([i-1])] = ([lib.mem.ref(int8)] { ptr = [d], ct = [#d] })
end
mapin = quote [mapin]
var osz = pathbuf.sz
pathbuf:push([v],[#v])
var f = lib.file.open(pathbuf.buf, [lib.file.mode.read])
if f.ok then defer f.val:close()
var map = f.val:mapin(0,0) -- currently maps are leaked, maybe do something more elegant in future
lib.report('loading static override content from ', pathbuf.buf)
([data.stmap])[([i-1])] = ([lib.mem.ref(int8)] {
ptr = [rawstring](map.addr);
ct = map.sz;
})
end
pathbuf.sz = osz
end
end
local terra static_init(mode: &options)
[static_setup]
if mode.builtin_data then return end
var [odir] = lib.proc.getenv('parsav_override_dir')
if mode.static_dir ~= nil then
odir=@mode.static_dir
end
if odir == nil then return end
var [pathbuf] defer pathbuf:free()
pathbuf:compose(odir,'/')
[mapin]
end
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.overlord
do var mode: options
mode:parse(argc,argv) defer mode:free()
static_init(&mode)
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 ~= nil
then cnf = @mode.backend_file
else cnf = lib.proc.getenv('parsav_backend_file')
end
if cnf == nil then cnf = "backend.conf" end
srv:start(cnf)
end
|
Added render/profile.t version [a405db9158].
> > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-- vim: ft=terra local terra render_profile(actor: &lib.store.actor) var profile = data.view.profile { nym = lib.coalesce(actor.nym, actor.handle); bio = lib.coalesce(actor.bio, "tall, dark, and mysterious"); xid = actor.xid; avatar = "/no-avatars-yet.png"; nposts = '0', nfollows = '0'; nfollowers = '0', nmutuals = '0'; tweetday = 'novembuary 67th'; } return profile:tostr() end return render_profile |
Added render/userpage.t version [052285d84c].
> > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local terra render_userpage(co: &lib.srv.convo, actor: &lib.store.actor) var ti: lib.str.acc defer ti:free() if co.aid ~= 0 and co.who.id == actor.id then ti:compose('my profile') else ti:compose('profile :: ', actor.handle) end var pftxt = lib.render.profile(actor) defer pftxt:free() var doc = data.view.docskel { instance = co.srv.cfg.instance.ptr; title = ti.buf; body = pftxt.ptr; class = 'profile'; } var hdrs = array( lib.http.header { 'Content-Type', 'text/html; charset=UTF-8' } ) doc:send(co.con,200,[lib.mem.ptr(lib.http.header)] {ct = 1, ptr = &hdrs[0]}) end return render_userpage |
Added route.t version [70d0da6b8e].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local r = lib.srv.route local method = lib.http.method local http = {} terra http.actor_profile_xid(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t) var handle = [lib.mem.ptr(int8)] { ptr = &uri.ptr[2], ct = 0 } for i=2,uri.ct do if uri.ptr[i] == @'/' or uri.ptr[i] == 0 then handle.ct = i - 2 break end end if handle.ct == 0 then handle.ct = uri.ct - 2 uri:advance(uri.ct) else if handle.ct + 2 < uri.ct then uri:advance(handle.ct + 2) --uri.ptr = uri.ptr + (handle.ct + 2) --uri.ct = uri.ct - (handle.ct + 2) end end lib.dbg('looking up user by xid "', {handle.ptr,handle.ct} ,'", path: ', {uri.ptr,uri.ct}) var path = lib.http.hier(uri) defer path:free() for i=0,path.ct do lib.dbg('got path component ', {path.ptr[i].ptr, path.ptr[i].ct}) end var actor = co.srv:actor_fetch_xid(handle) if actor.ptr == nil then co:complain(404,'no such user','no such user known to this server') return end defer actor:free() lib.render.userpage(co, actor.ptr) end terra http.actor_profile_uid(co: &lib.srv.convo, path: lib.mem.ptr(lib.mem.ref(int8)), meth: method.t) if path.ct < 2 then co:complain(404,'bad url','invalid user url') return end var uid, ok = lib.math.shorthand.parse(path.ptr[1].ptr, path.ptr[1].ct) if not ok then co:complain(400, 'bad user ID', 'that user ID is not valid') return end var actor = co.srv:actor_fetch_uid(uid) if actor.ptr == nil then co:complain(404, 'no such user', 'no user by that ID is known to this instance') return end defer actor:free() lib.render.userpage(co, actor.ptr) end do local branches = quote end local filename, flen = symbol(&int8), symbol(intptr) local page = symbol(lib.http.page) local send = label() local storage = data.stmap for i,e in ipairs(config.embeds) do local id,mime = e[1],e[2] local d = data.static[id] branches = quote [branches]; if lib.str.ncmp(filename, id, lib.math.biggest([#id], flen)) == 0 then page.headers.ptr[0].value = mime; page.body = [lib.mem.ptr(int8)] { ptr = storage[([i-1])].ptr; ct = storage[([i-1])].ct; } goto [send] end end end terra http.static_content(co: &lib.srv.convo, [filename], [flen]) var hdrs = array(lib.http.header{'Content-Type',nil}) var [page] = lib.http.page { respcode = 200; headers = [lib.mem.ptr(lib.http.header)] { ptr = &hdrs[0], ct = 1 } } [branches] do return false end ::[send]:: page:send(co.con) return true end end http.static_content:printpretty() -- entry points terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t) if uri.ptr[0] ~= @'/' then co:complain(404, 'what the hell', 'how did you do that') elseif uri.ptr[1] == @'@' then http.actor_profile_xid(co, uri, meth) elseif uri.ptr[1] == @'s' and uri.ptr[2] == @'/' and uri.ct > 3 then if meth ~= method.get then goto wrongmeth end if not http.static_content(co, uri.ptr + 3, uri.ct - 3) then goto notfound end else var path = lib.http.hier(uri) defer path:free() if path.ptr[0]:cmp(lib.str.lit('user')) then http.actor_profile_uid(co, path, meth) else goto notfound end end ::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this path') do return end ::notfound:: co:complain(404, 'not found', 'no such resource available') do return end end |
Modified schema.sql from [6d12737279] to [636689e0dd].
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 |
\prompt 'domain name: ' domain \prompt 'bind to socket: ' bind \qecho 'by default, parsav tracks rights on its own. you can override this later by replacing the rights table with a view, but you''ll then need to set appropriate rules on the view to allow administrators to modify rights from the web UI, or set the rights-readonly flag in the config table to true. for now, enter the name of an actor who will be granted full rights when she logs in.' \prompt 'admin actor: ' admin \qecho 'you will need to create an authentication view mapping your user database to something parsav can understand; see auth.sql for an example. enter the name of the view to use.' \prompt 'auth view: ' auth begin; drop table if exists parsav_config; create table if not exists parsav_config ( key text primary key, 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 |
> | < | |
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 |
\prompt 'domain name: ' domain \prompt 'instance name: ' inst \prompt 'bind to socket: ' bind \qecho 'by default, parsav tracks rights on its own. you can override this later by replacing the rights table with a view, but you''ll then need to set appropriate rules on the view to allow administrators to modify rights from the web UI, or set the rights-readonly flag in the config table to true. for now, enter the name of an actor who will be granted full rights when she logs in.' \prompt 'admin actor: ' admin \qecho 'you will need to create an authentication view named parsav_auth mapping your user database to something parsav can understand; see auth.sql for an example.' begin; drop table if exists parsav_config; create table if not exists parsav_config ( key text primary key, value text ); insert into parsav_config (key,value) values ('bind',:'bind'), ('domain',:'domain'), ('instance-name',:'inst'), ('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 |
Added session.t version [58f0eab21d].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra -- sessions are implemented so as to avoid any local data storage. they -- are tracked by storing an encrypted cookie which contains an authid, -- a login epoch time, and a truncated hmac code authenticating both, all -- encoded using Shorthand. we need functions to generate and parse these local m = { maxlen = lib.math.shorthand.maxlen*3 + 2; maxage = 2 * 60 * 60; -- 2 hours } terra m.cookie_gen(secret: lib.mem.ptr(int8), authid: uint64, time: uint64, out: &int8): intptr var ptr = out ptr = ptr + lib.math.shorthand.gen(authid, ptr) @ptr = @'.' ptr = ptr + 1 ptr = ptr + lib.math.shorthand.gen(time, ptr) @ptr = @'.' ptr = ptr + 1 var len = ptr - out var hash: uint8[lib.crypt.algsz.sha256] lib.crypt.hmac(lib.crypt.alg.sha256, [lib.mem.ptr(uint8)] {ptr = [&uint8](secret.ptr), ct = secret.ct}, [lib.mem.ptr( int8)] {ptr = out, ct = len}, &hash[0]) ptr = ptr + lib.math.shorthand.gen(lib.math.truncate64(hash, [hash.type.N]), ptr) return ptr - out end terra m.cookie_interpret(secret: lib.mem.ptr(int8), c: lib.mem.ptr(int8), now: uint64): uint64 -- returns either 0 or a valid authid var authid_sz = lib.str.cspan(c.ptr, lib.str.lit '.', c.ct) if authid_sz == 0 then return 0 end if authid_sz + 1 > c.ct then return 0 end var time_sz = lib.str.cspan(c.ptr+authid_sz+1, lib.str.lit '.', c.ct - (authid_sz+1)) if time_sz == 0 then return 0 end if (authid_sz + time_sz + 2) > c.ct then return 0 end var hash_sz = c.ct - (authid_sz + time_sz + 2) var knownhash: uint8[lib.crypt.algsz.sha256] lib.crypt.hmac(lib.crypt.alg.sha256, [lib.mem.ptr(uint8)] {ptr = [&uint8](secret.ptr), ct = secret.ct}, [lib.mem.ptr( int8)] {ptr = c.ptr, ct = c.ct - hash_sz}, &knownhash[0]) var authid, authok = lib.math.shorthand.parse(c.ptr, authid_sz) var time, timeok = lib.math.shorthand.parse(c.ptr + authid_sz + 1, time_sz) var hash, hashok = lib.math.shorthand.parse(c.ptr + c.ct - hash_sz, hash_sz) if not (timeok and authok and hashok) then return 0 end if lib.math.truncate64(knownhash, [knownhash.type.N]) ~= hash then return 0 end if now - time > m.maxage then return 0 end return authid end return m |
Modified srv.t from [aed7239c9c] to [ed3d5ec62e].
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 ... 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 ... 189 190 191 192 193 194 195 196 |
-- vim: ft=terra
local util = dofile 'common.lua'
local struct srv {
sources: lib.mem.ptr(lib.store.source)
webmgr: lib.net.mg_mgr
webcon: &lib.net.mg_connection
}
local handle = {
http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque)
switch event do
case lib.net.MG_EV_HTTP_MSG then
lib.dbg('routing HTTP request')
var msg = [&lib.net.mg_http_message](p)
end
end
end;
}
local char = macro(function(ch) return `[string.byte(ch:asvalue())] end)
local terra cfg(s: &srv, befile: rawstring)
lib.report('configuring backends from ', befile)
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
f:close()
var cur = text.buf
var segs: tuple(&int8, &int8)[3] = array(
{[&int8](0),[&int8](0)},
{[&int8](0),[&int8](0)},
{[&int8](0),[&int8](0)}
)
var segdup = [terra(s: {rawstring, rawstring})
var sz = s._1 - s._0
var str = s._0
return [lib.mem.ptr(int8)] {
ptr = lib.str.ndup(str, sz);
ct = sz;
}
end]
var fld = 0
while (cur - text.buf) < text.sz do
if segs[fld]._0 == nil then
if not (@cur == char(' ') or @cur == char('\t') or @cur == char('\n')) then
segs[fld] = {cur, nil}
end
else
if fld < 2 and @cur == char(' ') or @cur == char('\t') then
segs[fld]._1 = cur
fld = fld + 1
segs[fld] = {nil, nil}
elseif @cur == char('\n') or cur == text.buf + (text.sz-1) then
if fld < 2 then lib.bail('incomplete backend line in ', befile) else
segs[fld]._1 = cur
var src = c:new()
src.id = segdup(segs[0])
src.string = segdup(segs[2])
src.backend = nil
for i = 0,[lib.store.backends.type.N] do
if lib.str.ncmp(segs[1]._0, lib.store.backends[i].id, segs[1]._1 - segs[1]._0) == 0 then
src.backend = &lib.store.backends[i]
break
end
end
if src.backend == nil then
lib.bail('unknown backend in ', befile)
end
src.handle = nil
fld = 0
segs[0] = {nil, nil}
end
end
end
cur = cur + 1
end
text:free()
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 [ok] then break
else r = empty end
end
end
in r end
end
end)
srv.methods.start = terra(self: &srv, befile: rawstring)
cfg(self, befile)
var success = false
for i=0,self.sources.ct do var src = self.sources.ptr + i
lib.report('opening data source ', src.id.ptr, '(', src.backend.id, ')')
src.handle = src.backend.open(src)
if src.handle ~= nil then success = true end
end
if not success then
lib.bail('could not connect to any data sources!')
end
var dbbind = self:conf_get('bind')
var envbind = lib.proc.getenv('parsav_bind')
var bind: rawstring
if envbind ~= nil then
bind = envbind
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
................................................................................
for i=0,self.sources.ct do var src = self.sources.ptr + i
lib.report('closing data source ', src.id.ptr, '(', src.backend.id, ')')
src:close()
end
self.sources:free()
end
return srv
|
> > > > > > > > < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < > < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > |
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 .. 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 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 ... 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
-- vim: ft=terra local util = dofile 'common.lua' local struct srv local struct cfgcache { secret: lib.mem.ptr(int8) instance: lib.mem.ptr(int8) overlord: &srv } local struct srv { sources: lib.mem.ptr(lib.store.source) webmgr: lib.net.mg_mgr webcon: &lib.net.mg_connection cfg: cfgcache } terra cfgcache:free() -- :/ self.secret:free() self.instance:free() 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 [ok] then break else r = empty end end end in r end end end) local struct convo { srv: &srv con: &lib.net.mg_connection msg: &lib.net.mg_http_message aid: uint64 -- 0 if logged out who: &lib.store.actor -- who we're logged in as, if aid ~= 0 } -- this is unfortunately necessary to work around a terra bug -- it can't seem to handle forward-declarations of structs in C local getpeer do local struct strucheader { next: &lib.net.mg_connection mgr: &lib.net.mg_mgr peer: lib.net.mg_addr } terra getpeer(con: &lib.net.mg_connection) return [&strucheader](con).peer end end terra convo:complain(code: uint16, title: rawstring, msg: rawstring) var hdrs = array(lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' }) var ti: lib.str.acc ti:compose('error :: ', title) defer ti:free() var body = data.view.docskel { instance = self.srv.cfg.instance.ptr; title = ti.buf; body = msg; class = 'error'; } if body.body == nil then body.body = "i'm sorry, dave. i can't let you do that" end body:send(self.con, code, [lib.mem.ptr(lib.http.header)] { ptr = &hdrs[0], ct = [hdrs.type.N] }) end local urimatch = macro(function(uri, ptn) return `lib.net.mg_globmatch(ptn, [#ptn], uri.ptr, uri.ct+1) end) local route = {} -- these are defined in route.t, as they need access to renderers terra route.dispatch_http :: {&convo, lib.mem.ptr(int8), lib.http.method.t} -> {} local handle = { http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque) var server = [&srv](ext) var mgpeer = getpeer(con) var peer = lib.store.inet { port = mgpeer.port; } if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end if peer.pv == 6 then for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end else -- v4 @[&uint32](&peer.v4) = mgpeer.ip end -- the peer property is currently broken and there is precious -- little i can do about this -- it always reports a peer v4 IP -- of 0.0.0.0, altho the port seems to come through correctly. -- for now i'm leaving it as is, but note that netmask restrictions -- WILL NOT WORK until upstream gets its shit together. FIXME switch event do case lib.net.MG_EV_HTTP_MSG then lib.dbg('routing HTTP request') var msg = [&lib.net.mg_http_message](p) var co = convo { con = con, srv = server, msg = msg; aid = 0, who = nil; } -- we need to check if there's any cookies sent with the request, -- and if so, whether they contain any credentials. this will be -- used to set the auth parameters in the http conversation var cookies_p = lib.http.findheader(msg, 'Cookie') if cookies_p ~= nil then var cookies = cookies_p.ptr var key = [lib.mem.ref(int8)] {ptr = cookies, ct = 0} var val = [lib.mem.ref(int8)] {ptr = nil, ct = 0} var i = 0 while i < cookies_p.ct and cookies[i] ~= 0 and cookies[i] ~= @'\r' and cookies[i] ~= @'\n' do -- cover all the bases if val.ptr == nil then if cookies[i] == @'=' then key.ct = (cookies + i) - key.ptr val.ptr = cookies + i + 1 end i = i + 1 else if cookies[i] == @';' then val.ct = (cookies + i) - val.ptr if lib.str.ncmp(key.ptr, 'auth', key.ct) == 0 then goto foundcookie end i = i + 1 i = lib.str.ffw(cookies + i, cookies_p.ct - i) - cookies key.ptr = cookies + i val.ptr = nil else i = i + 1 end end end if val.ptr == nil then goto nocookie end val.ct = (cookies + i) - val.ptr if lib.str.ncmp(key.ptr, 'auth', key.ct) ~= 0 then goto nocookie end ::foundcookie:: do var aid = lib.session.cookie_interpret(server.cfg.secret, [lib.mem.ptr(int8)]{ptr=val.ptr,ct=val.ct}, lib.osclock.time(nil)) if aid ~= 0 then co.aid = aid end end ::nocookie::; end if co.aid ~= 0 then var sess, usr = co.srv:actor_session_fetch(co.aid, peer) if sess.ok == false then co.aid = 0 else co.who = usr.ptr end end var uridec = lib.mem.heapa(int8, msg.uri.len) defer uridec:free() var urideclen = lib.net.mg_url_decode(msg.uri.ptr, msg.uri.len, uridec.ptr, uridec.ct, 1) var uri = uridec if urideclen == -1 then for i = 0,msg.uri.len do if msg.uri.ptr[i] == @'+' then uri.ptr[i] = @' ' else uri.ptr[i] = msg.uri.ptr[i] end end uri.ct = msg.uri.len else uri.ct = urideclen end lib.dbg('routing URI ', {uri.ptr, uri.ct}) if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then route.dispatch_http(&co, uri, [lib.http.method.get]) else co:complain(400,'unknown method','you have submitted an invalid http request') end if co.aid ~= 0 then lib.mem.heapf(co.who) end end end end; } local terra cfg(s: &srv, befile: rawstring) lib.report('configuring backends from ', befile) 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 f:close() var cur = text.buf var segs: tuple(&int8, &int8)[3] = array( {[&int8](0),[&int8](0)}, {[&int8](0),[&int8](0)}, {[&int8](0),[&int8](0)} ) var segdup = [terra(s: {rawstring, rawstring}) var sz = s._1 - s._0 var str = s._0 return [lib.mem.ptr(int8)] { ptr = lib.str.ndup(str, sz); ct = sz; } end] var fld = 0 while (cur - text.buf) < text.sz do if segs[fld]._0 == nil then if not (@cur == @' ' or @cur == @'\t' or @cur == @'\n') then segs[fld] = {cur, nil} end else if fld < 2 and @cur == @' ' or @cur == @'\t' then segs[fld]._1 = cur fld = fld + 1 segs[fld] = {nil, nil} elseif @cur == @'\n' or cur == text.buf + (text.sz-1) then if fld < 2 then lib.bail('incomplete backend line in ', befile) else segs[fld]._1 = cur var src = c:new() src.id = segdup(segs[0]) src.string = segdup(segs[2]) src.backend = nil for i = 0,[lib.store.backends.type.N] do if lib.str.ncmp(segs[1]._0, lib.store.backends[i].id, segs[1]._1 - segs[1]._0) == 0 then src.backend = &lib.store.backends[i] break end end if src.backend == nil then lib.bail('unknown backend in ', befile) end src.handle = nil fld = 0 segs[0] = {nil, nil} end end end cur = cur + 1 end text:free() if c.sz > 0 then s.sources = c:crush() else s.sources.ptr = nil s.sources.ct = 0 end 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 terra cfgcache.methods.load :: {&cfgcache} -> {} terra cfgcache:init(o: &srv) self.overlord = o self:load() end srv.methods.start = terra(self: &srv, befile: rawstring) cfg(self, befile) var success = false if self.sources.ct == 0 then lib.bail('no data sources specified') end for i=0,self.sources.ct do var src = self.sources.ptr + i lib.report('opening data source ', src.id.ptr, '(', src.backend.id, ')') src.handle = src.backend.open(src) if src.handle ~= nil then success = true end end if not success then lib.bail('could not connect to any data sources!') end self.cfg:init(self) var dbbind = self:conf_get('bind') var envbind = lib.proc.getenv('parsav_bind') var bind: rawstring if envbind ~= nil then bind = envbind 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, self) var buf: int8[lib.session.maxlen] var len = lib.session.cookie_gen(self.cfg.secret, 9139084444658983115ULL, lib.osclock.time(nil), &buf[0]) buf[len] = 0 var authid = lib.session.cookie_interpret(self.cfg.secret, [lib.mem.ptr(int8)] {ptr=buf, ct=len}, lib.osclock.time(nil)) lib.io.fmt('generated cookie %s -- got authid %llu\n', buf, authid) if dbbind.ptr ~= nil then dbbind:free() end end srv.methods.poll = terra(self: &srv) lib.net.mg_mgr_poll(&self.webmgr,1000) end ................................................................................ for i=0,self.sources.ct do var src = self.sources.ptr + i lib.report('closing data source ', src.id.ptr, '(', src.backend.id, ')') src:close() end self.sources:free() end terra cfgcache:load() self.instance = self.overlord:conf_get('instance-name') self.secret = self.overlord:conf_get('server-secret') end return { overlord = srv; convo = convo; route = route; } |
Added static/style.scss version [a7ffc6f8bf].
Modified store.t from [2c2e954f5c] to [213b3d2729].
10 11 12 13 14 15 16 17 18 19 20 21 22 23 .. 28 29 30 31 32 33 34 35 36 37 38 39 40 41 ... 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 ... 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
};
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 {
................................................................................
-- user powers -- default on
login: bool
visible: bool
post: bool
shout: bool
propagate: bool
upload: bool
-- admin powers -- default off
ban: bool
config: bool
censor: bool
suspend: bool
rebrand: bool -- modify site's brand identity
................................................................................
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
................................................................................
-- 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)
|
> > > > > | < | > > > > > |
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 .. 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 ... 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 ... 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
};
relation = lib.enum {
'follow', 'mute', 'block'
};
credset = lib.set {
'pw', 'otp', 'challenge', 'trust'
};
privset = lib.set {
'post', 'edit', 'acct', 'upload', 'censor', 'admin'
}
}
local str = rawstring --lib.mem.ptr(int8)
struct m.source
struct m.rights {
................................................................................
-- user powers -- default on
login: bool
visible: bool
post: bool
shout: bool
propagate: bool
upload: bool
acct: bool
edit: bool
-- admin powers -- default off
ban: bool
config: bool
censor: bool
suspend: bool
rebrand: bool -- modify site's brand identity
................................................................................
end
struct m.auth {
aid: uint64
uid: uint64
aname: str
netmask: m.inet
privs: m.privset
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, lib.mem.ptr(int8)} -> 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
................................................................................
-- 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_session_fetch: {&m.source, uint64, m.inet} -> {lib.stat(m.auth), lib.mem.ptr(m.actor)}
-- retrieves an auth record + actor combo suitable by AID suitable
-- for determining session validity & caps
-- aid: uint64
-- origin: inet
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)
|
Modified str.t from [4b8724b0aa] to [c91733fef5].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 .. 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 .. 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 ... 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
................................................................................
}
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')
................................................................................
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 = {}
................................................................................
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
|
> > | | | | | > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 .. 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 .. 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 ... 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 |
-- vim: ft=terra -- string.t: string classes local util = dofile('common.lua') 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)); span = terralib.externfunction('strspn',{rawstring, rawstring} -> rawstring); } (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 ................................................................................ } 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') ................................................................................ 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.lit = macro(function(str) return `[lib.mem.ref(int8)] {ptr = [str:asvalue()], ct = [#(str:asvalue())]} 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 = {} ................................................................................ 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 local isnull, nullify if ty.ptr_basetype then kp = quote [box].obj.[k] = [ty] { ptr = [&ty.ptr_basetype]([ptr]) } ; end nullify = quote [box].obj.[k] = [ty] { ptr = nil, ct = 0 } end else kp = quote [box].obj.[k] = [ty]([ptr]) ; end nullify = quote [box].obj.[k] = nil end end local cpy if type(v) ~= 'table' or #v ~= 2 then cpy = quote [kp] ; [ptr] = m.cpy(ptr, v) end isnull = `v == nil end if type(v) == 'string' then memreq_const = memreq_const + #v + 1 isnull = `false 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 isnull = `[v].ptr == nil elseif type(v) == 'table' and v.asvalue and type(v:asvalue()) == 'string' then local str = tostring(v:asvalue()) memreq_const = memreq_const + #str + 1 isnull = `false 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 isnull = `[str] == nil else memreq_exp = `(m.sz(v) + 1) + [memreq_exp] -- make room for NUL isnull = `v == nil if ty.ptr_basetype then cpy = quote [cpy]; [box].obj.[k].ct = m.sz(v) end end end copiers[#copiers + 1] = quote if [isnull] then [nullify] else [cpy] end end 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 terra m.cspan(str: lib.mem.ptr(int8), reject: lib.mem.ref(int8), maxlen: intptr) for i=0, lib.math.smallest(maxlen,str.ct) do if str.ptr[i] == 0 then return 0 end for j=0, reject.ct do if str.ptr[i] == reject.ptr[j] then return i end end end return maxlen end terra m.ffw(str: &int8, maxlen: intptr) while maxlen > 0 and @str ~= 0 and (@str == @' ' or @str == @'\t' or @str == @'\n') do str = str + 1 maxlen = maxlen - 1 end return str end terra m.ffw_unsafe(str: &int8) while @str ~= 0 and (@str == @' ' or @str == @'\t' or @str == @'\n') do str = str + 1 end return str end return m |
Modified view/docskel.tpl from [1d38dac966] to [004398018e].
1 2 3 4 5 6 7 8 9 10 11 |
<!doctype html> <html> <head> <title>@instance :: @title</title> <link rel="stylesheet" href="/style.css"> </head> <body> <h1>@title</h1> @body </body> </html> |
| | |
1 2 3 4 5 6 7 8 9 10 11 |
<!doctype html> <html> <head> <title>@instance :: @title</title> <link rel="stylesheet" href="/s/style.css"> </head> <body class="@class"> <h1>@title</h1> @body </body> </html> |
Modified view/load.lua from [f2a65d7b61] to [53cfafaa7c].
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
-- file that indexes the templates manually, and
-- copies them into a data structure we can then
-- create templates from when we return to terra
local path = ...
local sources = {
'docskel';
'tweet';
}
local ingest = function(filename)
local hnd = io.open(path..'/'..filename)
local txt = hnd:read('*a')
io.close(hnd)
txt = txt:gsub('([^\\])!%b[]', '%1')
|
> |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
-- file that indexes the templates manually, and
-- copies them into a data structure we can then
-- create templates from when we return to terra
local path = ...
local sources = {
'docskel';
'tweet';
'profile';
}
local ingest = function(filename)
local hnd = io.open(path..'/'..filename)
local txt = hnd:read('*a')
io.close(hnd)
txt = txt:gsub('([^\\])!%b[]', '%1')
|
Added view/profile.tpl version [abc7153f4d].
> > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<div class="profile"> <div class="banner"> <img class="avatar" src="@avatar"> <div class="id">@nym [@xid]</div> <div class="bio"> @bio </div> </div> <table class="stats"> <tr><th>posts</th> <td>@nposts</td></tr> <tr><th>following</th> <td>@nfollows</td></tr> <tr><th>followers</th> <td>@nfollowers</td></tr> <tr><th>mutuals</th> <td>@nmutuals</td></tr> <tr><th>account created</th> <td>@tweetday</td></tr> </table> <div class="menu"> <a href="/\@@xid">posts</a> <a href="/\@@xid/media">media</a> <a href="/\@@xid/follows">follows</a> <a href="/\@@xid/chat">chat</a> </div> </div> |
Modified view/tweet.tpl from [59ae4e4ad9] to [4ad2e9eaa9].
1 2 3 4 5 6 7 8 9 10 11 12 |
<div class="post">
<div class="detail">
<a class="username" href="@link">
<span class="nym">@nym</span> <span class="handle">[@handle]</span>
</div>
<div class="when">@when</div>
</div>
<div class="content">
<img class="avatar" src="@avatar">
<div class="text">@text</div>
</div>
</div>
|
| |
1 2 3 4 5 6 7 8 9 10 11 12 |
<div class="post">
<div class="detail">
<a class="username" href="@link">
<span class="nym">@nym</span> <span class="handle">[@xid]</span>
</div>
<div class="when">@when</div>
</div>
<div class="content">
<img class="avatar" src="@avatar">
<div class="text">@text</div>
</div>
</div>
|