| Comment: | big ol iteration |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
5b3a03ad34669be0b5e5ecd853db0a2c |
| User & Date: | lexi on 2020-12-25 03:59:32 |
| Other Links: | manifest | tags |
|
2020-12-25
| ||
| 23:37 | iteration and important api adjustments check-in: f9559a83fc user: lexi tags: trunk | |
| 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 | |
Modified backend/pgsql.t from [17bd63f8c3] to [0ea1a47601].
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 .. 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 .. 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 ... 171 172 173 174 175 176 177 178 179 180 181 182 183 184 ... 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 ... 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 ... 439 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 ... 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 |
key = $1::text
]];
};
actor_fetch_uid = {
params = {uint64}, sql = [[
select
id, nym, handle, origin,
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
................................................................................
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,
handle ||'@'||
(select value from parsav_config
where key='domain' limit 1) as xid
from parsav_actors where origin is null
]];
};
actor_enum = {
params = {}, sql = [[
select a.id, a.nym, a.handle, a.origin,
a.bio, a.rank, a.quota, a.key,
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
................................................................................
(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,
................................................................................
for j=0,sz do i.v6[j] = v[4 + j] end -- 😬
return i
end
pqr.methods.int = macro(function(self, ty, row, col)
return quote
var i: ty:astype()
var v = lib.pq.PQgetvalue(self.res, row, col)
lib.math.netswap_ip(ty, v, &i)
in i end
end)
local pqt = {
[lib.store.inet] = function(cidr)
local tycode = cidr and 0x01 or 0x00
................................................................................
end
lib.bail('could not prepare PGSQL statement ',k,': ',lib.pq.PQresultErrorMessage(res))
end
lib.dbg('prepared PGSQL statement ',k)
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
end
end
terra q.exec(src: &lib.store.source, [args])
var params = arrayof([&int8], [casts])
var params_sz = arrayof(int, [counters])
var params_ft = arrayof(int, [ft])
[fixers]
var res = lib.pq.PQexecPrepared([&lib.pq.PGconn](src.handle), stmt,
[#args], params, params_sz, params_ft, 1)
if res == nil then
lib.bail(['grievous error occurred executing '..k..' against database'])
elseif lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
lib.bail(['PGSQL database procedure '..k..' failed\n'],
lib.pq.PQresultErrorMessage(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);
if r:null(row,7) then
a.ptr.key.ct = 0 a.ptr.key.ptr = nil
else
a.ptr.key = r:bin(row,7)
end
if r:null(row,3) then a.ptr.origin = 0
else a.ptr.origin = r:int(uint64,row,3) end
return a
end
local checksha = function(hnd, query, hash, origin, username, pw)
local inet_buf = symbol(uint8[4 + 16])
local validate = function(kind, cred, credlen)
return quote
var osz: intptr if origin.pv == 4 then osz = 4 else osz = 16 end
var formats = arrayof([int], 1,1,1,1)
var params = arrayof([&int8], username, kind,
[&int8](&cred), [&int8](&inet_buf))
var lens = arrayof(int, lib.str.sz(username), [#kind], credlen, osz + 4)
var res = lib.pq.PQexecParams([&lib.pq.PGconn](hnd), query, 4, nil,
params, lens, formats, 1)
if res == nil then
lib.bail('grievous failure checking pwhash')
elseif lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
lib.warn('pwhash query failed: ', lib.pq.PQresultErrorMessage(res), '\n', query)
else
var r = pqr {
sz = lib.pq.PQntuples(res);
res = res;
}
if r.sz > 0 then -- found a record! stop here
var aid = r:int(uint64, 0,0)
r:free()
return aid
end
end
end
end
local out = symbol(uint8[64])
local vdrs = {}
local alg = lib.md['MBEDTLS_MD_SHA' .. tostring(hash)]
vdrs[#vdrs+1] = quote
if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(alg),
[&uint8](pw), lib.str.sz(pw), out) ~= 0 then
lib.bail('hashing failure!')
end
[ validate(string.format('pw-sha%u', hash), out, hash / 8) ]
end
return quote
lib.dbg(['searching for hashed password credentials in format SHA' .. tostring(hash)])
var [inet_buf]
[pqt[lib.store.inet](false)](origin, inet_buf)
var [out]
[vdrs]
lib.dbg(['could not find password hash'])
end
end
local b = `lib.store.backend {
................................................................................
end
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)
................................................................................
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
|
| | > > | | > > > > > > > > > > > > > > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > | > | > | | > | | | < > | | | | | < < < < < < < < < < < | | | | < | | < < | | | | | | < < < < < < < < | | | | > > > > > > > > > > > | | | | | | | |
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 .. 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 ... 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 ... 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 ... 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 ... 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 ... 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 519 520 521 522 523 524 525 526 527 528 529 ... 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 |
key = $1::text
]];
};
actor_fetch_uid = {
params = {uint64}, sql = [[
select
id, nym, handle, origin, bio,
avataruri, rank, quota, key,
extract(epoch from knownsince)::bigint
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.avataruri, a.rank, a.quota, a.key,
extract(epoch from knownsince)::bigint,
coalesce(a.handle || '@' || s.domain,
'@' || a.handle) as xid,
coalesce(s.domain,
(select value from parsav_config
where key='domain' limit 1)) as domain
................................................................................
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_auth_pw = {
params = {lib.mem.ptr(int8),rawstring,lib.mem.ptr(int8),lib.store.inet}, sql = [[
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
]];
};
actor_enum_local = {
params = {}, sql = [[
select id, nym, handle, origin, bio,
null::text, rank, quota, key,
extract(epoch from knownsince)::bigint,
handle ||'@'||
(select value from parsav_config
where key='domain' limit 1) as xid
from parsav_actors where origin is null
]];
};
actor_enum = {
params = {}, sql = [[
select a.id, a.nym, a.handle, a.origin, a.bio,
a.avataruri, a.rank, a.quota, a.key,
extract(epoch from knownsince)::bigint,
coalesce(a.handle || '@' || s.domain,
'@' || a.handle) as xid
from parsav_actors a
left join parsav_servers s on s.id = a.origin
]];
};
actor_stats = {
params = {uint64}, sql = ([[
with tweets as (
select from parsav_posts where author = $1::bigint
),
follows as (
select relatee as user from parsav_rels
where relator = $1::bigint and kind = <follow>
),
followers as (
select relator as user from parsav_rels
where relatee = $1::bigint and kind = <follow>
),
mutuals as (select * from follows intersect select * from followers)
select count(tweets.*)::bigint,
count(follows.*)::bigint,
count(followers.*)::bigint,
count(mutuals.*)::bigint
from tweets, follows, followers, mutuals
]]):gsub('<(%w+)>',function(r) return tostring(lib.store.relation[r]) end)
};
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
................................................................................
(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.avataruri, a.rank, a.quota, a.key,
extract(epoch from knownsince)::bigint,
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,
................................................................................
for j=0,sz do i.v6[j] = v[4 + j] end -- 😬
return i
end
pqr.methods.int = macro(function(self, ty, row, col)
return quote
var i: ty:astype()
var v = lib.pq.PQgetvalue(self.res, row, col)
--i = @[&uint64](v)
lib.math.netswap_ip(ty, v, &i)
in i end
end)
local pqt = {
[lib.store.inet] = function(cidr)
local tycode = cidr and 0x01 or 0x00
................................................................................
end
lib.bail('could not prepare PGSQL statement ',k,': ',lib.pq.PQresultErrorMessage(res))
end
lib.dbg('prepared PGSQL statement ',k)
end
local args, casts, counters, fixers, ft, yield = {}, {}, {}, {}, {}, {}
local dumpers = {}
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]])
dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got rawstr %s\n'], [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
dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got inet\n'])
elseif ty.ptr_basetype == int8 or ty.ptr_basetype == uint8 then
counters[i] = `[args[i]].ct
casts[i] = `[&int8]([args[i]].ptr)
dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got ptr %llu %.*s\n'], [args[i]].ct, [args[i]].ct, [args[i]].ptr)
elseif ty:isintegral() then
counters[i] = ty.bytes
casts[i] = `[&int8](&[args[i]])
dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got int %llu\n'], [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
end
end
terra q.exec(src: &lib.store.source, [args])
var params = arrayof([&int8], [casts])
var params_sz = arrayof(int, [counters])
var params_ft = arrayof(int, [ft])
[fixers]
--[dumpers]
var res = lib.pq.PQexecPrepared([&lib.pq.PGconn](src.handle), stmt,
[#args], params, params_sz, params_ft, 1)
if res == nil then
lib.bail(['grievous error occurred executing '..k..' against database'])
elseif lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then
lib.bail(['PGSQL database procedure '..k..' failed\n'],
lib.pq.PQresultErrorMessage(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() >= 9 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};
avatar = {`r:string(row,5), `r:len(row,5)+1};
handle = {`r:string(row, 2); `r:len(row,2) + 1};
xid = {`r:string(row, 10); `r:len(row,10) + 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};
avatar = {`r:string(row,5), `r:len(row,5)+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, 6);
a.ptr.rights.quota = r:int(uint32, row, 7);
a.ptr.knownsince = r:int(int64,row, 9);
if r:null(row,8) then
a.ptr.key.ct = 0 a.ptr.key.ptr = nil
else
a.ptr.key = r:bin(row,8)
end
if r:null(row,3) then a.ptr.origin = 0
else a.ptr.origin = r:int(uint64,row,3) end
return a
end
local checksha = function(src, hash, origin, username, pw)
local validate = function(kind, cred, credlen)
return quote
var r = queries.actor_auth_pw.exec(
[&lib.store.source](src),
username,
kind,
[lib.mem.ptr(int8)] {ptr=[&int8](cred), ct=credlen},
origin)
if r.sz > 0 then -- found a record! stop here
var aid = r:int(uint64, 0,0)
r:free()
return aid
end
end
end
local out = symbol(uint8[64])
local vdrs = {}
local alg = lib.md['MBEDTLS_MD_SHA' .. tostring(hash)]
vdrs[#vdrs+1] = quote
if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(alg),
[&uint8](pw.ptr), pw.ct, out) ~= 0 then
lib.bail('hashing failure!')
end
[ validate(string.format('pw-sha%u', hash), `&out[0], hash / 8) ]
end
return quote
lib.dbg(['searching for hashed password credentials in format SHA' .. tostring(hash)])
var [out]
[vdrs]
lib.dbg(['could not find password hash'])
end
end
local b = `lib.store.backend {
................................................................................
end
end];
actor_auth_how = [terra(
src: &lib.store.source,
ip: lib.store.inet,
username: rawstring
): {lib.store.credset, bool}
var cs: lib.store.credset cs:clear();
var r = queries.actor_auth_how.exec(src, username, ip)
if r.sz == 0 then return cs, false 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, true
end];
actor_auth_pw = [terra(
src: &lib.store.source,
ip: lib.store.inet,
username: lib.mem.ptr(int8),
cred: lib.mem.ptr(int8)
): uint64
[ checksha(`src, 256, ip, username, cred) ] -- most common
[ checksha(`src, 512, ip, username, cred) ] -- most secure
[ checksha(`src, 384, ip, username, cred) ] -- weird
[ checksha(`src, 224, ip, username, cred) ] -- weirdest
-- TODO: check pbkdf2-hmac
-- TODO: check OTP
return 0
end];
actor_stats = [terra(src: &lib.store.source, uid: uint64)
var r = queries.actor_stats.exec(src, uid)
if r.sz == 0 then lib.bail('error fetching actor stats!') end
var s: lib.store.actor_stats
s.posts = r:int(uint64, 0, 0)
s.follows = r:int(uint64, 0, 1)
s.followers = r:int(uint64, 0, 2)
s.mutuals = r:int(uint64, 0, 3)
return s
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)
................................................................................
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,12) then -- restricted?
au.val.privs:clear()
(au.val.privs.post << r:bool(0,13))
(au.val.privs.edit << r:bool(0,14))
(au.val.privs.acct << r:bool(0,15))
(au.val.privs.upload << r:bool(0,16))
(au.val.privs.censor << r:bool(0,17))
(au.val.privs.admin << r:bool(0,18))
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 config.lua from [7cb566b503] to [dc89401662].
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 |
dist = default('parsav_dist', coalesce(
os.getenv('NIX_PATH') and 'nixos',
os.getenv('NIX_STORE') and 'nixos',
''));
tgttrip = default('parsav_arch_triple'); -- target triple, used in xcomp
tgtcpu = default('parsav_arch_cpu'); -- target cpu, used in xcomp
tgthf = u.tobool(default('parsav_arch_armhf',true));
endian = default('parsav_arch_endian', 'little');
build = {
id = u.rndstr(6);
release = u.ingest('release');
when = os.date();
};
feat = {};
backends = defaultlist('parsav_backends', 'pgsql');
braingeniousmode = false;
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']]
|
> > > > |
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 |
dist = default('parsav_dist', coalesce(
os.getenv('NIX_PATH') and 'nixos',
os.getenv('NIX_STORE') and 'nixos',
''));
tgttrip = default('parsav_arch_triple'); -- target triple, used in xcomp
tgtcpu = default('parsav_arch_cpu'); -- target cpu, used in xcomp
tgthf = u.tobool(default('parsav_arch_armhf',true));
outform = default('parsav_emit_type', 'o');
endian = default('parsav_arch_endian', 'little');
build = {
id = u.rndstr(6);
release = u.ingest('release');
when = os.date();
};
feat = {};
backends = defaultlist('parsav_backends', 'pgsql');
braingeniousmode = false;
embeds = {
{'style.css', 'text/css'};
{'default-avatar.webp', 'image/webp'};
{'padlock.webp', 'image/webp'};
{'warn.webp', 'image/webp'};
};
}
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']]
|
Modified http.t from [e5e590a634] to [654249752e].
1 2 3 4 5 6 7 8 9 10 11 12 |
-- 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
}
|
> > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-- vim: ft=terra
local m = {}
local util = dofile('common.lua')
m.method = lib.enum { 'get', 'post', 'head', 'options', 'put', 'delete' }
m.mime = lib.enum {
'html'; -- default
'json';
'mkdown';
'text';
'ansi';
'none';
}
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
}
|
Modified makefile from [2d2acfe121] to [3210eb684d].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
dl = git dbg-flags = $(if $(dbg),-g) parsav: parsav.t config.lua pkgdata.lua terra $(dbg-flags) $< parsav.o: parsav.t config.lua pkgdata.lua env parsav_link=no terra $(dbg-flags) $< clean: rm parsav parsav.o install: parsav mkdir $(prefix)/bin cp $< $(prefix)/bin/ ................................................................................ cd lib/json-c && cmake . lib/json-c/libjson-c.a: lib/json-c/Makefile $(MAKE) -C lib/json-c lib/mbedtls/library/%.a: lib/mbedtls $(MAKE) -C lib/mbedtls/library $*.a ifeq ($(dl), git) lib/mongoose: lib cd lib && git clone https://github.com/cesanta/mongoose.git lib/mbedtls: lib cd lib && git clone https://github.com/ARMmbed/mbedtls.git lib/json-c: lib cd lib && git clone https://github.com/json-c/json-c.git else lib/%: lib/%.tar.gz cd lib && tar zxf $*.tar.gz mv lib/$$(tar tf $< | head -n1) $@ ifeq ($(dl), wget) dlfile = wget "$(1)" -O "$(2)" |
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
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
..
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
dl = git dbg-flags = $(if $(dbg),-g) images = $(addsuffix .webp, $(basename $(wildcard static/*.svg))) styles = $(addsuffix .css, $(basename $(wildcard static/*.scss))) parsav: parsav.t config.lua pkgdata.lua $(images) $(styles) terra $(dbg-flags) $< parsav.o: parsav.t config.lua pkgdata.lua $(images) $(styles) env parsav_link=no terra $(dbg-flags) $< parsav.ll: parsav.t config.lua pkgdata.lua $(images) $(styles) env parsav_emit_type=ll parsav_link=no terra $(dbg-flags) $< parsav.s: parsav.ll llc --march=$(target) $< static/%.webp: static/%.png cwebp -q 90 $< -o $@ static/%.png: static/%.svg inkscape -f $< -C -d 180 -e $@ static/%.css: static/%.scss sassc -t compressed $< $@ clean: rm parsav parsav.o install: parsav mkdir $(prefix)/bin cp $< $(prefix)/bin/ ................................................................................ cd lib/json-c && cmake . lib/json-c/libjson-c.a: lib/json-c/Makefile $(MAKE) -C lib/json-c lib/mbedtls/library/%.a: lib/mbedtls $(MAKE) -C lib/mbedtls/library $*.a ifeq ($(dl), git) clone = git clone --depth 1 # save time lib/mongoose: lib cd lib && $(clone) https://github.com/cesanta/mongoose.git lib/mbedtls: lib cd lib && $(clone) https://github.com/ARMmbed/mbedtls.git lib/json-c: lib cd lib && $(clone) https://github.com/json-c/json-c.git else lib/%: lib/%.tar.gz cd lib && tar zxf $*.tar.gz mv lib/$$(tar tf $< | head -n1) $@ ifeq ($(dl), wget) dlfile = wget "$(1)" -O "$(2)" |
Modified math.t from [f661c3d77e] to [573a13128c].
145 146 147 148 149 150 151 152 153 |
buf = buf + 1 end end terra m.b32str(a: lib.mem.ptr(uint64)) end return m |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
buf = buf + 1 end end terra m.b32str(a: lib.mem.ptr(uint64)) end terra m.decstr(val: intptr, buf: &int8): rawstring -- works backwards to avoid copies. log10(2^64) ≈ 19.2 and we -- need a byte for NUL so buf MUST point to THE END OF a buffer -- at least 21 bytes long @buf = 0 if val > 0 then while val > 0 do buf = buf - 1 var dgt = val % 10 val = val / 10 @buf = 0x30 + dgt end else buf = buf - 1 @buf = 0x30 end return buf end terra m.decstr_friendly(val: intptr, buf: &int8): rawstring -- as above except needs size-28 buffers, on account of all the commas @buf = 0 var dgtct: uint8 = 0 if val > 0 then while val > 0 do buf = buf - 1 var dgt = val % 10 val = val / 10 @buf = 0x30 + dgt if dgtct == 2 and val > 0 then buf = buf - 1 @buf = @',' dgtct = 0 else dgtct = dgtct + 1 end end else buf = buf - 1 @buf = 0x30 end return buf end return m |
Modified parsav.md from [93a3706cc3] to [eb5d145ae6].
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
* mongoose
* json-c
* mbedtls
* **postgresql backend:**
* postgresql-libs
## building
first, either install any missing dependencies as shared libraries, or build them as static libraries with the command `make dep.$LIBRARY`. as a shortcut, `make dep` will build all dependencies as static libraries. note that if the build system finds a static version of a librari in the `lib/` folder, it will use that instead of any system library.
postgresql-libs must be installed systemwide, as `parsav` does not currently provide for statically compiling and linking it
## configuring
the `parsav` configuration is comprised of two components: the backends list and the config store. the backends list is a simple text file that tells `parsav` which data sources to draw from. the config store is a key-value store which contains the rest of the server's configuration, and is loaded from the backends. the configuration store can be spread across the backends; backends will be checked for configuration keys according to the order in which they are listed. changes to the configuration store affect parsav in real time; you only need to restart the server if you make a change to the backend list.
|
> > > > > > > > > > > > | |
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 |
* mongoose * json-c * mbedtls * **postgresql backend:** * postgresql-libs additional build-time dependencies are necessary if you are building directly from trunk, rather than from a release tarball that includes certain build artifacts which need to be embedded in the binary: * inkscape, for rendering out UI graphics * cwebp (libwebp package), for transforming inkscape PNGs to webp * sassc, for compiling the SCSS stylesheet into its final CSS all builds require terra, which, unfortunately, requires installing an older version of llvm, v9 at the latest (which i develop parsav under). with any luck, your distro will be clever enough to package terra and its dependencies properly (it's trivial on nix, tho you'll need to tweak the terra expression to select a more recent llvm package); Arch Linux is one of those distros which is not so clever, and whose (AUR) terra package is totally broken. due to these unfortunate circumstances, terra is distributed not just in source form, but also in the the form of LLVM IR. distributions will also be made in the form of tarballed object code and assembly listings for various common platforms, currently including x86-32/64, arm7hf, aarch64, riscv, mips32/64, and ppc64/64le. i've noticed that terra (at least with llvm9) seems to get a bit cantankerous and trigger llvm to fail with bizarre errors when you try to cross-compile parsav from x86-64 to any other platform, even x86-32. i don't know if this problem exists on other architectures or in what form, but as a workaround, the current cross-compile process consists of generating LLVM IR (ostensible for x86-64, though this is in reality an architecture-independent language), and then compiling that down to an object file with llc. this is an enormous hassle; hopefully the terra people will fix this eventually. also note that, while parsav has a flag to build with ASAN, ASAN has proven unusable for most purposes as it routinely reports false positive buffer-heap-overflows. if you figure out how to defuckulate this, i will be overjoyed. ## building first, either install any missing dependencies as shared libraries, or build them as static libraries with the command `make dep.$LIBRARY`. as a shortcut, `make dep` will build all dependencies as static libraries. note that if the build system finds a static version of a library in the `lib/` folder, it will use that instead of any system library. note that these commands require GNU make (it may be installed as `gmake` on your system), although this is a fairly soft dependency -- if you really need to build it on BSD make, you can probably translate it with a minute or so of work; you'll just have to do some of the various gmake functions' work manually. this may be worthwhile if you're packaging for a BSD. postgresql-libs must be installed systemwide, as `parsav` does not currently provide for statically compiling and linking it ## configuring the `parsav` configuration is comprised of two components: the backends list and the config store. the backends list is a simple text file that tells `parsav` which data sources to draw from. the config store is a key-value store which contains the rest of the server's configuration, and is loaded from the backends. the configuration store can be spread across the backends; backends will be checked for configuration keys according to the order in which they are listed. changes to the configuration store affect parsav in real time; you only need to restart the server if you make a change to the backend list. |
Modified parsav.t from [41c6f93980] to [11ad9b3025].
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 ... 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 ... 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 ... 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
end)
lib.enum = function(tbl)
local ty = uint8
if #tbl >= 2^32 then ty = uint64 -- hey, can't be too safe
elseif #tbl >= 2^16 then ty = uint32
elseif #tbl >= 2^8 then ty = uint16 end
local o = { t = ty }
for i, name in ipairs(tbl) do
o[name] = i
end
return o
end
lib.set = function(tbl)
local bytes = math.ceil(#tbl / 8)
local o = {}
for i, name in ipairs(tbl) do o[name] = i end
................................................................................
local struct 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)
local q = quote lib.io.say('dumping set:\n') end
for i,v in ipairs(tbl) do
q = quote
[q]
if [bool](self.[v])
then lib.io.say([' - ' .. v .. ': true\n'])
................................................................................
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
................................................................................
if bflag('dump-config','C') then
print(util.dump(config))
os.exit(0)
end
local holler = print
local out = config.exe and 'parsav' or 'parsav.o'
local linkargs = {}
if bflag('quiet','q') then holler = function() end end
if bflag('asan','s') then linkargs[#linkargs+1] = '-fsanitize=address' end
if bflag('lsan','S') then linkargs[#linkargs+1] = '-fsanitize=leak' end
if config.posix then
linkargs[#linkargs+1] = '-pthread'
|
> | > > > > > | > > > > > > > > > > | | |
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 ... 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 ... 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 ... 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 |
end)
lib.enum = function(tbl)
local ty = uint8
if #tbl >= 2^32 then ty = uint64 -- hey, can't be too safe
elseif #tbl >= 2^16 then ty = uint32
elseif #tbl >= 2^8 then ty = uint16 end
local o = { t = ty }
local strings = {}
for i, name in ipairs(tbl) do
o[name] = i - 1
strings[i] = `[lib.mem.ref(int8)]{ptr=[name], ct=[#name]}
end
o._str = terra(val: ty)
var l = array([strings])
return l[val]
end
return o
end
lib.set = function(tbl)
local bytes = math.ceil(#tbl / 8)
local o = {}
for i, name in ipairs(tbl) do o[name] = i end
................................................................................
local struct 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)
terra set:sz()
var ct: intptr = 0
for i = 0, [#tbl] do
if (self._store[i/8] and (1 << i % 8)) ~= 0 then ct = ct + 1 end
end
return ct
end
set.methods.dump = macro(function(self)
local q = quote lib.io.say('dumping set:\n') end
for i,v in ipairs(tbl) do
q = quote
[q]
if [bool](self.[v])
then lib.io.say([' - ' .. v .. ': true\n'])
................................................................................
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:nav';
'render:login';
'render:profile';
'render:userpage';
'render:compose';
'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
................................................................................
if bflag('dump-config','C') then
print(util.dump(config))
os.exit(0)
end
local holler = print
local out = config.exe and 'parsav' or ('parsav.' .. config.outform)
local linkargs = {'-O4'}
if bflag('quiet','q') then holler = function() end end
if bflag('asan','s') then linkargs[#linkargs+1] = '-fsanitize=address' end
if bflag('lsan','S') then linkargs[#linkargs+1] = '-fsanitize=leak' end
if config.posix then
linkargs[#linkargs+1] = '-pthread'
|
Added render/compose.t version [7a7e8f43ac].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local terra render_compose(co: &lib.srv.convo, edit: &lib.store.post) var target, tgtlen = co:getv('to') var form: data.view.compose if edit == nil then form = data.view.compose { content = lib.coalesce(target, ''); acl = lib.trn(target == nil, 'all', 'mentioned'); -- TODO default acl setting? handle = co.who.handle; } end var cotxt = form:tostr() defer cotxt:free() var doc = data.view.docskel { instance = co.srv.cfg.instance.ptr; title = 'compose'; body = cotxt.ptr; class = 'compose'; navlinks = co.navbar.ptr; } 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_compose |
Added render/login.t version [0d69ec17a3].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local terra login_form(co: &lib.srv.convo, user: &lib.store.actor, creds: &lib.store.credset, msg: &int8) var doc = data.view.docskel { instance = co.srv.cfg.instance.ptr; title = 'instance logon'; class = 'login'; navlinks = co.navbar.ptr; } if user == nil then var form = data.view.login_username { loginmsg = msg; } if form.loginmsg == nil then form.loginmsg = 'identify yourself for access to this instance.' end var formtxt = form:tostr() doc.body = formtxt.ptr elseif creds:sz() == 0 then co:complain(403,'access denied','your host is not eligible to authenticate as this user') return elseif creds:sz() == 1 then if creds.trust() then -- TODO log in immediately return end var ch = data.view.login_challenge { handle = user.handle; name = lib.coalesce(user.nym, user.handle); } if creds.pw() then ch.challenge = 'enter the password associated with your account' ch.label = 'password' ch.method = 'pw' elseif creds.otp() then ch.challenge = 'enter a valid one-time password for your account' ch.label = 'OTP code' ch.method = 'otp' elseif creds.challenge() then ch.challenge = 'sign the challenge token: <code>...</code>' ch.label = 'digest' ch.method = 'challenge' else co:complain(500,'login failure','unknown login method') return end doc.body = ch:tostr().ptr else -- pick a method end 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]}) lib.mem.heapf(doc.body) end return login_form |
Added render/nav.t version [2d4aa38bec].
> > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
-- vim: ft=terra local terra render_nav(co: &lib.srv.convo) var t: lib.str.acc t:init(64) if co.who ~= nil or co.srv.cfg.pol_sec == lib.srv.secmode.public then t:lpush('<a href="/">timeline</a>') end if co.who ~= nil then t:lpush('<a href="/compose">compose</a> <a href="/'):push(co.who.xid,0) t:lpush('">profile</a> <a href="/conf">configure</a> <a href="/logout">log out</a>') else t:lpush('<a href="/login">log in</a>') end return t:finalize() end return render_nav |
Modified render/profile.t from [a405db9158] to [ecfc7ba460].
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
|
| > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > | < > | > | > | > | > > > |
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 |
-- vim: ft=terra local terra render_profile(co: &lib.srv.convo, actor: &lib.store.actor) var aux: lib.str.acc var auxp: rawstring if co.aid ~= 0 and co.who.id == actor.id then auxp = '<a href="/conf/profile">alter</a>' elseif co.aid ~= 0 then aux:compose('<a href="/', actor.xid, '/follow">follow</a><a href="/', actor.xid, '/chat">chat</a>') if co.who.rights.powers:affect_users() then aux:push('<a href="/',11):push(actor.xid,0):push('/ctl">control</a>',17) end auxp = aux.buf else aux:compose('<a href="/', actor.xid, '/follow">remote follow</a>') end var avistr: lib.str.acc if actor.origin == 0 then avistr:compose('/avi/',actor.handle) end var timestr: int8[26] lib.osclock.ctime_r(&actor.knownsince, ×tr[0]) var strfbuf: int8[28*4] var stats = co.srv:actor_stats(actor.id) var sn_posts = lib.math.decstr_friendly(stats.posts, &strfbuf[ [strfbuf.type.N - 1] ]) var sn_follows = lib.math.decstr_friendly(stats.follows, sn_posts - 1) var sn_followers = lib.math.decstr_friendly(stats.followers, sn_follows - 1) var sn_mutuals = lib.math.decstr_friendly(stats.mutuals, sn_followers - 1) var profile = data.view.profile { nym = lib.coalesce(actor.nym, actor.handle); bio = lib.coalesce(actor.bio, "<em>tall, dark, and mysterious</em>"); xid = actor.xid; avatar = lib.trn(actor.origin == 0, avistr.buf, lib.coalesce(actor.avatar, '/s/default-avatar.webp')); nposts = sn_posts, nfollows = sn_follows; nfollowers = sn_followers, nmutuals = sn_mutuals; tweetday = timestr; timephrase = lib.trn(actor.origin == 0, 'joined', 'known since'); auxbtn = auxp; } var ret = profile:tostr() if actor.origin == 0 then avistr:free() end if not (co.aid ~= 0 and co.who.id == actor.id) then aux:free() end return ret end return render_profile |
Modified render/userpage.t from [052285d84c] to [cdf1e65d22].
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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
|
| > |
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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(co,actor) defer pftxt:free()
var doc = data.view.docskel {
instance = co.srv.cfg.instance.ptr;
title = ti.buf;
body = pftxt.ptr;
class = 'profile';
navlinks = co.navbar.ptr;
}
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
|
Modified route.t from [70d0da6b8e] to [d7d680b0a3].
53
54
55
56
57
58
59
60
61
62
63
64
65
66
..
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
|
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] ................................................................................ } [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 |
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
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
|
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 terra http.login_form(co: &lib.srv.convo, meth: method.t) if meth == method.get then -- request a username lib.render.login(co, nil, nil, nil) elseif meth == method.post then var usn, usnl = co:postv('user') lib.dbg('got name ',{usn,usnl}) lib.io.fmt('name len %llu\n',usnl) var am, aml = co:postv('authmethod') var chrs, chrsl = co:postv('response') var cs, authok = co.srv:actor_auth_how(co.peer, usn) var act = co.srv:actor_fetch_xid([lib.mem.ptr(int8)] { ptr = usn, ct = usnl }) if authok == false then lib.render.login(co, nil, nil, 'access denied') return end var fakeact = false var fakeactor: lib.store.actor if act.ptr == nil then -- the user is known to us but has not yet claimed an -- account on the server. create a template for the -- account that will be created once they log in fakeact = true fakeactor = lib.store.actor { id = 0, handle = usn, nym = usn; origin = 0, bio = nil; key = [lib.mem.ptr(uint8)] {ptr=nil, ct=0} } act.ct = 1 act.ptr = &fakeactor act.ptr.rights = lib.store.rights_default() end if am == nil then -- pick an auth method lib.render.login(co, act.ptr, &cs, nil) else var aid: uint64 = 0 lib.dbg('authentication attempt beginning') -- attempt login with provided method if lib.str.ncmp('pw', am, lib.math.biggest(2,aml)) == 0 and chrs ~= nil then aid = co.srv:actor_auth_pw(co.peer, [lib.mem.ptr(int8)]{ptr=usn,ct=usnl}, [lib.mem.ptr(int8)]{ptr=chrs,ct=chrsl}) elseif lib.str.ncmp('otp', am, lib.math.biggest(2,aml)) == 0 and chrs ~= nil then lib.dbg('using otp auth') -- ··· -- else lib.dbg('invalid auth method') end lib.io.fmt('login got aid = %llu\n', aid) -- error out if aid == 0 then lib.render.login(co, nil, nil, 'authentication failure') else var sesskey: int8[lib.session.maxlen + #lib.session.cookiename + #"=; Path=/" + 1] do var p = &sesskey[0] p = lib.str.ncpy(p, [lib.session.cookiename .. '='], [#lib.session.cookiename + 1]) p = p + lib.session.cookie_gen(co.srv.cfg.secret, aid, lib.osclock.time(nil), p) lib.dbg('sending cookie',&sesskey[0]) p = lib.str.ncpy(p, '; Path=/', 9) end co:reroute_cookie('/', &sesskey[0]) end end if act.ptr ~= nil and fakeact == false then act:free() end else ::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end end return end terra http.post_compose(co: &lib.srv.convo, meth: method.t) if meth == method.get then lib.render.compose(co, nil) elseif meth == method.post then if co.who.rights.powers.post() == false then co:complain(401,'insufficient privileges','you lack the <strong>post</strong> power and cannot perform this action') return end end 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] ................................................................................ } [branches] do return false end ::[send]:: page:send(co.con) return true end end terra http.local_avatar(co: &lib.srv.convo, handle: lib.mem.ptr(int8)) -- TODO retrieve user avatars co:reroute('/s/default-avatar.webp') end -- entry points terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t) lib.dbg('handling URI of form ', {uri.ptr,uri.ct}) co.navbar = lib.render.nav(co) -- some routes are non-hierarchical, and can be resolved with a simple strcmp -- we run through those first before giving up and parsing the URI if uri.ptr[0] ~= @'/' then co:complain(404, 'what the hell', 'how did you do that') return elseif uri.ct == 1 then -- root lib.io.fmt('root directory, aid is %llu\n', co.aid) if (co.srv.cfg.pol_sec == lib.srv.secmode.private or co.srv.cfg.pol_sec == lib.srv.secmode.lockdown) and co.aid == 0 then http.login_form(co, meth) else -- FIXME display home screen goto notfound end return elseif uri.ptr[1] == @'@' then http.actor_profile_xid(co, uri, meth) return 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 return elseif lib.str.ncmp('/avi/', uri.ptr, 5) == 0 then http.local_avatar(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 5, ct = uri.ct - 5}) return elseif lib.str.ncmp('/compose', uri.ptr, lib.math.biggest(uri.ct,8)) == 0 then if co.aid == 0 then co:reroute('/login') return end http.post_compose(co,meth) return elseif lib.str.ncmp('/login', uri.ptr, lib.math.biggest(uri.ct,6)) == 0 then if co.aid == 0 then http.login_form(co, meth) else co:reroute('/') end return elseif lib.str.ncmp('/logout', uri.ptr, lib.math.biggest(uri.ct,7)) == 0 then if co.aid == 0 then goto notfound else co:reroute_cookie('/','auth=; Path=/') end return else -- hierarchical routes 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 return end ::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end ::notfound:: co:complain(404, 'not found', 'no such resource available') do return end end |
Modified schema.sql from [636689e0dd] to [a3359b8b76].
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
..
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
|
\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 drop table if exists parsav_servers cascade; create table parsav_servers ( id bigint primary key default (1+random()*(2^63-1))::bigint, domain text not null, key bytea ); drop table if exists parsav_actors cascade; create table parsav_actors ( id bigint primary key default (1+random()*(2^63-1))::bigint, nym text, handle text not null, -- nym [@handle@origin] origin bigint references parsav_servers(id) on delete cascade, -- null origin = local actor bio text, rank smallint not null default 0, quota integer not null default 1000, key bytea, -- private if localactor; public if remote unique (handle,origin) ); drop table if exists parsav_rights cascade; create table parsav_rights ( key text, ................................................................................ ('censor',true), ('suspend',true), ('rebrand',true) ) as a; drop table if exists parsav_posts cascade; create table parsav_posts ( id bigint primary key default (1+random()*(2^63-1))::bigint, author bigint references parsav_actors(id) on delete cascade, subject text, body text, posted timestamp not null, discovered timestamp not null, scope smallint not null, convo bigint, parent bigint, circles bigint[], mentions bigint[] ); drop table if exists parsav_conversations cascade; create table parsav_conversations ( id bigint primary key default (1+random()*(2^63-1))::bigint, uri text not null, discovered timestamp not null, head bigint references parsav_posts(id) ); drop table if exists parsav_rels cascade; create table parsav_rels ( relator bigint references parsav_actors(id) on delete cascade, -- e.g. follower relatee bigint references parsav_actors(id) on delete cascade, -- e.g. follower kind smallint, -- e.g. follow, block, mute primary key (relator, relatee, kind) ); drop table if exists parsav_acts cascade; create table parsav_acts ( id bigint primary key default (1+random()*(2^63-1))::bigint, kind text not null, -- like, react, so on time timestamp not null, actor bigint references parsav_actors(id) on delete cascade, subject bigint -- may be post or act, depending on kind ); drop table if exists parsav_log cascade; create table parsav_log ( -- accesses are tracked for security & sending delete acts id bigint primary key default (1+random()*(2^63-1))::bigint, time timestamp not null, actor bigint references parsav_actors(id) on delete cascade, post bigint not null ); end; |
>
>
>
>
|
|
|
>
>
|
|
|
>
>
|
<
>
|
|
<
>
>
|
|
|
>
|
|
|
>
|
|
|
<
|
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
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
..
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
|
\prompt 'domain name: ' domain \prompt 'instance name: ' inst \prompt 'bind to socket: ' bind \qecho 'how locked down should this server be? public = anyone can see public timeline and tweets, private = anyone can see tweets with a link but login required for everything else, lockdown = login required for all activities, isolate = like lockdown but with federation protocols completely disabled' \prompt 'security mode: ' secmode \qecho 'should user self-registration be allowed? yes or no' \prompt 'registration: ' regpol \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 and identified as the server owner.' \prompt 'master 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'), ('policy-security',:'secmode'), ('policy-self-register',:'regpol'), ('master',:'admin'), ('server-secret', encode( digest(int8send((2^63 * (random()*2 - 1))::bigint), 'sha512'), 'base64')); -- note that valid ids should always > 0, as 0 is reserved for null -- on the client side, vastly simplifying code drop table if exists parsav_servers cascade; create table parsav_servers ( id bigint primary key default (1+random()*(2^63-1))::bigint, domain text not null, key bytea, parsav boolean -- whether to use parsav protocol extensions ); drop table if exists parsav_actors cascade; create table parsav_actors ( id bigint primary key default (1+random()*(2^63-1))::bigint, nym text, handle text not null, -- nym [@handle@origin] origin bigint references parsav_servers(id) on delete cascade, -- null origin = local actor bio text, avataruri text, -- null if local rank smallint not null default 0, quota integer not null default 1000, key bytea, -- private if localactor; public if remote title text unique (handle,origin) ); drop table if exists parsav_rights cascade; create table parsav_rights ( key text, ................................................................................ ('censor',true), ('suspend',true), ('rebrand',true) ) as a; drop table if exists parsav_posts cascade; create table parsav_posts ( id bigint primary key default (1+random()*(2^63-1))::bigint, author bigint references parsav_actors(id) on delete cascade, subject text, acl text not null default 'all', -- just store the script raw 🤷 body text, posted timestamp not null, discovered timestamp not null, scope smallint not null, convo bigint, parent bigint, circles bigint[], mentions bigint[] ); drop table if exists parsav_conversations cascade; create table parsav_conversations ( id bigint primary key default (1+random()*(2^63-1))::bigint, uri text not null, discovered timestamp not null, head bigint references parsav_posts(id) ); drop table if exists parsav_rels cascade; create table parsav_rels ( relator bigint references parsav_actors(id) on delete cascade, -- e.g. follower relatee bigint references parsav_actors(id) on delete cascade, -- e.g. followed kind smallint, -- e.g. follow, block, mute primary key (relator, relatee, kind) ); drop table if exists parsav_acts cascade; create table parsav_acts ( id bigint primary key default (1+random()*(2^63-1))::bigint, kind text not null, -- like, react, so on time timestamp not null default now(), actor bigint references parsav_actors(id) on delete cascade, subject bigint -- may be post or act, depending on kind ); drop table if exists parsav_log cascade; create table parsav_log ( -- accesses are tracked for security & sending delete acts id bigint primary key default (1+random()*(2^63-1))::bigint, time timestamp not null default now(), actor bigint references parsav_actors(id) on delete cascade, post bigint not null ); drop table if exists parsav_attach cascade; create table parsav_attach ( id bigint primary key default (1+random()*(2^63-1))::bigint, birth timestamp not null default now(), content bytea not null, mime text, -- null if unknown, will be reported as x-octet-stream description text, parent bigint -- post id, or userid for avatars ); drop table if exists parsav_circles cascade; create table parsav_circles ( id bigint primary key default (1+random()*(2^63-1))::bigint, owner bigint not null references parsav_actors(id), name text not null, members bigint[] not null default array[], unique (owner,name) ); drop table if exists parsav_rooms cascade; create table parsav_rooms ( id bigint primary key default (1+random()*(2^63-1))::bigint, origin bigint references parsav_servers(id), name text not null, description text not null, policy smallint not null ); drop table if exists parsav_room_members cascade; create table parsav_room_members ( room bigint references parsav_rooms(id), member bigint references parsav_actors(id), rank smallint not null default 0, admin boolean not null default false, -- non-admins with rank can only moderate + invite title text -- admin-granted title like reddit flair ); drop table if exists parsav_invites cascade; create table parsav_invites ( id bigint primary key default (1+random()*(2^63-1))::bigint, -- when a user is created from an invite, the invite is deleted and the invite -- ID becomes the user ID. privileges granted on the invite ID during the invite -- process are thus inherited by the user handle text, -- admin can lock invite to specific handle rank smallint not null default 0, quota integer not null default 1000 }; drop table if exists parsav_interventions cascade; create table parsav_interventions ( id bigint primary key default (1+random()*(2^63-1))::bigint, issuer bigint references parsav_actors(id) not null, scope bigint, -- can be null or room for local actions nature smallint not null, -- silence, suspend, disemvowel, etc victim bigint not null, -- could potentially target group as well expire timestamp -- auto-expires if set ); end; |
Modified session.t from [58f0eab21d] to [e8a79576f0].
3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
-- 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)
|
> |
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
-- 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
cookiename = 'auth';
}
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)
|
Modified srv.t from [ed3d5ec62e] to [afa0417e30].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .. 68 69 70 71 72 73 74 75 76 77 78 79 80 81 .. 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 ... 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 ... 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 ... 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 ... 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 ... 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 ... 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
}
................................................................................
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 {
................................................................................
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
................................................................................
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
................................................................................
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
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)
................................................................................
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
................................................................................
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
................................................................................
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;
}
|
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > | > | > | > | < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .. 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 .. 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 ... 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 ... 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 ... 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 ... 427 428 429 430 431 432 433 434 435 436 437 438 439 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 ... 490 491 492 493 494 495 496 497 498 499 500 501 502 503 ... 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
-- vim: ft=terra local util = dofile 'common.lua' local secmode = lib.enum { 'public', 'private', 'lockdown', 'isolate' } local struct srv local struct cfgcache { secret: lib.mem.ptr(int8) instance: lib.mem.ptr(int8) overlord: &srv pol_sec: secmode.t pol_reg: bool } local struct srv { sources: lib.mem.ptr(lib.store.source) webmgr: lib.net.mg_mgr webcon: &lib.net.mg_connection cfg: cfgcache } ................................................................................ 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 peer: lib.store.inet reqtype: lib.http.mime.t -- negotiated content type -- cache navbar: lib.mem.ptr(int8) -- private varbuf: lib.mem.ptr(int8) vbofs: &int8 } -- 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 { ................................................................................ peer: lib.net.mg_addr } terra getpeer(con: &lib.net.mg_connection) return [&strucheader](con).peer end end terra convo:reroute_cookie(dest: rawstring, cookie: rawstring) var hdrs = array( lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' }, lib.http.header { key = 'Location', value = dest }, lib.http.header { key = 'Set-Cookie', value = cookie } ) var body = data.view.docskel { instance = self.srv.cfg.instance.ptr; title = 'rerouting'; body = 'you are being redirected'; class = 'error'; navlinks = ''; } body:send(self.con, 303, [lib.mem.ptr(lib.http.header)] { ptr = &hdrs[0], ct = [hdrs.type.N] - lib.trn(cookie == nil,1,0) }) end terra convo:reroute(dest: rawstring) self:reroute_cookie(dest,nil) 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 bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.webp"><h1>error</h1><p>',msg,'</p></div>') defer bo:free() var body = data.view.docskel { instance = self.srv.cfg.instance.ptr; title = ti.buf; body = bo.buf; class = 'error'; navlinks = lib.coalesce(self.navbar.ptr, ''); } 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 -- CALL ONLY ONCE PER VAR terra convo:postv(name: rawstring) if self.varbuf.ptr == nil then self.varbuf = lib.mem.heapa(int8, self.msg.body.len + self.msg.query.len) self.vbofs = self.varbuf.ptr end var o = lib.net.mg_http_get_var(&self.msg.body, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr)) if o > 0 then var r = self.vbofs self.vbofs = self.vbofs + o return r, o else return nil, 0 end end terra convo:getv(name: rawstring) if self.varbuf.ptr == nil then self.varbuf = lib.mem.heapa(int8, self.msg.query.len + self.msg.body.len) self.vbofs = self.varbuf.ptr end var o = lib.net.mg_http_get_var(&self.msg.query, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr)) if o > 0 then var r = self.vbofs self.vbofs = self.vbofs + o return r, o else return nil, 0 end 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 mimetypes = { {'html', 'text/html'}; {'json', 'application/json'}; {'mkdown', 'text/markdown'}; {'text', 'text/plain'}; {'ansi', 'text/x-ansi'}; } local mimevar = symbol(lib.mem.ref(int8)) local mimeneg = `lib.http.mime.none for i, t in ipairs(mimetypes) do local name, mime = t[1], t[2] mimeneg = quote var ret: lib.http.mime.t if lib.str.ncmp(mimevar.ptr, mime, lib.math.biggest(mimevar.ct, [#mime])) == 0 then ret = [lib.http.mime[name]] else ret = [mimeneg] end in ret end end 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 ................................................................................ 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 -- needs to check for an X-Forwarded-For header from nginx and -- use that instead of the peer iff peer is ::1/127.1 FIXME -- maybe also haproxy support? 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, peer = peer; reqtype = lib.http.mime.none; } co.varbuf.ptr = nil co.navbar.ptr = nil -- first, check for an accept header. if it's there, we need to -- iterate over the values and pick the highest-priority one do var acc = lib.http.findheader(msg, 'Accept') -- TODO handle q-value if acc.ptr ~= nil then var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr } var i = 0 while i < acc.ct do if acc.ptr[i] == @',' or acc.ptr[i] == @';' then mimevar.ct = (acc.ptr+i) - mimevar.ptr var t = [mimeneg] if t ~= lib.http.mime.none then co.reqtype = t goto foundtype end if acc.ptr[i] == @';' then -- fast-forward over q for j=i+1,acc.ct do i=j if acc.ptr[j] == @',' then break end end end while i < acc.ct and -- fast-forward over ws acc.ptr[i+1] == @' ' or acc.ptr[i+1] == @'\t' do i=i+1 end mimevar.ptr = acc.ptr + i + 1 end i=i+1 end if co.reqtype == lib.http.mime.none then mimevar.ct = acc.ct - (mimevar.ptr - acc.ptr) co.reqtype = [mimeneg] if co.reqtype == lib.http.mime.none then co.reqtype = lib.http.mime.html end end else co.reqtype = lib.http.mime.html end ::foundtype::end -- 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 ................................................................................ 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, lib.session.cookiename, lib.math.biggest([#lib.session.cookiename],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, lib.session.cookiename, lib.math.biggest([#lib.session.cookiename], 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 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]) elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then route.dispatch_http(&co, uri, [lib.http.method.post]) elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then route.dispatch_http(&co, uri, [lib.http.method.head]) elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then route.dispatch_http(&co, uri, [lib.http.method.options]) 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 if co.varbuf.ptr ~= nil then co.varbuf:free() end if co.navbar.ptr ~= nil then co.navbar:free() end end end end; } local terra cfg(s: &srv, befile: rawstring) lib.report('configuring backends from ', befile) ................................................................................ if c.sz > 0 then s.sources = c:crush() else s.sources.ptr = nil s.sources.ct = 0 end end terra srv:actor_stats(uid: uint64) var stats = lib.store.actor_stats { posts = 0, mutuals = 0; follows = 0, followers = 0; } for i=0,self.sources.ct do var s = self.sources.ptr[i]:actor_stats(uid) stats.posts = stats.posts + s.posts stats.mutuals = stats.mutuals + s.mutuals stats.followers = stats.followers + s.followers stats.follows = stats.follows + s.follows end return stats end terra srv:actor_auth_how(ip: lib.store.inet, usn: rawstring) var cs: lib.store.credset cs:clear() var ok = false for i=0,self.sources.ct do var set, iok = self.sources.ptr[i]:actor_auth_how(ip, usn) if iok then cs = cs + set ok = iok end end return cs, ok end terra cfgcache.methods.load :: {&cfgcache} -> {} terra cfgcache:init(o: &srv) self.overlord = o self:load() end ................................................................................ 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) if dbbind.ptr ~= nil then dbbind:free() end end srv.methods.poll = terra(self: &srv) lib.net.mg_mgr_poll(&self.webmgr,1000) end ................................................................................ end self.sources:free() end terra cfgcache:load() self.instance = self.overlord:conf_get('instance-name') self.secret = self.overlord:conf_get('server-secret') self.pol_reg = false var sreg = self.overlord:conf_get('policy-self-register') if sreg.ptr ~= nil then if lib.str.cmp(sreg.ptr, 'on') == 0 then self.pol_reg = true else self.pol_reg = false end end sreg:free() self.pol_sec = secmode.lockdown var smode = self.overlord:conf_get('policy-security') if smode.ptr ~= nil then if lib.str.cmp(smode.ptr, 'public') == 0 then self.pol_sec = secmode.public elseif lib.str.cmp(smode.ptr, 'private') == 0 then self.pol_sec = secmode.private elseif lib.str.cmp(smode.ptr, 'lockdown') == 0 then self.pol_sec = secmode.lockdown elseif lib.str.cmp(smode.ptr, 'isolate') == 0 then self.pol_sec = secmode.isolate end end smode:free() end return { overlord = srv; convo = convo; route = route; secmode = secmode; } |
Added static/default-avatar.svg version [2764158102].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="25.4mm" height="25.4mm" viewBox="0 0 25.4 25.400001" version="1.1" id="svg8" sodipodi:docname="default-avatar.svg" inkscape:version="0.92.4 (5da689c313, 2019-01-14)" inkscape:export-filename="/home/lexi/dev/parsav/static/default-avatar.png" inkscape:export-xdpi="128" inkscape:export-ydpi="128"> <defs id="defs2"> <linearGradient id="linearGradient5138" inkscape:collect="always"> <stop id="stop5134" offset="0" style="stop-color:#ffffff;stop-opacity:1" /> <stop id="stop5136" offset="1" style="stop-color:#000000;stop-opacity:0.98260868" /> </linearGradient> <linearGradient inkscape:collect="always" id="linearGradient5126"> <stop style="stop-color:#ff648d;stop-opacity:0.68235296" offset="0" id="stop5122" /> <stop style="stop-color:#ff628a;stop-opacity:0.52549022" offset="1" id="stop5124" /> </linearGradient> <linearGradient inkscape:collect="always" id="linearGradient5075"> <stop style="stop-color:#100004;stop-opacity:0" offset="0" id="stop5071" /> <stop style="stop-color:#100004;stop-opacity:0.59130436" offset="1" id="stop5073" /> </linearGradient> <radialGradient inkscape:collect="always" xlink:href="#linearGradient5075" id="radialGradient5077" cx="12.7" cy="284.29998" fx="12.7" fy="284.29998" r="12.7" gradientUnits="userSpaceOnUse" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient5126" id="radialGradient5128" cx="47.583008" cy="72.722656" fx="47.583008" fy="72.722656" r="29.078285" gradientTransform="matrix(1,0,0,0.74354777,0,18.649887)" gradientUnits="userSpaceOnUse" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient5138" id="linearGradient5132" x1="47.611866" y1="62.544083" x2="47.611866" y2="83.615517" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.26458333,0,0,0.26458333,0,271.59998)" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient5138" id="linearGradient5146" gradientUnits="userSpaceOnUse" x1="47.611866" y1="62.544083" x2="47.611866" y2="83.615517" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient5138" id="linearGradient5152" gradientUnits="userSpaceOnUse" x1="47.611866" y1="62.544083" x2="47.611866" y2="83.615517" /> <mask maskUnits="userSpaceOnUse" id="mask5148"> <path id="path5150" d="m 39.580078,61.101544 c -4.433597,0.63549 -9.840689,2.053607 -13.849609,5.18555 -8.081221,6.313436 -7.197266,18.056655 -7.197266,18.056655 h 58.099611 c 0,0 0.883952,-11.743219 -7.197268,-18.056655 -3.931143,-3.071206 -9.20211,-4.489247 -13.585935,-5.142576 -2.90603,2.777877 -5.844385,4.437505 -8.111331,4.437505 -2.278666,0 -5.237507,-1.676296 -8.158202,-4.480479 z" style="fill:url(#linearGradient5152);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" inkscape:connector-curvature="0" /> </mask> <radialGradient inkscape:collect="always" xlink:href="#linearGradient5126" id="radialGradient843" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,0.74354777,0,18.649887)" cx="47.583008" cy="72.722656" fx="47.583008" fy="72.722656" r="29.078285" /> </defs> <sodipodi:namedview id="base" pagecolor="#1a1a1a" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="5.6" inkscape:cx="41.777193" inkscape:cy="59.103277" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" units="mm" inkscape:window-width="1920" inkscape:window-height="1042" inkscape:window-x="0" inkscape:window-y="38" inkscape:window-maximized="0" /> <metadata id="metadata5"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-271.59998)"> <rect style="fill:url(#radialGradient5077);fill-opacity:1;stroke:none;stroke-width:1.05821478;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4518" width="25.4" height="25.4" x="-2.220446e-16" y="271.59998" /> <g id="g841" transform="translate(0.11032924)"> <path mask="url(#mask5148)" transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)" id="path5086" d="m 39.580078,61.101562 c -4.433597,0.635491 -9.840689,2.053587 -13.849609,5.185547 -8.081221,6.313437 -7.197266,18.056641 -7.197266,18.056641 h 58.099609 c 0,0 0.883954,-11.743204 -7.197265,-18.056641 -3.931145,-3.071199 -9.202111,-4.489236 -13.585938,-5.142578 -2.906029,2.777894 -5.844384,4.4375 -8.111328,4.4375 -2.278667,0 -5.237509,-1.67631 -8.158203,-4.480469 z" style="fill:url(#radialGradient843);fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" inkscape:connector-curvature="0" /> <path sodipodi:nodetypes="saszs" inkscape:connector-curvature="0" d="m 17.503541,281.31806 c 0.203041,-3.87199 -2.372092,-5.70473 -4.872977,-5.70473 -2.500884,0 -5.0760174,1.83274 -4.8729766,5.70473 0.2030395,3.87195 3.2371566,7.26639 4.8729766,7.26639 1.63582,0 4.669938,-3.39444 4.872977,-7.26639 z" style="fill:#ffb6c8;fill-opacity:0.86086958;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="path5081" /> </g> </g> </svg> |
Added static/padlock.svg version [a621b7bd06].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="0.2in" height="0.2in" viewBox="0 0 5.0800002 5.0800002" version="1.1" id="svg8" inkscape:version="0.92.4 (5da689c313, 2019-01-14)" sodipodi:docname="padlock.svg" inkscape:export-filename="/home/lexi/dev/parsav/static/padlock.png" inkscape:export-xdpi="240" inkscape:export-ydpi="240"> <defs id="defs2"> <linearGradient inkscape:collect="always" id="linearGradient884"> <stop style="stop-color:#d2a7b2;stop-opacity:1" offset="0" id="stop880" /> <stop style="stop-color:#bd7c8d;stop-opacity:1" offset="1" id="stop882" /> </linearGradient> <linearGradient inkscape:collect="always" id="linearGradient863"> <stop style="stop-color:#894657;stop-opacity:1" offset="0" id="stop859" /> <stop id="stop867" offset="0.35023043" style="stop-color:#733a49;stop-opacity:1" /> <stop style="stop-color:#884556;stop-opacity:1" offset="1" id="stop861" /> </linearGradient> <linearGradient inkscape:collect="always" xlink:href="#linearGradient863" id="linearGradient865" x1="4.1353216" y1="294.38614" x2="4.1353216" y2="296.71402" gradientUnits="userSpaceOnUse" gradientTransform="translate(1.0345579e-4,-0.15310986)" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient884" id="radialGradient886" cx="2.0557182" cy="292.57834" fx="2.0557182" fy="292.57834" r="1.4498034" gradientTransform="matrix(1,0,0,0.99890953,0,0.47312247)" gradientUnits="userSpaceOnUse" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient884" id="radialGradient921" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,0.99890953,5.2916667,0.47312243)" cx="2.0557182" cy="292.57834" fx="2.0557182" fy="292.57834" r="1.4498034" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient884" id="radialGradient925" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,0.99890953,4.6302084,-0.40803457)" cx="2.0557182" cy="292.57834" fx="2.0557182" fy="292.57834" r="1.4498034" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient884" id="radialGradient938" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.92374945,0,0,0.92274213,0.19377986,22.690922)" cx="2.0557182" cy="292.57834" fx="2.0557182" fy="292.57834" r="1.4498034" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient884" id="radialGradient943" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,0.99890953,-5.2916667,0.47312247)" cx="2.0557182" cy="292.57834" fx="2.0557182" fy="292.57834" r="1.4498034" /> </defs> <sodipodi:namedview id="base" pagecolor="#4b002d" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="11.2" inkscape:cx="25.660286" inkscape:cy="1.1491243" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="1920" inkscape:window-height="1042" inkscape:window-x="0" inkscape:window-y="38" inkscape:window-maximized="0" units="in" /> <metadata id="metadata5"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-291.91998)"> <path style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 2.5400001,292.32047 c -1.3794528,0 -1.3389364,1.46451 -1.3389364,1.46451 v 0.44804 H 0.94474863 v 2.32802 H 4.1352514 v -2.32802 H 3.8794531 v -0.44804 c 0,0 0.04,-1.46451 -1.339453,-1.46451 z m 0,0.5209 c 0.8656493,0 0.8402588,0.9493 0.8402588,0.9493 v 0.44235 H 1.6997413 v -0.44235 c 0,0 -0.02539,-0.9493 0.8402588,-0.9493 z" id="path945" inkscape:connector-curvature="0" /> <path id="path936" style="fill:url(#radialGradient938);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 3.8792355,293.78476 c 0,0 0.040321,-1.46443 -1.3391321,-1.46443 -1.3794528,0 -1.339132,1.46443 -1.339132,1.46443 v 0.75607 l 0.4987852,0.006 v -0.75607 c 0,0 -0.025302,-0.94927 0.8403468,-0.94927 0.8656493,0 0.8403469,0.94927 0.8403469,0.94927 v 0.75607 l 0.4987852,-0.006 z" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" /> <rect style="fill:url(#linearGradient865);fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect857" width="3.1906431" height="2.3278909" x="0.94478196" y="294.23303" /> <path style="fill:#281419;fill-opacity:1;stroke:#000000;stroke-width:0.09348611;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 2.5400001,294.87847 c -0.1706687,1e-5 -0.3090221,0.13836 -0.3090249,0.30903 1.516e-4,0.1325 0.084759,0.25016 0.2103229,0.29248 v 0 l -0.00938,0.74317 h 0.108082 0.1085944 l -0.010409,-0.74317 v 0 c 0.1257675,-0.0422 0.2106132,-0.15984 0.2108396,-0.29248 -2.8e-6,-0.17067 -0.1383562,-0.30902 -0.309025,-0.30903 z" id="path910" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" /> </g> </svg> |
Modified static/style.scss from [a7ffc6f8bf] to [7905a3444d].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
$color: hsl(323,100%,65%); %sans { font-family: "Alegreya Sans", "Open Sans", sans-serif; } %serif { font-family: Alegreya, GaramondNo8, "Garamond Premier Pro", "Adobe Garamond", Garamond, Junicode, serif; } %teletype { font-family: "Inconsolata LGC", Inconsolata, monospace; font-size: 12pt !important; } @function tone($pct) { @return adjust-color($color, $lightness: $pct) } body { @extend %sans; background-color: adjust-color($color, $lightness: -55%); color: adjust-color($color, $lightness: 25%); font-size: 14pt; margin: 0; padding: 0; } a[href] { color: adjust-color($color, $lightness: 10%); &:hover { color: white; text-shadow: 0 0 15px adjust-color($color, $lightness: 20%); } } a[href^="//"], a[href^="http://"], a[href^="https://"] { // external link &:hover::after { color: black; background-color: white; } &::after { content: "↗"; display: inline-block; color: black; margin-left: 4pt; background-color: adjust-color($color, $lightness: 10%); padding: 0 4px; text-shadow: none; padding-right: 5px; vertical-align: baseline; font-size: 80%; } } %content { width: 8in; margin: auto; } %glow { box-shadow: 0 0 20px adjust-color($color, $alpha: -0.8); } %button { @extend %sans; font-size: 14pt; padding: 0.1in 0.2in; border: 1px solid black; color: adjust-color($color, $lightness: 25%); text-shadow: 1px 1px black; text-decoration: none; text-align: center; background: linear-gradient(to bottom, adjust-color($color, $lightness: -45%), adjust-color($color, $lightness: -50%) 15%, adjust-color($color, $lightness: -50%) 75%, adjust-color($color, $lightness: -55%) ); &:hover, &:focus { @extend %glow; outline: none; color: adjust-color($color, $lightness: -55%); text-shadow: none; background: linear-gradient(to bottom, adjust-color($color, $lightness: -25%), adjust-color($color, $lightness: -30%) 15%, adjust-color($color, $lightness: -30%) 75%, adjust-color($color, $lightness: -35%) ); } &:active { color: black; padding-bottom: calc(0.1in - 2px); padding-top: calc(0.1in + 2px); background: linear-gradient(to top, adjust-color($color, $lightness: -25%), adjust-color($color, $lightness: -30%) 15%, adjust-color($color, $lightness: -30%) 75%, adjust-color($color, $lightness: -35%) ); } } button { @extend %button; &:first-of-type { @extend %button; color: white; box-shadow: inset 0 1px adjust-color($color, $lightness: -25%), inset 0 -1px adjust-color($color, $lightness: -50%); background: linear-gradient(to bottom, adjust-color($color, $lightness: -35%), adjust-color($color, $lightness: -40%) 15%, adjust-color($color, $lightness: -40%) 75%, adjust-color($color, $lightness: -45%) ); &:hover, &:focus { box-shadow: inset 0 1px adjust-color($color, $lightness: -15%), inset 0 -1px adjust-color($color, $lightness: -40%); } &:active { box-shadow: inset 0 1px adjust-color($color, $lightness: -50%), inset 0 -1px adjust-color($color, $lightness: -25%); background: linear-gradient(to top, adjust-color($color, $lightness: -30%), adjust-color($color, $lightness: -35%) 15%, adjust-color($color, $lightness: -35%) 75%, adjust-color($color, $lightness: -40%) ); } } &:hover { font-weight: bold; } } $grad-ui-focus: linear-gradient(to bottom, adjust-color($color, $lightness: -50%), adjust-color($color, $lightness: -35%) ); input[type='text'], input[type='password'], textarea { @extend %serif; padding: 0.08in 0.1in; border: 1px solid black; background: linear-gradient(to bottom, adjust-color($color, $lightness: -55%), adjust-color($color, $lightness: -40%) ); font-size: 16pt; color: adjust-color($color, $lightness: 25%); box-shadow: inset 0 0 20px -3px adjust-color($color, $lightness: -55%); &:focus { color: white; border-image: linear-gradient(to bottom, adjust-color($color, $lightness: -10%), adjust-color($color, $lightness: -30%) ) 1 / 1px; background: $grad-ui-focus; outline: none; @extend %glow; } } @mixin glass { @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); background-color: adjust-color($color, $lightness: -53%, $alpha: -0.7); } @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { background-color: adjust-color($color, $lightness: -53%, $alpha: -0.1); } } header { position: fixed; height: min-content; width: 100vw; margin: 0; padding: 0; border-bottom: 1px solid black; z-index: 1; @include glass; background: linear-gradient(to bottom, transparent, rgba(0,0,0,0.3)); > div { position: relative; max-width: 10in; margin: auto; display: grid; grid-template-columns: 1fr max-content; grid-template-rows: 1fr; h1 { all: unset; display: block; font-size: 1.4em; padding: 0.25in 0; text-shadow: 2px 2px 1px black; grid-column: 1/2; grid-row: 1/2; } nav { all: unset; display: flex; justify-content: flex-end; align-items: center; grid-column: 2/3; grid-row: 1/2; > a[href] { display: block; padding: 0.25in 0.15in; //padding: calc((25% - 1em)/2) 0.15in; &, &::after { transition: 0.3s; } text-shadow: 1px 1px 1px black; &:hover{ transform: scale(120%); } } } } } main { @extend %content; display: block; position: relative; min-height: calc(100vh - 1.1in); margin-top: 0; margin-bottom: 0; padding: 0 0.4in; padding-top: 1.1in; background-color: adjust-color($color, $lightness: -45%, $alpha: 0.4); border: { left: 1px solid black; right: 1px solid black; } } div.profile { @extend %box; padding: 0.1in; position: relative; display: grid; grid-template-columns: 2fr 1fr; grid-template-rows: 1fr 1fr; width: 100%; > .banner { grid-column: 1 / 3; grid-row: 1 / 2; display: grid; grid-template-columns: 1.1in 1fr; grid-template-rows: 0.3in 1fr; > .avatar { display: block; width: 1in; height: 1in; grid-column: 1 / 2; grid-row: 1 / 3; border: 1px solid black; } > .id { grid-column: 2 / 3; grid-row: 1 / 2; color: adjust-color($color, $lightness: 25%, $alpha: -0.4); > .nym { font-weight: bold; color: adjust-color($color, $lightness: 25%); } > .xid { color: adjust-color($color, $lightness: 20%, $alpha: -0.1); font-size: 80%; vertical-align: text-top; } } > .bio { grid-column: 2 / 3; grid-row: 2 / 3; } } > .stats { grid-column: 3 / 4; grid-row: 1 / 3; } > .menu { grid-column: 1 / 3; grid-row: 2 / 3; display: flex; justify-content: center; align-items: center; > a[href] { @extend %button; display: block; margin: 0 0.05in; } > hr { all: unset; display: block; height: 0.3in; width: 1px; border-left: 1px solid rgba(0,0,0,0.6); } } } %box { margin: auto; border: 1px solid adjust-color($color, $lightness: -55%); border-bottom: 3px solid black; box-shadow: 0 0 1px black; border-image: linear-gradient(to bottom, adjust-color($color, $lightness: -40%), adjust-color($color, $lightness: -52%) 10%, adjust-color($color, $lightness: -55%) 90%, adjust-color($color, $lightness: -60%) ) 1 / 1px; background: linear-gradient(to bottom, adjust-color($color, $lightness: -58%), adjust-color($color, $lightness: -55%) 10%, adjust-color($color, $lightness: -50%) 80%, adjust-color($color, $lightness: -45%) ); // outline: 1px solid black; } body.error .message { @extend %box; width: 4in; margin:auto; padding: 0.5in; text-align: center; } div.login { @extend %box; width: 4in; padding: 0.4in; > .msg { text-align: center; padding: 0.3in; } > .msg:first-child { padding-top: 0; } > .user { width: min-content; margin: auto; background: adjust-color($color, $lightness: -20%, $alpha: -0.3); border: 1px solid black; color: adjust-color($color, $lightness: -50%); padding: 0.1in; > img { width: 1in; height: 1in; border: 1px solid black; } > .name { @extend %serif; text-align: center; font-size: 130%; font-weight: bold; margin-top: 0.08in; } } >form { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1.2em 1fr 1fr; grid-gap: 5px; > label, input, button { display: block; } > label { grid-column: 1 / 3; grid-row: 1/2; font-weight: bold } > input { grid-column: 1 / 3; grid-row: 2/3; } > button { grid-column: 2 / 3; grid-row: 3/4; } > a { @extend %button; grid-column: 1 / 2; grid-row: 3/4; } } } form.compose { @extend %box; display: grid; grid-template-columns: 1.1in 2fr min-content 1fr; grid-template-rows: 1fr min-content; grid-gap: 2px; padding: 0.1in; > img { grid-column: 1/2; grid-row: 1/3; width: 1in; height: 1in;} > textarea { grid-column: 2/5; grid-row: 1/2; height: 3in;} > input[name="acl"] { grid-column: 2/3; grid-row: 2/3; } > button { grid-column: 4/5; grid-row: 2/3; } a.help[href] { margin-right: 0.05in } } a.help[href] { display: block; text-align: center; padding: 0.09in 0.2in; background: tone(-40%); border: 1px solid black; font-weight: bold; text-decoration: none; cursor: help; } input.acl { @extend %teletype; background: url(/s/padlock.webp) no-repeat; background-size: 20pt; background-position: 0.05in 50%; &:focus { background: url(/s/padlock.webp) no-repeat, $grad-ui-focus; background-size: 20pt; background-position: 0.05in 50%; }; padding-left: 0.40in; } div.modal { @extend %box; position: fixed; display: none; left: 0; right: 0; bottom: 0; top: 0; max-width: 7in; margin: 1in auto; padding: 0.2in 0.3in; &:target { display: block; } box-shadow: 0 0 4in 5in rgba(0,0,0,0.5); z-index: 2; > div { height: 100%; overflow-y: scroll; >p:first-of-type { margin-top: 0; } } >a[href="#0"] { // close link @extend %button; cursor: default; display: block; position: absolute; top: -0.3in; right: 0.1in; margin: 0.1in; padding: 0.1in; &:hover { font-weight: bold; } } } code { @extend %teletype; background: adjust-color($color, $lightness: -50%); border: 1px solid adjust-color($color, $lightness: -20%); padding: 2px 6px; text-shadow: 2px 2px black; } |
Added static/warn.svg version [a819eb65cc].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="1in" height="1in" viewBox="0 0 25.4 25.400001" version="1.1" id="svg8" inkscape:version="0.92.4 (5da689c313, 2019-01-14)" sodipodi:docname="warn.svg" inkscape:export-filename="/home/lexi/dev/parsav/static/warn.png" inkscape:export-xdpi="200" inkscape:export-ydpi="200"> <defs id="defs2"> <linearGradient inkscape:collect="always" id="linearGradient2322"> <stop style="stop-color:#ca0050;stop-opacity:1;" offset="0" id="stop2318" /> <stop style="stop-color:#7b0031;stop-opacity:1" offset="1" id="stop2320" /> </linearGradient> <linearGradient inkscape:collect="always" id="linearGradient2302"> <stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" id="stop2298" /> <stop style="stop-color:#ffffff;stop-opacity:0;" offset="1" id="stop2300" /> </linearGradient> <linearGradient inkscape:collect="always" id="linearGradient1228"> <stop style="stop-color:#8c0037;stop-opacity:1" offset="0" id="stop1224" /> <stop style="stop-color:#ff1a75;stop-opacity:1" offset="1" id="stop1226" /> </linearGradient> <linearGradient inkscape:collect="always" id="linearGradient851"> <stop style="stop-color:#ffffff;stop-opacity:0" offset="0" id="stop847" /> <stop style="stop-color:#ffa9c6;stop-opacity:1" offset="1" id="stop849" /> </linearGradient> <radialGradient inkscape:collect="always" xlink:href="#linearGradient851" id="radialGradient853" cx="12.699999" cy="285.82184" fx="12.699999" fy="285.82184" r="1.7905753" gradientTransform="matrix(2.2137788,-2.5697531e-5,6.7771541e-5,5.8383507,-15.43436,-1382.906)" gradientUnits="userSpaceOnUse" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient1228" id="linearGradient1230" x1="87.388672" y1="90.857147" x2="67.185623" y2="-29.734592" gradientUnits="userSpaceOnUse" /> <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter2294" x="-0.38068429" width="1.7613686" y="-0.089708175" height="1.1794164"> <feGaussianBlur inkscape:collect="always" stdDeviation="0.5243555" id="feGaussianBlur2296" /> </filter> <radialGradient inkscape:collect="always" xlink:href="#linearGradient2302" id="radialGradient2304" cx="12.58085" cy="285.25314" fx="12.58085" fy="285.25314" r="10.573204" gradientTransform="matrix(1.1874875,-1.9679213e-8,1.9699479e-8,1.1887104,-2.3811363,-53.650199)" gradientUnits="userSpaceOnUse" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient2322" id="linearGradient2324" x1="20.298828" y1="97.196846" x2="20.298828" y2="12.911131" gradientUnits="userSpaceOnUse" gradientTransform="translate(1.3858268e-6)" /> </defs> <sodipodi:namedview id="base" pagecolor="#313131" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="1.4" inkscape:cx="-71.210763" inkscape:cy="60.089308" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" units="in" inkscape:window-width="1920" inkscape:window-height="1042" inkscape:window-x="0" inkscape:window-y="38" inkscape:window-maximized="0" /> <metadata id="metadata5"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-271.59998)"> <path id="rect829" style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 11.543949,291.41721 -7e-6,0.52521 0.893337,0.89333 0.525444,2.4e-4 0.893329,-0.89333 -2.34e-4,-0.52545 -0.893338,-0.89333 h -0.525203 z m 1.995206,-2.42709 0.813727,-9.44222 -0.826441,-0.74021 h -1.652884 l -0.826441,0.74021 0.813726,9.44222 0.419578,0.5079 h 0.839157 z" inkscape:connector-curvature="0" /> <path style="fill:url(#linearGradient1230);fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 48.013672 8.5 A 46.78571 46.78571 0 0 0 44.353516 8.6621094 L 8.6132812 80.511719 A 46.78571 46.78571 0 0 0 14.115234 87.5 L 81.867188 87.5 A 46.78571 46.78571 0 0 0 87.388672 80.460938 L 51.685547 8.6855469 A 46.78571 46.78571 0 0 0 48.013672 8.5 z M 44.876953 27.242188 L 51.123047 27.242188 L 54.248047 30.039062 L 51.171875 65.726562 L 49.585938 67.646484 L 46.414062 67.646484 L 44.828125 65.726562 L 41.751953 30.039062 L 44.876953 27.242188 z M 47.007812 71.523438 L 48.992188 71.523438 L 52.369141 74.900391 L 52.369141 76.884766 L 48.992188 80.261719 L 47.007812 80.261719 L 43.630859 76.884766 L 43.630859 74.900391 L 47.007812 71.523438 z " id="path817" transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)" /> <path inkscape:connector-curvature="0" id="path838" d="m -29.39374,273.80193 a 12.378719,12.378719 0 0 0 -0.968416,0.0424 l -9.456272,19.01021 a 12.378719,12.378719 0 0 0 1.455725,1.84899 h 17.926038 a 12.378719,12.378719 0 0 0 1.460894,-1.86242 l -9.446452,-18.99057 a 12.378719,12.378719 0 0 0 -0.971517,-0.0486 z" style="fill:#ca0050;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> <path id="path1146" d="m 12.703617,273.84894 c -0.323241,0.002 -0.646295,0.016 -0.968416,0.0429 l -9.4562703,19.01021 c 0.425424,0.66116 0.9128677,1.28028 1.4557249,1.84898 H 21.660694 c 0.545337,-0.57272 1.034537,-1.19637 1.460892,-1.86242 l -9.446452,-18.99058 c -0.32307,-0.0291 -0.64716,-0.0455 -0.971517,-0.0491 z" style="fill:none;fill-opacity:1;stroke:url(#radialGradient2304);stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccc" /> <path id="path1232" style="opacity:1;fill:#ffe7f0;fill-opacity:1;stroke:none;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 12.134766,279.49414 -0.375,0.33594 0.647507,8.88867 0.07813,0.12618 h 0.429595 l 0.07813,-0.12618 0.647507,-8.88867 -0.375,-0.33594 z m 0.06945,12.07312 -3e-6,0.22524 0.383115,0.38311 0.225341,1.1e-4 0.383112,-0.38311 -1.01e-4,-0.22535 -0.383115,-0.38311 h -0.225238 z" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccccccccccc" /> <path inkscape:connector-curvature="0" d="m 11.543949,291.41721 -7e-6,0.52521 0.893337,0.89333 0.525444,2.4e-4 0.893329,-0.89333 -2.34e-4,-0.52545 -0.893338,-0.89333 h -0.525203 z m 1.995206,-2.42709 0.813727,-9.44222 -0.826441,-0.74021 h -1.652884 l -0.826441,0.74021 0.813726,9.44222 0.419578,0.5079 h 0.839157 z" style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter2294)" id="path1856" /> </g> </svg> |
Modified store.t from [213b3d2729] to [5ad659a834].
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 ... 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 ... 219 220 221 222 223 224 225 226 227 228 229 |
'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 {
rank: uint16 -- lower = more powerful except 0 = regular user
-- creating staff automatically assigns rank immediately below you
quota: uint32 -- # of allowed tweets per day; 0 = no limit
-- 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
}
terra m.rights_default()
return m.rights {
rank = 0, quota = 1000;
login = true, visible = true, post = true;
shout = true, propagate = true, upload = true;
ban = false, config = false, censor = false;
suspend = false, rebrand = false;
}
end
struct m.actor {
id: uint64
nym: str
handle: str
origin: uint64
bio: str
rights: m.rights
key: lib.mem.ptr(uint8)
xid: str
source: &m.source
}
struct m.range {
time: bool
union {
from_time: m.timepoint
from_idx: uint64
}
................................................................................
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
-- given user from a certain origin
-- origin: inet
-- handle: rawstring
actor_auth_otp: {&m.source, m.inet, rawstring, rawstring} -> uint64
actor_auth_pw: {&m.source, m.inet, rawstring, rawstring} -> uint64
-- handles password-based logins against hashed passwords
-- origin: inet
-- handle: rawstring
-- token: rawstring
actor_auth_tls: {&m.source, m.inet, rawstring} -> uint64
-- handles implicit authentication performed as part of an TLS connection
-- origin: inet
................................................................................
terra m.source:free()
self.id:free()
self.string:free()
end
m.source.metamethods.__methodmissing = macro(function(meth, obj, ...)
local q = {...}
-- syntax sugar to forward unrecognized calls onto the backend
return `obj.backend.[meth](&obj, [q])
end)
return m
|
| > > > > | > > > > > > > > > > > | < < < < < < < < | < < < < < < | < < > > > > > > > > > | < < < < < < < > > > < > > > > > > > > | | | | |
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 ... 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 ... 230 231 232 233 234 235 236 237 238 239 240 |
'follow', 'mute', 'block'
};
credset = lib.set {
'pw', 'otp', 'challenge', 'trust'
};
privset = lib.set {
'post', 'edit', 'acct', 'upload', 'censor', 'admin'
};
powerset = lib.set {
-- user powers -- default on
'login', 'visible', 'post', 'shout',
'propagate', 'upload', 'acct', 'edit';
-- admin powers -- default off
'purge', 'config', 'censor', 'suspend',
'cred', 'elevate', 'demote', 'rebrand' -- modify site's brand identity
}
}
terra m.powerset:affect_users()
return self.purge() or self.censor() or self.suspend() or
self.elevate() or self.demote() or self.rebrand() or
self.cred()
end
local str = rawstring --lib.mem.ptr(int8)
struct m.source
struct m.rights {
rank: uint16 -- lower = more powerful except 0 = regular user
-- creating staff automatically assigns rank immediately below you
quota: uint32 -- # of allowed tweets per day; 0 = no limit
powers: m.powerset
}
terra m.rights_default()
var pow: m.powerset pow:fill()
(pow.purge << false)
(pow.config << false)
(pow.censor << false)
(pow.suspend << false)
(pow.elevate << false)
(pow.demote << false)
(pow.cred << false)
(pow.rebrand << false)
return m.rights { rank = 0, quota = 1000, powers = pow; }
end
struct m.actor {
id: uint64
nym: str
handle: str
origin: uint64
bio: str
avatar: str
knownsince: int64
rights: m.rights
key: lib.mem.ptr(uint8)
-- ephemera
xid: str
source: &m.source
}
struct m.actor_stats {
posts: intptr
follows: intptr
followers: intptr
mutuals: intptr
}
struct m.range {
time: bool
union {
from_time: m.timepoint
from_idx: uint64
}
................................................................................
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_stats: {&m.source, uint64} -> m.actor_stats
actor_auth_how: {&m.source, m.inet, rawstring} -> {m.credset, bool}
-- returns a set of auth method categories that are available for a
-- given user from a certain origin
-- origin: inet
-- username: rawstring
actor_auth_otp: {&m.source, m.inet, rawstring, rawstring} -> uint64
actor_auth_pw: {&m.source, m.inet, lib.mem.ptr(int8), lib.mem.ptr(int8) } -> uint64
-- handles password-based logins against hashed passwords
-- origin: inet
-- handle: rawstring
-- token: rawstring
actor_auth_tls: {&m.source, m.inet, rawstring} -> uint64
-- handles implicit authentication performed as part of an TLS connection
-- origin: inet
................................................................................
terra m.source:free()
self.id:free()
self.string:free()
end
m.source.metamethods.__methodmissing = macro(function(meth, obj, ...)
local q = {...}
-- syntax sugar to forward unrecognized calls onto the backend
return quote var r = obj.backend.[meth](&obj, [q]) in r end
end)
return m
|
Modified str.t from [c91733fef5] to [c8d105a016].
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 .. 65 66 67 68 69 70 71 72 73 74 75 76 77 78 .. 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
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
end
end
struct m.acc {
buf: rawstring
sz: intptr
run: intptr
................................................................................
var pt: lib.mem.ptr(int8)
pt.ptr = self.buf
pt.ct = self.sz
self.buf = nil
self.sz = 0
return pt
end;
terra m.acc:push(str: rawstring, len: intptr)
--var llen = len
if str == nil then return self end
--if str[len - 1] == 0xA then llen = llen - 1 end -- don't display newlines in debug output
-- lib.dbg('pushing "',{str,llen},'" onto accumulator')
if self.buf == nil then self:init(self.run) end
................................................................................
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 = {}
|
> > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 .. 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 ... 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
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);
}
do local strptr = (lib.mem.ptr(int8))
local byteptr = (lib.mem.ptr(uint8))
strptr.metamethods.__cast = function(from,to,e)
if from == &int8 then
return `strptr {ptr = e, ct = m.sz(e)}
elseif to == &int8 then
return e.ptr
end
end
terra strptr:cmp(other: strptr)
var sz = lib.math.biggest(self.ct, other.ct)
for i = 0, sz do
if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end
if self.ptr[i] ~= other.ptr[i] then return false end
end
return true
end
terra byteptr:cmp(other: byteptr)
var sz = lib.math.biggest(self.ct, other.ct)
for i = 0, sz do
if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end
if self.ptr[i] ~= other.ptr[i] then return false end
end
return true
end
end
struct m.acc {
buf: rawstring
sz: intptr
run: intptr
................................................................................
var pt: lib.mem.ptr(int8)
pt.ptr = self.buf
pt.ct = self.sz
self.buf = nil
self.sz = 0
return pt
end;
terra m.acc:cue(sz: intptr)
if sz <= self.run then return end
self.run = sz
if self.space - self.sz < self.run then
self.space = self.sz + self.run
self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.space))
end
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
................................................................................
return self
end;
m.lit = macro(function(str)
return `[lib.mem.ref(int8)] {ptr = [str:asvalue()], ct = [#(str:asvalue())]}
end)
m.acc.methods.lpush = macro(function(self,str)
return `self:push([str:asvalue()], [#(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 = {}
|
Modified tpl.t from [ad44dd6129] to [3cd51c8b03].
27 28 29 30 31 32 33 34 35 36 37 38 39 40 .. 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 .. 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
local segs = {}
local constlen = 0
-- strip out all irrelevant whitespace to tidy things up
-- TODO: find way to exclude <pre> tags?
str = str:gsub('[\n^]%s+','')
str = str:gsub('%s+[\n$]','')
str = str:gsub('\n','')
for start, key, stop in string.gmatch(str,'()'..tplchar..'(%w+)()') do
if string.sub(str,start-1,start-1) ~= '\\' then
segs[#segs+1] = string.sub(str,last,start-1)
fields[#segs] = key
last = stop
end
end
................................................................................
[runningtally] = [runningtally] + lib.str.sz([symself].[key])*fac
end
end
end
local copiers = {}
local senders = {}
local symtxt = symbol(lib.mem.ptr(int8))
local cpypos = symbol(&opaque)
local destcon = symbol(&lib.net.mg_connection)
for idx, seg in ipairs(segs) do
copiers[#copiers+1] = quote [cpypos] = lib.mem.cpy([cpypos], [&opaque]([seg]), [#seg]) end
senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
if fields[idx] then
copiers[#copiers+1] = quote
[cpypos] = lib.mem.cpy([cpypos],
[&opaque](symself.[fields[idx]]),
lib.str.sz(symself.[fields[idx]]))
end
senders[#senders+1] = quote
................................................................................
lib.dbg(['compiling template ' .. tid])
[tallyup]
var [symtxt] = lib.mem.heapa(int8, [runningtally])
var [cpypos] = [&opaque](symtxt.ptr)
[copiers]
@[&int8](cpypos) = 0
return symtxt
end
rec.methods.send = terra([symself], [destcon], code: uint16, hd: lib.mem.ptr(lib.http.header))
lib.dbg(['transmitting template ' .. tid])
[tallyup]
lib.net.mg_printf([destcon], 'HTTP/1.1 %s', lib.http.codestr(code))
for i = 0, hd.ct do
lib.net.mg_printf([destcon], '%s: %s\r\n', hd.ptr[i].key, hd.ptr[i].value)
|
> > > > > > > > > > > |
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 .. 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 .. 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
local segs = {}
local constlen = 0
-- strip out all irrelevant whitespace to tidy things up
-- TODO: find way to exclude <pre> tags?
str = str:gsub('[\n^]%s+','')
str = str:gsub('%s+[\n$]','')
str = str:gsub('\n','')
str = str:gsub('</a><a ','</a> <a ') -- keep nav links from getting smooshed
for start, key, stop in string.gmatch(str,'()'..tplchar..'(%w+)()') do
if string.sub(str,start-1,start-1) ~= '\\' then
segs[#segs+1] = string.sub(str,last,start-1)
fields[#segs] = key
last = stop
end
end
................................................................................
[runningtally] = [runningtally] + lib.str.sz([symself].[key])*fac
end
end
end
local copiers = {}
local senders = {}
local appenders = {}
local symtxt = symbol(lib.mem.ptr(int8))
local cpypos = symbol(&opaque)
local accumulator = symbol(&lib.str.acc)
local destcon = symbol(&lib.net.mg_connection)
for idx, seg in ipairs(segs) do
copiers[#copiers+1] = quote [cpypos] = lib.mem.cpy([cpypos], [&opaque]([seg]), [#seg]) end
senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
appenders[#appenders+1] = quote [accumulator]:push([seg], [#seg]) end
if fields[idx] then
copiers[#copiers+1] = quote
[cpypos] = lib.mem.cpy([cpypos],
[&opaque](symself.[fields[idx]]),
lib.str.sz(symself.[fields[idx]]))
end
senders[#senders+1] = quote
................................................................................
lib.dbg(['compiling template ' .. tid])
[tallyup]
var [symtxt] = lib.mem.heapa(int8, [runningtally])
var [cpypos] = [&opaque](symtxt.ptr)
[copiers]
@[&int8](cpypos) = 0
return symtxt
end
rec.methods.append = terra([symself], [accumulator])
lib.dbg(['appending template ' .. tid])
[tallyup]
accumulator:cue([runningtally])
[appenders]
return accumulator
end
rec.methods.send = terra([symself], [destcon], code: uint16, hd: lib.mem.ptr(lib.http.header))
lib.dbg(['transmitting template ' .. tid])
[tallyup]
lib.net.mg_printf([destcon], 'HTTP/1.1 %s', lib.http.codestr(code))
for i = 0, hd.ct do
lib.net.mg_printf([destcon], '%s: %s\r\n', hd.ptr[i].key, hd.ptr[i].value)
|
Added view/compose.tpl version [09c6180294].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
<form class="compose" method="post"> <img src="/avi/@handle"> <textarea autofocus name="post" placeholder="it was a dark and stormy night…">@content</textarea> <input required type="text" name="acl" class="acl" value="@acl"> <a href="#aclhelp" class="help">?</a> <button type="submit">commit</button> </form> <div id="aclhelp" class="modal"> <a href="#0">close</a> <div> <p>to control who can see your post (and how far it is propagated), <code>parsav</code> uses <strong>ACL expressions</strong>. this is roughly equivalent to scoping in pleroma or GNU social terms. an ACL expression consists of one or more space-separated terms, each of which match a certain set of users. a term can be negated by prefixing it with <code>~</code>, a tilde character, so <code>~all</code> matches nobody, and <code>~followed</code> matches users you do not follow.</p> <ul> <li><strong>all</strong>: matches any and all users</li> <li><strong>local</strong>: matches users who belong to this instance</li> <li><strong>mutuals</strong>: matches users you follow who also follow you</li> <li><strong>followed</strong>: matches users you follow</li> <li><strong>followers</strong>: matches users who follow you</li> <li><strong>groupies</strong>: matches users who follow you, but whom you do not follow</li> <li><strong>mentioned</strong>: matches users who are mentioned in the post</li> <li><strong>staff</strong>: matches instance staff (equivalent to <code>~%0</code>)</li> <li><strong>admin</strong>: matches the individual named as the instance administrator, if any</li> <li><strong>\@</strong><em>handle</em>: matches the user <em>handle</em></li> <li><strong>+</strong><em>circle</em>: matches users you have categorized under <em>circle</em></li> <li><strong>#</strong><em>room</em>: matches users who are members of <em>room</em></li> <li><strong>%</strong><em>rank</em>: matches users of <em>rank</em> or higher (e.g. <code>%3</code> matches users of rank 3, 2, and 1). as a special case, <code>%0</code> matches ordinary users</li> <li><strong>#</strong><em>room</em><strong>%</strong><em>rank</em>: matches users who hold <em>rank</em> in <em>room</em></li> <li><strong><</strong><em>title</em><strong>></strong>: matches peers of the net who have been created <em>title</em> by the sovereign</li> <li><strong>#</strong><em>room</em><strong><</strong><em>title</em><strong>></strong>: matches peers of the chat who have been created <em>title</em> by <em>room</em> staff</li> </ul> <p>to evaluate an ACL expression, <code>parsav</code> reads each term from start to finish. for each term, it considers whether it describes the user who is attempting to access the content. if the term matches, its policy is applied and the expression completes. if the term doesn't match, the server proceeds on to the next term and the process repeats until it finds a matching term or runs out of terms, applying the fallback policy.</p> <p><strong>policy</strong> is whether a term grants or denies access. the default term policy is <strong>allow</strong>, but you can control the policy with the keywords <code>allow</code> and <code>deny</code>. if a term finishes evaluating without any match being found, a fallback policy is applied; this fallback is the opposite of whatever the current policy is. this sounds confusing but makes ACL expressions much more intuitive; <code>allow \@bob</code> and <code>deny trent</code> do exactly what you'd expect &em; the former allows bob and only bob in; the latter denies access only to trent, but grants access to the rest of the world.</p> <p>expressions must contain at least one term to be valid. if they consist only of policy keywords, they will be rejected.</p> <p>in effect, this all means that an ACL expression can be treated as a simple list of who is allowed to view your post. for instance, an expression of <code>local</code> means only local users can view it. however, much more complex expressions are possible.</p> <ul> <li><code>deny groupies allow +illuminati</code>: permits access to the illuminati, but excluding those members who are groupies</li> <li><code>+illuminati deny groupies</code>: allows access to everyone but groupies (unless they're in the illuminati)</li> <li><code>\@eve \@alice\@nowhere.tld deny \@bob \@trent\@witches.live</code>: grants access to eve and alice, but locks out bob and trent</li> <li><code><grand duke> #4th-intl<comrade></code>: restricts the post to the eyes of the Fourth International's secret cabal of anointed comrades and the grand dukes of the Empire</li> <li><code>deny ~%3</code>: blocks a post from being seen by anyone with a staff rank level below 3</li> </ul> <p><strong>limitations:</strong> to inhibit potential denial-of-service attacks, ACL expressions can be a maximum of 128 characters, can contain at most 16 words, and cannot trigger queries against other servers. all information needed to evaluate an ACL expression must be known locally. this is particularly relevant with respect to rooms.</p> </div></div> |
Modified view/docskel.tpl from [004398018e] to [cb4a31dcc6].
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> |
> | > > > > > > | > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!doctype html> <html> <head> <title>@instance :: @title</title> <link rel="stylesheet" href="/s/style.css"> </head> <body class="@class"> <header><div> <h1>@title</h1> <nav> <a href="/instance">instance</a> @navlinks </nav> </div></header> <main> @body </main> </body> </html> |
Modified view/load.lua from [53cfafaa7c] to [1503c625ad].
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
18
19
20
21
22
23
24
25
26
|
-- 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')
................................................................................
txt = txt:gsub('\\(!%b[])', '%1')
txt = txt:gsub('\\(!!)', '%1')
return txt
end
local views = {}
for _,n in pairs(sources) do views[n] = ingest(n .. '.tpl') end
return views
|
>
>
>
|
|
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
21
22
23
24
25
26
27
28
29
|
-- copies them into a data structure we can then
-- create templates from when we return to terra
local path = ...
local sources = {
'docskel';
'tweet';
'profile';
'compose';
'login-username';
'login-challenge';
}
local ingest = function(filename)
local hnd = io.open(path..'/'..filename)
local txt = hnd:read('*a')
io.close(hnd)
txt = txt:gsub('([^\\])!%b[]', '%1')
................................................................................
txt = txt:gsub('\\(!%b[])', '%1')
txt = txt:gsub('\\(!!)', '%1')
return txt
end
local views = {}
for _,n in pairs(sources) do views[n:gsub('-','_')] = ingest(n .. '.tpl') end
return views
|
Added view/login-challenge.tpl version [c8511de2b7].
> > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div class="login"> <div class="user"> <img src="/avi/@handle"> <div class="name">@name</div> </div> <div class="msg">@challenge</div> <form action="/login" method="post"> <label for="response">@label</label> <input type="hidden" name="user" value="@handle"> <input type="password" name="response" id="response" autofocus required> <button type="submit" name="authmethod" value="@method">authenticate</button> <a href="/login">cancel</a> </form> </div> |
Added view/login-username.tpl version [4dc628d5ef].
> > > > > > > > |
1 2 3 4 5 6 7 8 |
<div class="login"> <div class="msg">@loginmsg</div> <form action="/login" method="post"> <label for="user">local handle</label> <input type="text" name="user" id="user" autofocus required> <button type="submit">log on</button> </form> </div> |
Modified view/profile.tpl from [abc7153f4d] to [6c61b2a3c1].
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> |
| | | | | | > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<div class="profile"> <div class="banner"> <img class="avatar" src="@avatar"> <div class="id"><span class="nym">@nym</span> [<span class="xid">@xid</span>]</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>@timephrase</th> <td>@tweetday</td></tr> </table> <div class="menu"> <a href="/@xid">posts</a> <a href="/@xid/media">media</a> <a href="/@xid/social">associates</a> <hr> @auxbtn </div> </div> |