| Comment: | add lots more shit |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
d4ecea913fb950b3a9a4686743283906 |
| User & Date: | lexi on 2020-12-31 00:15:53 |
| Other Links: | manifest | tags |
|
2020-12-31
| ||
| 02:18 | start work on user mgmt check-in: db4c5fd644 user: lexi tags: trunk | |
| 00:15 | add lots more shit check-in: d4ecea913f user: lexi tags: trunk | |
|
2020-12-30
| ||
| 02:44 | enable profile editing check-in: ac4a630ad5 user: lexi tags: trunk | |
Modified acl.t from [3939510d0a] to [7cc6c4467d].
1 |
-- vim: ft=terra |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 m = { agentkind = lib.enum { 'user', 'circle' }; } struct m.agent { kind: m.agentkind.t id: uint64 } terra m.eval(expr: lib.str.t, agent: m.agent) end terra lib.store.post:save(ctupdate: bool) -- this post handles the messy details of registering a post's -- circles and actors, and increments the edit-count if ctupdate -- is true, which is should be in almost all cases. if ctupdate then self.chgcount = self.chgcount + 1 self.edited = lib.osclock.time(nil) end -- TODO extract mentions from body, circles from acl self.source:post_save(self) end return m |
Modified backend/pgsql.t from [d54604496b] to [af6b4187ca].
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 ... 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 ... 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 ... 234 235 236 237 238 239 240 241 242 243 244 245 246 247 ... 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 ... 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 ... 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 ... 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 ... 959 960 961 962 963 964 965 966 967 968 969 970 971 972 .... 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 |
values (
(select count(tweets.*)::bigint from tweets),
(select count(follows.*)::bigint from follows),
(select count(followers.*)::bigint from followers),
(select count(mutuals.*)::bigint from 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 (
................................................................................
(select count(*) from mts where kind like 'otp-%') > 0,
(select count(*) from mts where kind like 'challenge-%') > 0,
(select count(*) from mts where kind = 'trust') > 0
]]; -- cheat
};
actor_session_fetch = {
params = {uint64, lib.store.inet}, sql = [[
select a.id, a.nym, a.handle, a.origin, a.bio,
a.avataruri, a.rank, a.quota, a.key, a.epithet,
extract(epoch from a.knownsince)::bigint,
coalesce(a.handle || '@' || s.domain,
'@' || a.handle) as xid,
au.restrict,
................................................................................
array['admin' ] <@ au.restrict as can_admin
from parsav_auth au
left join parsav_actors a on au.uid = a.id
left join parsav_servers s on a.origin = s.id
where au.aid = $1::bigint and au.blacklist = false and
(au.netmask is null or au.netmask >> $2::inet)
]];
};
actor_powers_fetch = {
params = {uint64}, sql = [[
select key, allow from parsav_rights where actor = $1::bigint
]]
................................................................................
actor_power_delete = {
params = {uint64,lib.mem.ptr(int8)}, cmd = true, sql = [[
delete from parsav_rights where
actor = $1::bigint and
key = $2::text
]]
};
auth_create_pw = {
params = {uint64, lib.mem.ptr(uint8)}, cmd = true, sql = [[
insert into parsav_auth (uid, name, kind, cred) values (
$1::bigint,
(select handle from parsav_actors where id = $1::bigint),
'pw-sha256', $2::bytea
................................................................................
auth_purge_type = {
params = {rawstring, uint64, rawstring}, cmd = true, sql = [[
delete from parsav_auth where
((uid = 0 and name = $1::text) or uid = $2::bigint) and
kind like $3::text
]]
};
post_create = {
params = {uint64, rawstring, rawstring, rawstring}, sql = [[
insert into parsav_posts (
author, subject, acl, body,
posted, discovered,
circles, mentions
................................................................................
) values (
$1::bigint, case when $2::text = '' then null else $2::text end,
$3::text, $4::text,
now(), now(), array[]::bigint[], array[]::bigint[]
) returning id
]]; -- TODO array handling
};
post_enum_author_uid = {
params = {uint64,uint64,uint64,uint64, uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
p.parent, p.convoheaduri
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where p.author = $5::bigint and
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted)
order by (p.posted, p.discovered) desc
limit case when $3::bigint = 0 then null
................................................................................
timeline_instance_fetch = {
params = {uint64, uint64, uint64, uint64}, sql = [[
select true,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
p.parent, null::text
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted) and
(a.origin is null)
order by (p.posted, p.discovered) desc
................................................................................
local terra row_to_post(r: &pqr, row: intptr): lib.mem.ptr(lib.store.post)
var subj: rawstring, sblen: intptr
var cvhu: rawstring, cvhlen: intptr
if r:null(row,3)
then subj = nil sblen = 0
else subj = r:string(row,3) sblen = r:len(row,3)+1
end
if r:null(row,9)
then cvhu = nil cvhlen = 0
else cvhu = r:string(row,9) cvhlen = r:len(row,9)+1
end
var p = [ lib.str.encapsulate(lib.store.post, {
subject = { `subj, `sblen };
acl = {`r:string(row,4), `r:len(row,4)+1};
body = {`r:string(row,5), `r:len(row,5)+1};
convoheaduri = { `cvhu, `cvhlen }; --FIXME
}) ]
p.ptr.id = r:int(uint64,row,1)
p.ptr.author = r:int(uint64,row,2)
p.ptr.posted = r:int(uint64,row,6)
p.ptr.discovered = r:int(uint64,row,7)
if r:null(row,8)
then p.ptr.parent = 0
else p.ptr.parent = r:int(uint64,row,8)
end
p.ptr.localpost = r:bool(row,0)
return p
end
local terra row_to_actor(r: &pqr, row: intptr): lib.mem.ptr(lib.store.actor)
var a: lib.mem.ptr(lib.store.actor)
................................................................................
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)
if r.sz == 0 then goto fail end
do defer r:free()
if r:null(0,0) then goto fail end
var a = row_to_actor(&r, 0)
a.ptr.source = src
................................................................................
): uint64
var r = queries.post_create.exec(src,post.author,post.subject,post.acl,post.body)
if r.sz == 0 then return 0 end
defer r:free()
var id = r:int(uint64,0,0)
return id
end];
timeline_instance_fetch = [terra(src: &lib.store.source, rg: lib.store.range)
var r = pqr { sz = 0 }
var A,B,C,D = rg:matrix() -- :/
r = queries.timeline_instance_fetch.exec(src,A,B,C,D)
var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz)
................................................................................
detach: bool
): {}
if detach
then queries.post_attach_ctl_del.exec(src,post,artifact)
else queries.post_attach_ctl_ins.exec(src,post,artifact)
end
end];
actor_auth_register_uid = nil; -- TODO better support non-view based auth
}
return b
|
| | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | > | | > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 ... 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 ... 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 ... 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 ... 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 ... 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 ... 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 ... 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 .... 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 .... 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 |
values (
(select count(tweets.*)::bigint from tweets),
(select count(follows.*)::bigint from follows),
(select count(followers.*)::bigint from followers),
(select count(mutuals.*)::bigint from mutuals)
)
]]):gsub('<(%w+)>',function(r) return tostring(lib.store.relation.idvmap[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 (
................................................................................
(select count(*) from mts where kind like 'otp-%') > 0,
(select count(*) from mts where kind like 'challenge-%') > 0,
(select count(*) from mts where kind = 'trust') > 0
]]; -- cheat
};
actor_session_fetch = {
params = {uint64, lib.store.inet, int64}, sql = [[
select a.id, a.nym, a.handle, a.origin, a.bio,
a.avataruri, a.rank, a.quota, a.key, a.epithet,
extract(epoch from a.knownsince)::bigint,
coalesce(a.handle || '@' || s.domain,
'@' || a.handle) as xid,
au.restrict,
................................................................................
array['admin' ] <@ au.restrict as can_admin
from parsav_auth au
left join parsav_actors a on au.uid = a.id
left join parsav_servers s on a.origin = s.id
where au.aid = $1::bigint and au.blacklist = false and
(au.netmask is null or au.netmask >> $2::inet) and
($3::bigint = 0 or --slightly abusing the epoch time fmt here, but
((a.authtime is null or a.authtime <= to_timestamp($3::bigint)) and
(au.valperiod is null or au.valperiod <= to_timestamp($3::bigint))))
]];
};
actor_powers_fetch = {
params = {uint64}, sql = [[
select key, allow from parsav_rights where actor = $1::bigint
]]
................................................................................
actor_power_delete = {
params = {uint64,lib.mem.ptr(int8)}, cmd = true, sql = [[
delete from parsav_rights where
actor = $1::bigint and
key = $2::text
]]
};
auth_sigtime_user_fetch = {
params = {uint64}, sql = [[
select extract(epoch from authtime)::bigint
from parsav_actors where id = $1::bigint
]];
};
auth_sigtime_user_alter = {
params = {uint64,int64}, cmd = true, sql = [[
update parsav_actors set
authtime = to_timestamp($2::bigint)
where id = $1::bigint
]];
};
auth_create_pw = {
params = {uint64, lib.mem.ptr(uint8)}, cmd = true, sql = [[
insert into parsav_auth (uid, name, kind, cred) values (
$1::bigint,
(select handle from parsav_actors where id = $1::bigint),
'pw-sha256', $2::bytea
................................................................................
auth_purge_type = {
params = {rawstring, uint64, rawstring}, cmd = true, sql = [[
delete from parsav_auth where
((uid = 0 and name = $1::text) or uid = $2::bigint) and
kind like $3::text
]]
};
post_save = {
params = {
uint64, uint32, int64;
rawstring, rawstring, rawstring;
}, cmd = true, sql = [[
update parsav_posts set
subject = $4::text,
acl = $5::text,
body = $6::text,
chgcount = $2::integer,
edited = to_timestamp($3::bigint)
where id = $1::bigint
]]
};
post_create = {
params = {uint64, rawstring, rawstring, rawstring}, sql = [[
insert into parsav_posts (
author, subject, acl, body,
posted, discovered,
circles, mentions
................................................................................
) values (
$1::bigint, case when $2::text = '' then null else $2::text end,
$3::text, $4::text,
now(), now(), array[]::bigint[], array[]::bigint[]
) returning id
]]; -- TODO array handling
};
post_fetch = {
params = {uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, p.convoheaduri, p.chgcount
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where p.id = $1::bigint
]];
};
post_enum_author_uid = {
params = {uint64,uint64,uint64,uint64, uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, p.convoheaduri, p.chgcount
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where p.author = $5::bigint and
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted)
order by (p.posted, p.discovered) desc
limit case when $3::bigint = 0 then null
................................................................................
timeline_instance_fetch = {
params = {uint64, uint64, uint64, uint64}, sql = [[
select true,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, null::text, p.chgcount
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted) and
(a.origin is null)
order by (p.posted, p.discovered) desc
................................................................................
local terra row_to_post(r: &pqr, row: intptr): lib.mem.ptr(lib.store.post)
var subj: rawstring, sblen: intptr
var cvhu: rawstring, cvhlen: intptr
if r:null(row,3)
then subj = nil sblen = 0
else subj = r:string(row,3) sblen = r:len(row,3)+1
end
if r:null(row,10)
then cvhu = nil cvhlen = 0
else cvhu = r:string(row,10) cvhlen = r:len(row,10)+1
end
var p = [ lib.str.encapsulate(lib.store.post, {
subject = { `subj, `sblen };
acl = {`r:string(row,4), `r:len(row,4)+1};
body = {`r:string(row,5), `r:len(row,5)+1};
convoheaduri = { `cvhu, `cvhlen }; --FIXME
}) ]
p.ptr.id = r:int(uint64,row,1)
p.ptr.author = r:int(uint64,row,2)
p.ptr.posted = r:int(uint64,row,6)
p.ptr.discovered = r:int(uint64,row,7)
p.ptr.edited = r:int(uint64,row,8)
if r:null(row,9)
then p.ptr.parent = 0
else p.ptr.parent = r:int(uint64,row,9)
end
if r:null(row,11)
then p.ptr.chgcount = 0
else p.ptr.chgcount = r:int(uint32,row,11)
end
p.ptr.localpost = r:bool(row,0)
return p
end
local terra row_to_actor(r: &pqr, row: intptr): lib.mem.ptr(lib.store.actor)
var a: lib.mem.ptr(lib.store.actor)
................................................................................
s.mutuals = r:int(uint64, 0, 3)
return s
end];
actor_session_fetch = [terra(
src: &lib.store.source,
aid: uint64,
ip : lib.store.inet,
issuetime: lib.store.timepoint
): { lib.stat(lib.store.auth), lib.mem.ptr(lib.store.actor) }
var r = queries.actor_session_fetch.exec(src, aid, ip, issuetime)
if r.sz == 0 then goto fail end
do defer r:free()
if r:null(0,0) then goto fail end
var a = row_to_actor(&r, 0)
a.ptr.source = src
................................................................................
): uint64
var r = queries.post_create.exec(src,post.author,post.subject,post.acl,post.body)
if r.sz == 0 then return 0 end
defer r:free()
var id = r:int(uint64,0,0)
return id
end];
post_fetch = [terra(
src: &lib.store.source,
post: uint64
): lib.mem.ptr(lib.store.post)
var r = queries.post_fetch.exec(src, post)
if r.sz == 0 then return [lib.mem.ptr(lib.store.post)].null() end
var p = row_to_post(&r, 0)
p.ptr.source = src
return p
end];
timeline_instance_fetch = [terra(src: &lib.store.source, rg: lib.store.range)
var r = pqr { sz = 0 }
var A,B,C,D = rg:matrix() -- :/
r = queries.timeline_instance_fetch.exec(src,A,B,C,D)
var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz)
................................................................................
detach: bool
): {}
if detach
then queries.post_attach_ctl_del.exec(src,post,artifact)
else queries.post_attach_ctl_ins.exec(src,post,artifact)
end
end];
post_save = [terra(
src: &lib.store.source,
post: &lib.store.post
): {}
queries.post_save.exec(src,
post.id, post.chgcount, post.edited,
post.subject, post.acl, post.body)
end];
auth_sigtime_user_fetch = [terra(
src: &lib.store.source,
uid: uint64
): lib.store.timepoint
var r = queries.auth_sigtime_user_fetch.exec(src, uid)
if r.sz > 0 then defer r:free()
var t = r:int(int64,0,0)
return t
else return 0 end
end];
auth_sigtime_user_alter = [terra(
src: &lib.store.source,
uid: uint64,
time: lib.store.timepoint
): {} queries.auth_sigtime_user_alter.exec(src, uid, time) end];
actor_auth_register_uid = nil; -- TODO better support non-view based auth
}
return b
|
Modified backend/schema/pgsql-auth.sql from [1170b3857b] to [8bbbf24f23].
42 43 44 45 46 47 48 49 50 51 |
-- if the credential matches, access will be denied, even if -- non-blacklisted credentials match. most useful with -- uid = null, kind = trust, cidr = (untrusted IP range) valperiod timestamp default now(), -- cookies bearing timestamps earlier than this point in time -- will be considered invalid and will not grant access unique(name,kind,cred) ); |
> > > > |
42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
-- if the credential matches, access will be denied, even if -- non-blacklisted credentials match. most useful with -- uid = null, kind = trust, cidr = (untrusted IP range) valperiod timestamp default now(), -- cookies bearing timestamps earlier than this point in time -- will be considered invalid and will not grant access comment text, -- a field the user can use to identify the specific credential, -- in order to aid credential management unique(name,kind,cred) ); |
Modified backend/schema/pgsql.sql from [cb277a93b1] to [135f2b367a].
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
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
knownsince timestamp,
bio text,
avatarid bigint, -- artifact id, null if remote
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
epithet text,
................................................................................
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,
parent bigint not null default 0, -- if post: part of conversation; if chatroom: top-level post
circles bigint[], -- TODO at edit or creation, iterate through each circle
mentions bigint[], -- a user has, check if it can see her post, and if so add
artifacts bigint[],
convoheaduri text
-- only used for tracking foreign conversations and tying them to post heads;
|
|
>
>
|
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
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 knownsince timestamp not null default now(), bio text, avatarid bigint, -- artifact id, null if remote 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 epithet text, ................................................................................ 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, chgcount integer not null default 0, edited timestamp, parent bigint not null default 0, -- if post: part of conversation; if chatroom: top-level post circles bigint[], -- TODO at edit or creation, iterate through each circle mentions bigint[], -- a user has, check if it can see her post, and if so add artifacts bigint[], convoheaduri text -- only used for tracking foreign conversations and tying them to post heads; |
Modified cmdparse.t from [49c267b075] to [dec7841af5].
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
for o,desc in pairs(tbl) do
local consume = desc.consume or 0
local incr = desc.inc or 0
options.entries[#options.entries + 1] = {
field = o, type = (consume > 0) and &rawstring or
(incr > 0) and uint or bool
}
helpstr = helpstr .. string.format(' -%s --%s: %s\n',
desc[1], sanitize(o), desc[2])
end
for o,desc in pairs(tbl) do
local flag = desc[1]
local consume = desc.consume or 0
local incr = desc.inc or 0
init[#init + 1] = quote [self].[o] = [
(consume > 0 and `nil) or
................................................................................
end
end
elseif incr > 0 then
ch = quote [self].[o] = [self].[o] + incr end
else ch = quote
[self].[o] = true
end end
shortcases[#shortcases + 1] = quote
case [int8]([string.byte(flag)]) then [ch] end
end
longcases[#longcases + 1] = quote
if lib.str.cmp([arg]+2, [sanitize(o)]) == 0 then [ch] goto [skip] end
end
end
terra options:free() self.arglist:free() end
options.methods.parse = terra([self], [argc], [argv])
|
>
|
|
>
>
>
>
>
|
|
>
|
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
..
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
for o,desc in pairs(tbl) do
local consume = desc.consume or 0
local incr = desc.inc or 0
options.entries[#options.entries + 1] = {
field = o, type = (consume > 0) and &rawstring or
(incr > 0) and uint or bool
}
if desc[1] then
helpstr = helpstr .. string.format(' -%s --%s: %s\n',
desc[1], sanitize(o), desc[2])
else
helpstr = helpstr .. string.format(' --%s: %s\n',
sanitize(o), desc[2])
end
end
for o,desc in pairs(tbl) do
local flag = desc[1]
local consume = desc.consume or 0
local incr = desc.inc or 0
init[#init + 1] = quote [self].[o] = [
(consume > 0 and `nil) or
................................................................................
end
end
elseif incr > 0 then
ch = quote [self].[o] = [self].[o] + incr end
else ch = quote
[self].[o] = true
end end
if flag ~= nil then
shortcases[#shortcases + 1] = quote
case [int8]([string.byte(flag)]) then [ch] end
end
end
longcases[#longcases + 1] = quote
if lib.str.cmp([arg]+2, [sanitize(o)]) == 0 then [ch] goto [skip] end
end
end
terra options:free() self.arglist:free() end
options.methods.parse = terra([self], [argc], [argv])
|
Modified config.lua from [931922a3e1] to [3d0e5432a4].
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
when = os.date();
};
feat = {};
debug = u.tobool(default('parsav_enable_debug',true));
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 os.getenv('parsav_let_me_be_an_idiot') == "i know what i'm doing" then
conf.braingeniousmode = true -- SOUND GENERAL QUARTERS
end
if u.ping '.fslckout' or u.ping '_FOSSIL_' then
if u.ping '_FOSSIL_' then default_os = 'windows' end
|
> > > > > > |
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
when = os.date();
};
feat = {};
debug = u.tobool(default('parsav_enable_debug',true));
backends = defaultlist('parsav_backends', 'pgsql');
braingeniousmode = false;
embeds = {
-- TODO with gzip compression, svg is dramatically superior to webp
-- we should have a build-time option to serve svg so instances
-- proxied behind nginx can serve svgz, or possibly just straight-up
-- add support for content-encoding headers and pre-compress the
-- damn things before compiling
{'style.css', 'text/css'};
{'default-avatar.webp', 'image/webp'};
{'padlock.webp', 'image/webp'};
{'warn.webp', 'image/webp'};
{'query.webp', 'image/webp'};
};
}
if os.getenv('parsav_let_me_be_an_idiot') == "i know what i'm doing" then
conf.braingeniousmode = true -- SOUND GENERAL QUARTERS
end
if u.ping '.fslckout' or u.ping '_FOSSIL_' then
if u.ping '_FOSSIL_' then default_os = 'windows' end
|
Modified math.t from [573a13128c] to [bc01716315].
1
2
3
4
5
6
7
8
9
10
11
...
182
183
184
185
186
187
188
189
190
|
-- vim: ft=terra
local m = {
shorthand = {maxlen = 14}
}
-- swap in place -- faster on little endian
m.netswap_ip = macro(function(ty, src, dest)
if ty:astype().type ~= 'integer' then error('bad type') end
local bytes = ty:astype().bytes
src = `[&uint8](src)
dest = `[&uint8](dest)
................................................................................
else dgtct = dgtct + 1 end
end else
buf = buf - 1
@buf = 0x30
end
return buf
end
return m
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
...
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
|
-- vim: ft=terra
local m = {
shorthand = {maxlen = 14}
}
local pstring = lib.mem.ptr(int8)
-- swap in place -- faster on little endian
m.netswap_ip = macro(function(ty, src, dest)
if ty:astype().type ~= 'integer' then error('bad type') end
local bytes = ty:astype().bytes
src = `[&uint8](src)
dest = `[&uint8](dest)
................................................................................
else dgtct = dgtct + 1 end
end else
buf = buf - 1
@buf = 0x30
end
return buf
end
terra m.ndigits(n: intptr, base: intptr): intptr
var c = base
var i = 1
while true do
if n < c then return i end
c = c * base
i = i + 1
end
end
terra m.fsz_parse(f: pstring): {intptr, bool}
-- take a string representing a file size and return {nbytes, true}
-- or {0, false} if the parse fails
if f.ct == 0 then f.ct = lib.str.sz(f.ptr) end
var sz: intptr = 0
for i = 0, f.ct do
if f(i) == @',' then goto skip end
if f(i) >= 0x30 and f(i) <= 0x39 then
sz = sz * 10
sz = sz + f(i) - 0x30
else
if i+1 == f.ct or f(i) == 0 then return sz, true end
if i+2 == f.ct or f(i+1) == 0 then
if f(i) == @'b' then return sz/8, true end -- bits
else
var s: intptr = 0
if i+3 == f.ct or f(i+2) == 0 then
s = i + 1
elseif (i+4 == f.ct or f(i+3) == 0) and f(i+1) == @'i' then
-- grudgingly tolerate ~mebibits~ and its ilk, without
-- affecting the result in any way
s = i + 2
else return 0, false end
if f(s) == @'b' then sz = sz/8 -- bits
elseif f(s) ~= @'B' then return 0, false end -- wth
end
var c = f(i)
if c >= @'A' and c <= @'Z' then c = c - 0x20 end
switch c do -- normal char literal syntax doesn't work here, leads to llvm error (!!)
case [uint8]([string.byte('k')]) then return sz * [1024ULL ^ 1], true end
case [uint8]([string.byte('m')]) then return sz * [1024ULL ^ 2], true end
case [uint8]([string.byte('g')]) then return sz * [1024ULL ^ 3], true end
case [uint8]([string.byte('t')]) then return sz * [1024ULL ^ 4], true end
case [uint8]([string.byte('e')]) then return sz * [1024ULL ^ 5], true end
case [uint8]([string.byte('y')]) then return sz * [1024ULL ^ 6], true end
else return sz, true
end
end
::skip::end
return sz, true
end
return m
|
Modified mgtool.t from [03b0c72484] to [faf7450a82].
297 298 299 300 301 302 303 304 305 306 307 308 309 310 ... 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 ... 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
if dbmode.arglist.ct == 1 then
lib.bail('you are attempting to completely obliterate all data! make sure you have selected your target correctly. if you really want to do this, pass the confirmation string ', &cfmstr[0])
elseif dbmode.arglist.ct == 2 then
if lib.str.cmp(dbmode.arglist(1), cfmstr) == 0 then
lib.warn('completely obliterating all data!')
dlg:obliterate_everything()
else
lib.bail('you passed an incorrect confirmation string; pass ', &cfmstr[0], ' if you really want to destroy everything')
end
else goto cmderr end
else goto cmderr end
elseif lib.str.cmp(mode.arglist(0),'be') == 0 then
srv:setup(cnf)
................................................................................
if cfmode.arglist.ct < 1 then goto cmderr end
if cfmode.arglist.ct == 1 then
if lib.str.cmp(cfmode.arglist(0),'chsec') == 0 then
var sec: int8[65] gensec(&sec[0])
dlg:conf_set('server-secret', &sec[0])
lib.report('server secret reset')
-- FIXME notify server to reload its config
elseif lib.str.cmp(cfmode.arglist(0),'refresh') == 0 then
-- TODO notify server to reload config
else goto cmderr end
elseif cfmode.arglist.ct == 3 and
lib.str.cmp(cfmode.arglist(0),'set') == 0 then
dlg:conf_set(cfmode.arglist(1),cfmode.arglist(2))
lib.report('parameter set')
else goto cmderr end
else
srv:setup(cnf)
srv:conprep(lib.store.prepmode.full)
if lib.str.cmp(mode.arglist(0),'mkroot') == 0 then
var cfmode: pbasic cfmode:parse(mode.arglist.ct, &mode.arglist(0))
if cfmode.help then
[ lib.emit(false, 1, 'usage: ', `argv[0], ' mkroot ', cfmode.type.helptxt.flags, ' <handle>', cfmode.type.helptxt.opts) ]
................................................................................
var root = lib.store.actor.mk(&kbuf[0])
root.handle = cfmode.arglist(0)
var epithets = array(
'root', 'god', 'regional jehovah', 'titan king',
'king of olympus', 'cyberpharaoh', 'electric ellimist',
"rampaging c'tan", 'deathless tweetlord', 'postmaster',
'faerie queene', 'lord of the posts', 'ruthless cybercrat',
'general secretary', 'commissar', 'kwisatz haderach'
-- feel free to add more
)
root.epithet = epithets[lib.crypt.random(intptr,0,[epithets.type.N])]
root.rights.powers:fill() -- grant omnipotence
root.rights.rank = 1
var ruid = dlg:actor_create(&root)
dlg:conf_set('master',root.handle)
|
> > < < > > > > > > | > |
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 ... 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
if dbmode.arglist.ct == 1 then
lib.bail('you are attempting to completely obliterate all data! make sure you have selected your target correctly. if you really want to do this, pass the confirmation string ', &cfmstr[0])
elseif dbmode.arglist.ct == 2 then
if lib.str.cmp(dbmode.arglist(1), cfmstr) == 0 then
lib.warn('completely obliterating all data!')
dlg:obliterate_everything()
elseif lib.str.cmp(dbmode.arglist(1), 'print-confirmation-string') == 0 then
lib.io.send(1, cfmstr, lib.str.sz(cfmstr))
else
lib.bail('you passed an incorrect confirmation string; pass ', &cfmstr[0], ' if you really want to destroy everything')
end
else goto cmderr end
else goto cmderr end
elseif lib.str.cmp(mode.arglist(0),'be') == 0 then
srv:setup(cnf)
................................................................................
if cfmode.arglist.ct < 1 then goto cmderr end
if cfmode.arglist.ct == 1 then
if lib.str.cmp(cfmode.arglist(0),'chsec') == 0 then
var sec: int8[65] gensec(&sec[0])
dlg:conf_set('server-secret', &sec[0])
lib.report('server secret reset')
elseif lib.str.cmp(cfmode.arglist(0),'refresh') == 0 then
cfmode.no_notify = false -- duh
else goto cmderr end
elseif cfmode.arglist.ct == 3 and
lib.str.cmp(cfmode.arglist(0),'set') == 0 then
dlg:conf_set(cfmode.arglist(1),cfmode.arglist(2))
lib.report('parameter set')
else goto cmderr end
-- successful commands fall through
if not cfmode.no_notify then
dlg:ipc_send(lib.ipc.cmd.cfgrefresh,0)
end
else
srv:setup(cnf)
srv:conprep(lib.store.prepmode.full)
if lib.str.cmp(mode.arglist(0),'mkroot') == 0 then
var cfmode: pbasic cfmode:parse(mode.arglist.ct, &mode.arglist(0))
if cfmode.help then
[ lib.emit(false, 1, 'usage: ', `argv[0], ' mkroot ', cfmode.type.helptxt.flags, ' <handle>', cfmode.type.helptxt.opts) ]
................................................................................
var root = lib.store.actor.mk(&kbuf[0])
root.handle = cfmode.arglist(0)
var epithets = array(
'root', 'god', 'regional jehovah', 'titan king',
'king of olympus', 'cyberpharaoh', 'electric ellimist',
"rampaging c'tan", 'deathless tweetlord', 'postmaster',
'faerie queene', 'lord of the posts', 'ruthless cybercrat',
'general secretary', 'commissar', 'kwisatz haderach',
'dedicated hyperturing'
-- feel free to add more
)
root.epithet = epithets[lib.crypt.random(intptr,0,[epithets.type.N])]
root.rights.powers:fill() -- grant omnipotence
root.rights.rank = 1
var ruid = dlg:actor_create(&root)
dlg:conf_set('master',root.handle)
|
Modified parsav.t from [dc97a3f269] to [022b1bf037].
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 ... 273 274 275 276 277 278 279 280 281 282 283 284 285 286 ... 292 293 294 295 296 297 298 299 300 301 302 303 304 305 ... 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 ... 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
var [val]
[exp]
in val end
return q
end);
proc = {
fork = terralib.externfunction('fork', {} -> int);
daemonize = terralib.externfunction('daemon', {int,int} -> {});
exit = terralib.externfunction('exit', int -> {});
getenv = terralib.externfunction('getenv', rawstring -> rawstring);
exec = terralib.externfunction('execv', {rawstring,&rawstring} -> int);
execp = terralib.externfunction('execvp', {rawstring,&rawstring} -> int);
};
io = {
send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
recv = terralib.externfunction('read', {int, rawstring, intptr} -> ptrdiff);
close = terralib.externfunction('close', {int} -> int);
say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
fmt = terralib.externfunction('printf',
terralib.types.funcpointer({rawstring},{int},true));
};
str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };
copy = function(tbl)
local new = {}
for k,v in pairs(tbl) do new[k] = v end
setmetatable(new, getmetatable(tbl))
return new
................................................................................
then lib.io.say([' - ' .. v .. ': true\n'])
else lib.io.say([' - ' .. v .. ': false\n'])
end
end
end
return q
end)
set.metamethods.__add = macro(function(self,other)
local new = symbol(set)
local q = quote var [new] new:clear() end
for i = 0, bytes - 1 do
q = quote [q]
new._store[i] = self._store[i] or other._store[i]
end
................................................................................
local q = quote var [new] new:clear() end
for i = 0, bytes - 1 do
q = quote [q]
new._store[i] = self._store[i] and other._store[i]
end
end
return quote [q] in new end
end)
set.metamethods.__not = macro(function(self)
local new = symbol(set)
local q = quote var [new] new:clear() end
for i = 0, bytes - 1 do
q = quote [q]
new._store[i] = not self._store[i]
................................................................................
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.load {
'mem', 'math', 'str', 'file', 'crypt', 'ipc';
'http', 'html', 'session', 'tpl', 'store';
'smackdown'; -- md-alike parser
}
local be = {}
for _, b in pairs(config.backends) do
be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
................................................................................
lib.load {
'srv';
'render:nav';
'render:nym';
'render:login';
'render:profile';
'render:compose';
'render:tweet';
'render:userpage';
'render:timeline';
'render:docpage';
'render:conf:profile';
'render:conf';
'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
|
< > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | > |
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 ... 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 ... 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 ... 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 ... 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
var [val]
[exp]
in val end
return q
end);
proc = {
fork = terralib.externfunction('fork', {} -> int);
exit = terralib.externfunction('exit', int -> {});
getenv = terralib.externfunction('getenv', rawstring -> rawstring);
exec = terralib.externfunction('execv', {rawstring,&rawstring} -> int);
execp = terralib.externfunction('execvp', {rawstring,&rawstring} -> int);
};
io = {
send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
recv = terralib.externfunction('read', {int, rawstring, intptr} -> ptrdiff);
close = terralib.externfunction('close', {int} -> int);
say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
fmt = terralib.externfunction('printf',
terralib.types.funcpointer({rawstring},{int},true));
ttyp = terralib.externfunction('isatty', int -> int);
};
str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };
copy = function(tbl)
local new = {}
for k,v in pairs(tbl) do new[k] = v end
setmetatable(new, getmetatable(tbl))
return new
................................................................................
then lib.io.say([' - ' .. v .. ': true\n'])
else lib.io.say([' - ' .. v .. ': false\n'])
end
end
end
return q
end)
terra set:setbit(i: intptr, val: bool)
if val then
self._store[i/8] = self._store[i/8] or (1 << (i % 8))
else
self._store[i/8] = self._store[i/8] and not (1 << (i % 8))
end
end
set.bits = {}
set.idvmap = {}
for i,v in ipairs(tbl) do
set.idvmap[v] = i
set.bits[v] = quote var b: set b:clear() b:setbit(i, true) in b end
end
set.metamethods.__add = macro(function(self,other)
local new = symbol(set)
local q = quote var [new] new:clear() end
for i = 0, bytes - 1 do
q = quote [q]
new._store[i] = self._store[i] or other._store[i]
end
................................................................................
local q = quote var [new] new:clear() end
for i = 0, bytes - 1 do
q = quote [q]
new._store[i] = self._store[i] and other._store[i]
end
end
return quote [q] in new end
end)
set.metamethods.__eq = macro(function(self,other)
local rt = symbol(bool)
local fb if #tbl % 8 == 0 then fb = bytes - 1 else fb = bytes - 2 end
local q = quote rt = true end
for i = 0, fb do
q = quote
if self._store[i] ~= other._store[i] then rt = false else [q] end
end
end
-- we need to mask out any extraneous bits the values might have, as we
-- don't want the kind of noise introduced by :fill() to affect comparison
if #tbl % 8 ~= 0 then
local last = #tbl-1
local msk = (2 ^ (#tbl % 8)) - 1
q = quote
if (self._store [last] and [uint8](msk)) ~=
(other._store[last] and [uint8](msk)) then rt = false else [q] end
end
end
return quote var [rt]; [q] in rt end
end)
set.metamethods.__not = macro(function(self)
local new = symbol(set)
local q = quote var [new] new:clear() end
for i = 0, bytes - 1 do
q = quote [q]
new._store[i] = not self._store[i]
................................................................................
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.load {
'mem', 'math', 'str', 'file', 'crypt', 'ipc';
'http', 'html', 'session', 'tpl', 'store', 'acl';
'smackdown'; -- md-alike parser
}
local be = {}
for _, b in pairs(config.backends) do
be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
................................................................................
lib.load {
'srv';
'render:nav';
'render:nym';
'render:login';
'render:profile';
'render:compose';
'render:tweet';
'render:tweet-page';
'render:user-page';
'render:timeline';
'render:docpage';
'render:conf:profile';
'render:conf:sec';
'render:conf';
'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
|
Modified render/compose.t from [cb3a66bab9] to [13509724e6].
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 |
-- 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;
circles = ''; -- TODO: list user's circles, rooms, and saved aclexps
}
end
var cotxt = form:tostr() defer cotxt:free()
var doc = data.view.docskel {
instance = co.srv.cfg.instance;
title = lib.str.plit 'compose';
body = cotxt;
class = lib.str.plit 'compose';
navlinks = co.navbar;
}
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
|
| < | < < | | | > > > > > > > > < < > | | < < < |
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 |
-- vim: ft=terra local terra render_compose(co: &lib.srv.convo, edit: &lib.store.post, acc: &lib.str.acc) var target, tgtlen = co:getv('to') var form: data.view.compose form = data.view.compose { handle = co.who.handle; circles = ''; -- TODO: list user's circles, rooms, and saved aclexps } if edit == nil then form.content = lib.coalesce(target, '') form.acl = lib.trn(target == nil, 'all', 'mentioned') -- TODO default acl setting? else form.content = lib.coalesce(edit.body, '') form.acl = edit.acl end if acc ~= nil then form:append(acc) return end var cotxt = form:tostr() defer cotxt:free() var doc = [lib.srv.convo.page] { title = lib.str.plit 'compose'; body = cotxt; class = lib.str.plit 'compose'; cache = true; } co:stdpage(doc) end return render_compose |
Modified render/conf/profile.t from [248ab207d4] to [7f970c2f4a].
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_conf_profile(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
var c = data.view.conf_profile {
handle = cs(co.who.handle);
nym = cs(lib.coalesce(co.who.nym,''));
bio = cs(lib.coalesce(co.who.bio,''));
}
return c:tostr()
end
return render_conf_profile
|
< |
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_conf_profile(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
var c = data.view.conf_profile {
handle = cs(co.who.handle);
nym = cs(lib.coalesce(co.who.nym,''));
bio = cs(lib.coalesce(co.who.bio,''));
}
return c:tostr()
end
return render_conf_profile
|
Added render/conf/sec.t version [2ed8642241].
> > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
-- vim: ft=terra local pstr = lib.mem.ptr(int8) local pref = lib.mem.ref(int8) local terra render_conf_sec(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr var time: lib.store.timepoint = co.who.source:auth_sigtime_user_fetch(co.who.id) var tstr: int8[26] lib.osclock.ctime_r(&time, &tstr[0]) var body = data.view.conf_sec { lastreset = pstr { ptr = &tstr[0], ct = lib.str.sz(&tstr[0]) } } if co.srv.cfg.credmgd then var a: lib.str.acc a:init(768) body:append(&a) var credmgr = data.view.conf_sec_credmg { credlist = '<option>your password</option>' } credmgr:append(&a) return a:finalize() else return body:tostr() end end return render_conf_sec |
Modified render/docpage.t from [3eda6110a6] to [148acf7303].
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
local terra
pushbranches(list: &lib.str.acc, idx: intptr, ps: lib.store.powerset): {}
var [pages] = array([allpages])
var started = false
for i=0,[pages.type.N] do
if pages[i].parent == idx+1 and (pages[i].priv:sz() == 0 or
(ps and pages[i].priv):sz() > 0) then
if not started then
started = true
list:lpush('<ul>')
end
list:lpush('<li><a href="/doc/'):rpush(pages[i].name):lpush('">')
:rpush(pages[i].title):lpush('</a>')
pushbranches(list, i, ps)
|
| |
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
local terra
pushbranches(list: &lib.str.acc, idx: intptr, ps: lib.store.powerset): {}
var [pages] = array([allpages])
var started = false
for i=0,[pages.type.N] do
if pages[i].parent == idx+1 and (pages[i].priv:sz() == 0 or
(ps and pages[i].priv) == pages[i].priv) then
if not started then
started = true
list:lpush('<ul>')
end
list:lpush('<li><a href="/doc/'):rpush(pages[i].name):lpush('">')
:rpush(pages[i].title):lpush('</a>')
pushbranches(list, i, ps)
|
Modified render/profile.t from [19457b4b7c] to [ae13f6f2b7].
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_profile(co: &lib.srv.convo, actor: &lib.store.actor)
var aux: lib.str.acc
if co.aid ~= 0 and co.who.id == actor.id then
aux:compose('<a href="/conf/profile?go=/',actor.xid,'">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:lpush('<a href="/'):push(actor.xid,0):lpush('/ctl">control</a>')
end
else
aux:compose('<a href="/', actor.xid, '/follow">remote follow</a>')
end
var auxp = aux:finalize()
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])
|
> | < | > > > > > | | |
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 |
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_profile(co: &lib.srv.convo, actor: &lib.store.actor)
var aux: lib.str.acc
var followed = true -- FIXME
if co.aid ~= 0 and co.who.id == actor.id then
aux:compose('<a class="button" href="/conf/profile?go=/',actor.xid,'">alter</a>')
elseif co.aid ~= 0 then
if not followed then
aux:compose('<button method="post" name="act" value="follow">follow</a>')
elseif not followed then
aux:compose('<button method="post" name="act" value="unfollow">unfollow</a>')
end
aux:lpush('<a href="/'):push(actor.xid,0):lpush('/chat">chat</a>')
if co.who.rights.powers:affect_users() then
aux:lpush('<a class="button" href="/'):push(actor.xid,0):lpush('/ctl">control</a>')
end
else
aux:compose('<a class="button" href="/', actor.xid, '/follow">remote follow</a>')
end
var auxp = aux:finalize()
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])
|
Added render/tweet-page.t version [ad592d16bd].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local pstr = lib.mem.ptr(int8) local pref = lib.mem.ref(int8) local terra cs(s: rawstring) return pstr { ptr = s, ct = lib.str.sz(s) } end local terra render_tweet_page( co: &lib.srv.convo, path: lib.mem.ptr(pref), p: &lib.store.post ): {} var pg: lib.str.acc pg:init(256) lib.render.tweet(co, p, &pg) pg:lpush('<form class="action-bar" method="post">') if co.aid ~= 0 then var liked = false -- FIXME var rtd = false if not liked then pg:lpush('<button class="pos" name="act" value="like">like</button>') else pg:lpush('<button class="neg" name="act" value="dislike">dislike</button>') end if not rtd then pg:lpush('<button class="pos" name="act" value="rt">retweet</button>') else pg:lpush('<button class="neg" name="act" value="unrt">detweet</button>') end if p.author == co.who.id then pg:lpush('<a class="button" href="/post/'):rpush(path(1)):lpush('/edit">edit</a><a class="neg button" href="/post/'):rpush(path(1)):lpush('/del">delete</a>') end -- TODO list user's chosen reaction emoji pg:lpush('</form>') if co.who.rights.powers.post() then lib.render.compose(co, nil, &pg) end end var ppg = pg:finalize() defer ppg:free() co:stdpage([lib.srv.convo.page] { title = lib.str.plit 'post'; cache = false; class = lib.str.plit 'post'; body = ppg; }) -- TODO display conversation -- perhaps display descendant nodes here, and have a link to the top of the whole tree? end return render_tweet_page |
Name change from render/userpage.t to render/user-page.t.
Modified route.t from [ae96c17fe6] to [2469fad253].
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 .. 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 .. 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 ... 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 ... 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 ... 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 ... 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 ... 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 |
var handle = [lib.mem.ptr(int8)] { ptr = &uri.ptr[2], ct = 0 }
for i=2,uri.ct do
if uri.ptr[i] == @'/' or uri.ptr[i] == 0 then handle.ct = i - 2 break end
end
if handle.ct == 0 then
handle.ct = uri.ct - 2
uri:advance(uri.ct)
else
if handle.ct + 2 < uri.ct then
uri:advance(handle.ct + 2)
--uri.ptr = uri.ptr + (handle.ct + 2)
--uri.ct = uri.ct - (handle.ct + 2)
end
end
lib.dbg('looking up user by xid "', {handle.ptr,handle.ct} ,'", path: ', {uri.ptr,uri.ct})
var path = lib.http.hier(uri) defer path:free()
for i=0,path.ct do
lib.dbg('got path component ', {path.ptr[i].ptr, path.ptr[i].ct})
end
................................................................................
var actor = co.srv:actor_fetch_xid(handle)
if actor.ptr == nil then
co:complain(404,'no such user','no such user known to this server')
return
end
defer actor:free()
lib.render.userpage(co, actor.ptr)
end
terra http.actor_profile_uid(co: &lib.srv.convo, path: lib.mem.ptr(lib.mem.ref(int8)), meth: method.t)
if path.ct < 2 then
co:complain(404,'bad url','invalid user url')
return
end
var uid, ok = lib.math.shorthand.parse(path.ptr[1].ptr, path.ptr[1].ct)
if not ok then
................................................................................
var actor = co.srv:actor_fetch_uid(uid)
if actor.ptr == nil then
co:complain(404, 'no such user', 'no user by that ID is known to this instance')
return
end
defer actor:free()
lib.render.userpage(co, actor.ptr)
end
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, lib.str.plit(nil))
elseif meth == method.post then
................................................................................
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
-- error out
if aid == 0 then
lib.render.login(co, nil, nil, lib.str.plit '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],15})
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
................................................................................
terra http.post_compose(co: &lib.srv.convo, meth: method.t)
if not co:assertpow('post') then return end
--if co.who.rights.powers.post() == false then
--co:complain(403,'insufficient privileges','you lack the <strong>post</strong> power and cannot perform this action')
if meth == method.get then
lib.render.compose(co, nil)
elseif meth == method.post then
var text, textlen = co:postv("post")
var acl, acllen = co:postv("acl")
var subj, subjlen = co:postv("subject")
if text == nil or acl == nil then
co:complain(405, 'invalid post', 'every post must have at least body text and an ACL')
return
................................................................................
lib.render.docpage(co,path(1))
elseif path.ct == 1 then
lib.render.docpage(co, rstring.null())
else
co:complain(404, 'no such documentation', 'invalid documentation URL')
end
end
terra http.configure(co: &lib.srv.convo, path: hpath, meth: method.t)
var msg = pstring.null()
if meth == method.post and path.ct >= 1 then
var user_refresh = false var fail = false
if path(1):cmp(lib.str.lit 'profile') then
co.who.bio = co:postv('bio')._0
co.who.nym = co:postv('nym')._0
if co.who.bio ~= nil and @co.who.bio == 0 then co.who.bio = nil end
if co.who.nym ~= nil and @co.who.nym == 0 then co.who.nym = nil end
co.who.source:actor_save(co.who)
msg = lib.str.plit 'profile changes saved'
--user_refresh = true -- not really necessary here, actually
elseif path(1):cmp(lib.str.lit 'srv') then
elseif path(1):cmp(lib.str.lit 'users') then
end
if user_refresh then -- refresh the user info for the renderer
var usr = co.srv:actor_fetch_uid(co.who.id)
lib.mem.heapf(co.who)
co.who = usr.ptr
end
................................................................................
ct = storage[([i-1])].ct;
}
goto [send]
end
end
end
terra http.static_content(co: &lib.srv.convo, [filename], [flen])
var hdrs = array(lib.http.header{'Content-Type',nil})
var [page] = lib.http.page {
respcode = 200;
headers = [lib.mem.ptr(lib.http.header)] {
ptr = &hdrs[0], ct = 1
}
}
[branches]
................................................................................
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)
elseif path.ptr[0]:cmp(lib.str.lit('tl')) then
http.timeline(co, path)
elseif path.ptr[0]:cmp(lib.str.lit('doc')) then
if meth ~= method.get and meth ~= method.head then goto wrongmeth end
http.documentation(co, path)
elseif path.ptr[0]:cmp(lib.str.lit('conf')) then
if co.aid == 0 then goto unauth end
http.configure(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
::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end
end
|
< | < < < < < | > > | > > | < | < | < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > > > > > > > | | > > | | | |
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 .. 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 .. 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 ... 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 ... 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 ... 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 ... 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 ... 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 |
var handle = [lib.mem.ptr(int8)] { ptr = &uri.ptr[2], ct = 0 }
for i=2,uri.ct do
if uri.ptr[i] == @'/' or uri.ptr[i] == 0 then handle.ct = i - 2 break end
end
if handle.ct == 0 then
handle.ct = uri.ct - 2
uri:advance(uri.ct)
elseif handle.ct + 2 < uri.ct then uri:advance(handle.ct + 2) end
lib.dbg('looking up user by xid "', {handle.ptr,handle.ct} ,'", path: ', {uri.ptr,uri.ct})
var path = lib.http.hier(uri) defer path:free()
for i=0,path.ct do
lib.dbg('got path component ', {path.ptr[i].ptr, path.ptr[i].ct})
end
................................................................................
var actor = co.srv:actor_fetch_xid(handle)
if actor.ptr == nil then
co:complain(404,'no such user','no such user known to this server')
return
end
defer actor:free()
lib.render.user_page(co, actor.ptr)
end
terra http.actor_profile_uid (
co: &lib.srv.convo,
path: lib.mem.ptr(lib.mem.ref(int8)),
meth: method.t
)
if path.ct < 2 then
co:complain(404,'bad url','invalid user url')
return
end
var uid, ok = lib.math.shorthand.parse(path.ptr[1].ptr, path.ptr[1].ct)
if not ok then
................................................................................
var actor = co.srv:actor_fetch_uid(uid)
if actor.ptr == nil then
co:complain(404, 'no such user', 'no user by that ID is known to this instance')
return
end
defer actor:free()
lib.render.user_page(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, lib.str.plit(nil))
elseif meth == method.post then
................................................................................
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
-- error out
if aid == 0 then
lib.render.login(co, nil, nil, lib.str.plit 'authentication failure')
else
co:installkey('/',aid)
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
................................................................................
terra http.post_compose(co: &lib.srv.convo, meth: method.t)
if not co:assertpow('post') then return end
--if co.who.rights.powers.post() == false then
--co:complain(403,'insufficient privileges','you lack the <strong>post</strong> power and cannot perform this action')
if meth == method.get then
lib.render.compose(co, nil, nil)
elseif meth == method.post then
var text, textlen = co:postv("post")
var acl, acllen = co:postv("acl")
var subj, subjlen = co:postv("subject")
if text == nil or acl == nil then
co:complain(405, 'invalid post', 'every post must have at least body text and an ACL')
return
................................................................................
lib.render.docpage(co,path(1))
elseif path.ct == 1 then
lib.render.docpage(co, rstring.null())
else
co:complain(404, 'no such documentation', 'invalid documentation URL')
end
end
terra http.tweet_page(co: &lib.srv.convo, path: hpath, meth: method.t)
var pid, ok = lib.math.shorthand.parse(path(1).ptr, path(1).ct)
if not ok then
co:complain(400, 'bad post ID', 'that post ID is not valid')
return
end
var post = co.srv:post_fetch(pid)
if not post then
co:complain(404, 'post not found', 'no such post is known to this server')
return
end
defer post:free()
if path.ct == 3 then
if path(2):cmp(lib.str.lit 'edit') then
if post(0).author ~= co.who.id then
co:complain(403, 'forbidden', 'you cannot edit other people\'s posts')
return
end
if meth == method.get then
lib.render.compose(co, post.ptr, nil)
return
elseif meth == method.post then
var newbody = co:postv('post')._0
var newacl = co:postv('acl')._0
var newsubj = co:postv('subject')._0
if newbody ~= nil then post(0).body = newbody end
if newacl ~= nil then post(0).acl = newacl end
if newsubj ~= nil then post(0).subject = newsubj end
post(0):save(true)
var lnk: lib.str.acc lnk:compose('/post/', path(1))
co:reroute(lnk.buf)
lnk:free()
end
return
else goto badurl end
end
if meth == method.post then
co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful')
return
end
lib.render.tweet_page(co, path, post.ptr)
do return end
::badurl:: co:complain(404, 'invalid URL', 'this URL does not reference extant content or functionality')
end
terra http.configure(co: &lib.srv.convo, path: hpath, meth: method.t)
var msg = pstring.null()
if meth == method.post and path.ct >= 1 then
var user_refresh = false var fail = false
if path(1):cmp(lib.str.lit 'profile') then
lib.dbg('updating profile')
co.who.bio = co:postv('bio')._0
co.who.nym = co:postv('nym')._0
if co.who.bio ~= nil and @co.who.bio == 0 then co.who.bio = nil end
if co.who.nym ~= nil and @co.who.nym == 0 then co.who.nym = nil end
co.who.source:actor_save(co.who)
msg = lib.str.plit 'profile changes saved'
--user_refresh = true -- not really necessary here, actually
elseif path(1):cmp(lib.str.lit 'srv') then
elseif path(1):cmp(lib.str.lit 'users') then
elseif path(1):cmp(lib.str.lit 'sec') then
var act = co:ppostv('act')
if act:cmp(lib.str.plit 'invalidate') then
lib.dbg('setting user\'s cookie validation time to now')
co.who.source:auth_sigtime_user_alter(co.who.id, lib.osclock.time(nil))
-- the current session has been invalidated as well, so we need to immediately install a new authentication cookie with the same aid so the user doesn't need to log back in all over again
co:installkey('/conf/sec',co.aid)
return
end
end
if user_refresh then -- refresh the user info for the renderer
var usr = co.srv:actor_fetch_uid(co.who.id)
lib.mem.heapf(co.who)
co.who = usr.ptr
end
................................................................................
ct = storage[([i-1])].ct;
}
goto [send]
end
end
end
terra http.static_content(co: &lib.srv.convo, [filename], [flen])
var hdrs = array(
lib.http.header{'Content-Type',nil})
var [page] = lib.http.page {
respcode = 200;
headers = [lib.mem.ptr(lib.http.header)] {
ptr = &hdrs[0], ct = 1
}
}
[branches]
................................................................................
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.ct > 1 and path(0):cmp(lib.str.lit('user')) then
http.actor_profile_uid(co, path, meth)
elseif path.ct > 1 and path(0):cmp(lib.str.lit('post')) then
http.tweet_page(co, path, meth)
elseif path(0):cmp(lib.str.lit('tl')) then
http.timeline(co, path)
elseif path(0):cmp(lib.str.lit('doc')) then
if meth ~= method.get and meth ~= method.head then goto wrongmeth end
http.documentation(co, path)
elseif path(0):cmp(lib.str.lit('conf')) then
if co.aid == 0 then goto unauth end
http.configure(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
::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end
end
|
Modified session.t from [e8a79576f0] to [78b2aad470].
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 |
[lib.mem.ptr(uint8)] {ptr = [&uint8](secret.ptr), ct = secret.ct},
[lib.mem.ptr( int8)] {ptr = out, ct = len},
&hash[0])
ptr = ptr + lib.math.shorthand.gen(lib.math.truncate64(hash, [hash.type.N]), ptr)
return ptr - out
end
terra m.cookie_interpret(secret: lib.mem.ptr(int8), c: lib.mem.ptr(int8), now: uint64): uint64 -- returns either 0 or a valid authid
var authid_sz = lib.str.cspan(c.ptr, lib.str.lit '.', c.ct)
if authid_sz == 0 then return 0 end
if authid_sz + 1 > c.ct then return 0 end
var time_sz = lib.str.cspan(c.ptr+authid_sz+1, lib.str.lit '.', c.ct - (authid_sz+1))
if time_sz == 0 then return 0 end
if (authid_sz + time_sz + 2) > c.ct then return 0 end
var hash_sz = c.ct - (authid_sz + time_sz + 2)
var knownhash: uint8[lib.crypt.algsz.sha256]
lib.crypt.hmac(lib.crypt.alg.sha256,
[lib.mem.ptr(uint8)] {ptr = [&uint8](secret.ptr), ct = secret.ct},
[lib.mem.ptr( int8)] {ptr = c.ptr, ct = c.ct - hash_sz},
&knownhash[0])
var authid, authok = lib.math.shorthand.parse(c.ptr, authid_sz)
var time, timeok = lib.math.shorthand.parse(c.ptr + authid_sz + 1, time_sz)
var hash, hashok = lib.math.shorthand.parse(c.ptr + c.ct - hash_sz, hash_sz)
if not (timeok and authok and hashok) then return 0 end
if lib.math.truncate64(knownhash, [knownhash.type.N]) ~= hash then return 0 end
if now - time > m.maxage then return 0 end
return authid
end
return m
|
| | | | | | | | | |
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 |
[lib.mem.ptr(uint8)] {ptr = [&uint8](secret.ptr), ct = secret.ct},
[lib.mem.ptr( int8)] {ptr = out, ct = len},
&hash[0])
ptr = ptr + lib.math.shorthand.gen(lib.math.truncate64(hash, [hash.type.N]), ptr)
return ptr - out
end
terra m.cookie_interpret(secret: lib.mem.ptr(int8), c: lib.mem.ptr(int8), now: uint64) -- returns either 0,0 or a valid {authid, timepoint}
var authid_sz = lib.str.cspan(c.ptr, lib.str.lit '.', c.ct)
if authid_sz == 0 then return 0,0 end
if authid_sz + 1 > c.ct then return 0,0 end
var time_sz = lib.str.cspan(c.ptr+authid_sz+1, lib.str.lit '.', c.ct - (authid_sz+1))
if time_sz == 0 then return 0,0 end
if (authid_sz + time_sz + 2) > c.ct then return 0,0 end
var hash_sz = c.ct - (authid_sz + time_sz + 2)
var knownhash: uint8[lib.crypt.algsz.sha256]
lib.crypt.hmac(lib.crypt.alg.sha256,
[lib.mem.ptr(uint8)] {ptr = [&uint8](secret.ptr), ct = secret.ct},
[lib.mem.ptr( int8)] {ptr = c.ptr, ct = c.ct - hash_sz},
&knownhash[0])
var authid, authok = lib.math.shorthand.parse(c.ptr, authid_sz)
var time, timeok = lib.math.shorthand.parse(c.ptr + authid_sz + 1, time_sz)
var hash, hashok = lib.math.shorthand.parse(c.ptr + c.ct - hash_sz, hash_sz)
if not (timeok and authok and hashok) then return 0,0 end
if lib.math.truncate64(knownhash, [knownhash.type.N]) ~= hash then return 0,0 end
if now - time > m.maxage then return 0,0 end
return authid, time
end
return m
|
Modified srv.t from [4721bcd333] to [9e0d1b7489].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ... 108 109 110 111 112 113 114 115 116 117 118 119 120 121 ... 154 155 156 157 158 159 160 161 162 163 164 165 166 167 ... 232 233 234 235 236 237 238 239 240 241 242 243 244 245 ... 247 248 249 250 251 252 253 254 255 256 257 258 259 260 ... 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 ... 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 ... 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 |
-- vim: ft=terra
local util = lib.util
local secmode = lib.enum { 'public', 'private', 'lockdown', 'isolate' }
local pstring = lib.mem.ptr(int8)
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
id: rawstring
................................................................................
end)
local struct convo {
srv: &srv
con: &lib.net.mg_connection
msg: &lib.net.mg_http_message
aid: uint64 -- 0 if logged out
who: &lib.store.actor -- who we're logged in as, if aid ~= 0
peer: lib.store.inet
reqtype: lib.http.mime.t -- negotiated content type
-- cache
navbar: lib.mem.ptr(int8)
actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
-- private
................................................................................
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' },
lib.http.header { key = 'Cache-Control', value = 'no-store' }
)
................................................................................
var r = self.vbofs
self.vbofs = self.vbofs + o + 1
@(self.vbofs - 1) = 0
var norm = lib.str.normalize([lib.mem.ptr(int8)]{ptr = r, ct = o})
return norm.ptr, norm.ct
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))
................................................................................
var r = self.vbofs
self.vbofs = self.vbofs + o + 1
@(self.vbofs - 1) = 0
var norm = lib.str.normalize([lib.mem.ptr(int8)]{ptr = r, ct = o})
return norm.ptr, norm.ct
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} -> {}
................................................................................
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
co.actorcache.top = 0
co.actorcache.cur = 0
-- first, check for an accept header. if it's there, we need to
-- iterate over the values and pick the highest-priority one
................................................................................
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 ::nocookie::;
end
if co.aid ~= 0 then
var sess, usr = co.srv:actor_session_fetch(co.aid, peer)
if sess.ok == false then co.aid = 0 else
co.who = usr.ptr
co.who.rights.powers = server:actor_powers_fetch(co.who.id)
end
end
var uridec = lib.mem.heapa(int8, msg.uri.len) defer uridec:free()
var urideclen = lib.net.mg_url_decode(msg.uri.ptr, msg.uri.len, uridec.ptr, uridec.ct, 1)
................................................................................
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
|
< < > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | | | | > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 ... 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 ... 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 ... 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 ... 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 ... 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 ... 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 |
-- vim: ft=terra
local util = lib.util
local secmode = lib.enum { 'public', 'private', 'lockdown', 'isolate' }
local pstring = lib.mem.ptr(int8)
local struct srv
local struct cfgcache {
secret: lib.mem.ptr(int8)
pol_sec: secmode.t
pol_reg: bool
credmgd: bool
maxupsz: intptr
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
id: rawstring
................................................................................
end)
local struct convo {
srv: &srv
con: &lib.net.mg_connection
msg: &lib.net.mg_http_message
aid: uint64 -- 0 if logged out
aid_issue: lib.store.timepoint
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)
actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
-- private
................................................................................
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:installkey(dest: rawstring, aid: uint64)
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(self.srv.cfg.secret, aid, lib.osclock.time(nil), p)
lib.dbg('sending cookie ',{&sesskey[0],15})
p = lib.str.ncpy(p, '; Path=/', 9)
end
self:reroute_cookie(dest, &sesskey[0])
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' },
lib.http.header { key = 'Cache-Control', value = 'no-store' }
)
................................................................................
var r = self.vbofs
self.vbofs = self.vbofs + o + 1
@(self.vbofs - 1) = 0
var norm = lib.str.normalize([lib.mem.ptr(int8)]{ptr = r, ct = o})
return norm.ptr, norm.ct
else return nil, 0 end
end
terra convo:ppostv(name: rawstring)
var s,l = self:postv(name)
return pstring { ptr = s, ct = l }
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))
................................................................................
var r = self.vbofs
self.vbofs = self.vbofs + o + 1
@(self.vbofs - 1) = 0
var norm = lib.str.normalize([lib.mem.ptr(int8)]{ptr = r, ct = o})
return norm.ptr, norm.ct
else return nil, 0 end
end
terra convo:pgetv(name: rawstring)
var s,l = self:getv(name)
return pstring { ptr = s, ct = l }
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} -> {}
................................................................................
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, aid_issue = 0, who = nil;
reqtype = lib.http.mime.none;
peer = peer;
} co.varbuf.ptr = nil
co.navbar.ptr = nil
co.actorcache.top = 0
co.actorcache.cur = 0
-- first, check for an accept header. if it's there, we need to
-- iterate over the values and pick the highest-priority one
................................................................................
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, tp = 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 co.aid_issue = tp end
end ::nocookie::;
end
if co.aid ~= 0 then
var sess, usr = co.srv:actor_session_fetch(co.aid, peer, co.aid_issue)
if sess.ok == false then co.aid = 0 co.aid_issue = 0 else
co.who = usr.ptr
co.who.rights.powers = server:actor_powers_fetch(co.who.id)
end
end
var uridec = lib.mem.heapa(int8, msg.uri.len) defer uridec:free()
var urideclen = lib.net.mg_url_decode(msg.uri.ptr, msg.uri.len, uridec.ptr, uridec.ct, 1)
................................................................................
self.sources:free()
end
terra cfgcache:load()
self.instance = self.overlord:conf_get('instance-name')
self.secret = self.overlord:conf_get('server-secret')
do self.pol_reg = false
var sreg = self.overlord:conf_get('policy-self-register')
if sreg:ref() then
if lib.str.cmp(sreg.ptr, 'on') == 0
then self.pol_reg = true
else self.pol_reg = false
end
end
sreg:free() end
do self.credmgd = false
var sreg = self.overlord:conf_get('credential-store')
if sreg:ref() then
if lib.str.cmp(sreg.ptr, 'managed') == 0
then self.credmgd = true
else self.credmgd = false
end
end
sreg:free() end
do self.maxupsz = [1024 * 100] -- 100 kilobyte default
var sreg = self.overlord:conf_get('maximum-artifact-size')
if sreg:ref() then
var sz, ok = lib.math.fsz_parse(sreg)
if ok then self.maxupsz = sz else
lib.warn('invalid configuration value for maximum-artifact-size; keeping default 100K upload limit')
end
end
sreg:free() end
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
|
Added static/query.svg version [eb8b842615].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 422 423 424 425 |
<?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="query.svg" inkscape:export-filename="/home/lexi/dev/parsav/static/warn.png" inkscape:export-xdpi="200" inkscape:export-ydpi="200"> <defs id="defs2"> <linearGradient id="linearGradient1362" inkscape:collect="always"> <stop id="stop1358" offset="0" style="stop-color:#b64bc7;stop-opacity:1" /> <stop id="stop1360" offset="1" style="stop-color:#7932a4;stop-opacity:1" /> </linearGradient> <linearGradient inkscape:collect="always" id="linearGradient973"> <stop style="stop-color:#9b3bd7;stop-opacity:1;" offset="0" id="stop969" /> <stop style="stop-color:#5d267e;stop-opacity:1" offset="1" id="stop971" /> </linearGradient> <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="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" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient2302" id="radialGradient2304" cx="7.2493401" cy="278.65524" fx="7.2493401" fy="278.65524" r="10.573204" gradientTransform="matrix(1.1874875,-1.9679213e-8,1.9699479e-8,1.1887104,-2.3813503,-53.649456)" 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)" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient2302" id="radialGradient2304-8" cx="12.58085" cy="285.25314" fx="12.58085" fy="285.25314" r="10.573204" gradientTransform="matrix(4.4881418,-7.4378129e-8,7.4454725e-8,4.4927638,-17.038663,-1237.2864)" gradientUnits="userSpaceOnUse" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient2302" id="radialGradient960" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.77290068,-0.9015271,0.4066395,0.34862174,-111.85097,215.11933)" cx="23.48217" cy="269.32919" fx="23.48217" fy="269.32919" r="10.573204" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient973" id="linearGradient975" x1="5.8321538" y1="275.4801" x2="21.037828" y2="292.65216" gradientUnits="userSpaceOnUse" gradientTransform="matrix(3.7795276,0,0,3.7795276,0,-1026.5196)" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient973" id="linearGradient1014" gradientUnits="userSpaceOnUse" x1="5.8321538" y1="275.4801" x2="21.037828" y2="292.65216" gradientTransform="translate(-42.333335)" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient2302" id="radialGradient1016" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.1874875,-1.9679213e-8,1.9699479e-8,1.1887104,-44.714686,-53.649456)" cx="7.2493401" cy="278.65524" fx="7.2493401" fy="278.65524" r="10.573204" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient2302" id="radialGradient1018" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.77290068,-0.9015271,0.4066395,0.34862174,-154.18433,215.11933)" cx="23.48217" cy="269.32919" fx="23.48217" fy="269.32919" r="10.573204" /> <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter1222" x="-0.12898994" width="1.2579799" y="-0.05840071" height="1.1168014"> <feGaussianBlur inkscape:collect="always" stdDeviation="0.33911411" id="feGaussianBlur1224" /> </filter> <filter inkscape:collect="always" style="color-interpolation-filters:sRGB" id="filter1352" x="-0.15389959" width="1.3077992" y="-0.077627083" height="1.1552542"> <feGaussianBlur inkscape:collect="always" stdDeviation="0.50074934" id="feGaussianBlur1354" /> </filter> <radialGradient inkscape:collect="always" xlink:href="#linearGradient1362" id="radialGradient1356" cx="6.5823746" cy="278.22546" fx="6.5823746" fy="278.22546" r="9.8748493" gradientTransform="matrix(2.0818304,0,0,2.0818304,-8.516817,-303.27685)" gradientUnits="userSpaceOnUse" /> <meshgradient y="277.31741" x="5.7174305" gradientUnits="userSpaceOnUse" id="meshgradient5802" inkscape:collect="always"> <meshrow id="meshrow5804"> <meshpatch id="meshpatch5806"> <stop path="c 3.85637,-3.85637 10.1088,-3.85637 13.9651,0" style="stop-color:#eccaff;stop-opacity:1" id="stop5808" /> <stop path="c 3.85637,3.85637 3.85637,10.1088 0,13.9651" style="stop-color:#800080;stop-opacity:1" id="stop5810" /> <stop path="c -3.85637,3.85637 -10.1088,3.85637 -13.9651,0" style="stop-color:#ef9aff;stop-opacity:1" id="stop5812" /> <stop path="c -3.85637,-3.85637 -3.85637,-10.1088 0,-13.9651" style="stop-color:#800080;stop-opacity:1" id="stop5814" /> </meshpatch> </meshrow> </meshgradient> </defs> <sodipodi:namedview id="base" pagecolor="#313131" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="3.959798" inkscape:cx="81.587655" inkscape:cy="1.9529273" 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)"> <circle style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:url(#radialGradient2304);stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path910" cx="12.7" cy="284.29999" r="9.8746281" /> <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,290.50847 -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" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccc" /> <g aria-label="?" style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.12330705;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="text877" transform="matrix(2.1597213,0,0,2.1385442,0.73403254,-313.27713)"> <path style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Averia Serif Libre';-inkscape-font-specification:'Averia Serif Libre';vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.12330705;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="m 7.6924933,277.43408 c 0.1165795,0.95131 -0.6562552,1.66432 -1.3319477,2.19936 -0.6322781,0.5146 -0.3926261,1.03197 -0.696752,1.4212 -0.7264746,0.20404 -0.708357,-0.94298 -0.5084803,-1.3771 0.3220556,-0.77195 1.5189026,-1.18082 1.6106521,-2.0731 0.1697248,-0.85219 -1.0638516,-1.33257 -1.6164509,-0.77546 -0.4346018,0.40863 -1.0937506,0.72692 -1.0597346,-0.0803 0.061275,-0.68615 0.5563323,-0.90025 1.144623,-0.96295 0.9913021,-0.15129 2.2140894,0.34092 2.4316458,1.40994 l 0.018295,0.11853 z" id="path884" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" /> </g> <path style="opacity:1;vector-effect:none;fill:url(#meshgradient5802);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="M 12.7,274.42513 A 9.874628,9.874628 0 0 0 2.8251504,284.29998 9.874628,9.874628 0 0 0 12.7,294.17483 9.874628,9.874628 0 0 0 22.574849,284.29998 9.874628,9.874628 0 0 0 12.7,274.42513 Z m 0.160197,2.02055 c 1.946164,0.016 4.019091,1.07178 4.43022,3.07216 l 0.03979,0.25373 0.01757,0.25632 c 0.251779,2.03442 -1.41752,3.55938 -2.876827,4.70359 -1.365545,1.10049 -0.847991,2.2067 -1.504818,3.03909 -1.568983,0.43635 -1.529802,-2.01666 -1.098124,-2.94504 0.69555,-1.65085 3.280704,-2.52514 3.478857,-4.43332 0.366559,-1.82245 -2.297799,-2.8497 -3.491259,-1.6583 -0.938619,0.87387 -2.3622147,1.55471 -2.2887497,-0.17156 0.132337,-1.46737 1.2016567,-1.92522 2.4722007,-2.05931 0.267617,-0.0404 0.543115,-0.0596 0.821139,-0.0574 z m -0.422714,13.16922 h 0.525033 l 0.893485,0.89349 v 0.52555 l -0.893485,0.89348 -0.525033,-5.2e-4 -0.893485,-0.89348 v -0.52503 z" id="circle967" inkscape:connector-curvature="0" /> <circle r="9.8746281" cy="284.29999" cx="12.7" id="circle958" style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:url(#radialGradient960);stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <g aria-label="?" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.11434174px;line-height:1.25;font-family:'Averia Serif Libre';-inkscape-font-specification:'Averia Serif Libre';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458335" id="text979" /> <circle r="9.8746281" cy="284.29999" cx="-29.633333" id="circle998" style="opacity:1;vector-effect:none;fill:url(#linearGradient1014);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <g transform="translate(-42.333335,-0.90874193)" id="g1006"> <path id="path1000" 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" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccc" /> <g aria-label="?" style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.12330705;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="g1004" transform="matrix(2.1597213,0,0,2.1385442,0.73403254,-312.36839)"> <path style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Averia Serif Libre';-inkscape-font-specification:'Averia Serif Libre';vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.12330705;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="m 7.6924933,277.43408 c 0.1165795,0.95131 -0.6562552,1.66432 -1.3319477,2.19936 -0.6322781,0.5146 -0.3926261,1.03197 -0.696752,1.4212 -0.7264746,0.20404 -0.708357,-0.94298 -0.5084803,-1.3771 0.3220556,-0.77195 1.5189026,-1.18082 1.6106521,-2.0731 0.1697248,-0.85219 -1.0638516,-1.33257 -1.6164509,-0.77546 -0.4346018,0.40863 -1.0937506,0.72692 -1.0597346,-0.0803 0.061275,-0.68615 0.5563323,-0.90025 1.144623,-0.96295 0.9913021,-0.15129 2.2140894,0.34092 2.4316458,1.40994 l 0.018295,0.11853 z" id="path1002" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" /> </g> </g> <circle r="9.8746281" cy="284.29999" cx="-29.633333" id="circle1008" style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:url(#radialGradient1016);stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> <circle style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:url(#radialGradient1018);stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="circle1010" cx="-29.633333" cy="284.29999" r="9.8746281" /> <g id="g1012" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:31.11434174px;line-height:1.25;font-family:'Averia Serif Libre';-inkscape-font-specification:'Averia Serif Libre';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458335" aria-label="?" transform="translate(-42.333335)" /> <g transform="matrix(2.1597213,0,0,2.1385442,0.73403254,-313.27713)" id="g1024" style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.12330705;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" aria-label="?" /> <g transform="translate(-3.3333335e-8,-0.90874193)" id="g1039" style="opacity:0.64600004;filter:url(#filter1352)"> <path id="path1033" 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" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccc" /> <g aria-label="?" style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.12330705;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="g1037" transform="matrix(2.1597213,0,0,2.1385442,0.73403254,-312.36839)"> <path style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Averia Serif Libre';-inkscape-font-specification:'Averia Serif Libre';vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.12330705;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="m 7.6924933,277.43408 c 0.1165795,0.95131 -0.6562552,1.66432 -1.3319477,2.19936 -0.6322781,0.5146 -0.3926261,1.03197 -0.696752,1.4212 -0.7264746,0.20404 -0.708357,-0.94298 -0.5084803,-1.3771 0.3220556,-0.77195 1.5189026,-1.18082 1.6106521,-2.0731 0.1697248,-0.85219 -1.0638516,-1.33257 -1.6164509,-0.77546 -0.4346018,0.40863 -1.0937506,0.72692 -1.0597346,-0.0803 0.061275,-0.68615 0.5563323,-0.90025 1.144623,-0.96295 0.9913021,-0.15129 2.2140894,0.34092 2.4316458,1.40994 l 0.018295,0.11853 z" id="path1035" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" /> </g> </g> <path d="m 12.150391,277.24219 a 0.748792,0.748792 0 0 1 -0.0332,0.004 c -0.568389,0.06 -1.027224,0.19774 -1.306641,0.39649 -0.277209,0.19717 -0.442118,0.42727 -0.494141,0.97851 -7.25e-4,0.16027 0.0015,0.16112 0.01172,0.19922 0.194129,-0.0613 0.650675,-0.29317 1.017578,-0.63476 l -0.01953,0.0195 c 0.869948,-0.86845 2.158273,-0.89534 3.158203,-0.45899 0.990823,0.43239 1.8253,1.49756 1.589844,2.76368 -0.148683,1.24943 -1.014306,2.08321 -1.78711,2.75976 -0.782208,0.68479 -1.493175,1.28446 -1.730468,1.84766 a 0.748792,0.748792 0 0 1 -0.0098,0.0254 c -0.113045,0.24312 -0.238287,1.0468 -0.144531,1.52344 0.02878,0.14631 0.0631,0.17601 0.09961,0.24218 0.06761,-0.20148 0.132148,-0.34938 0.222656,-0.73047 0.140154,-0.5901 0.460972,-1.37295 1.275391,-2.02929 a 0.748792,0.748792 0 0 1 0.0078,-0.008 c 0.71169,-0.55801 1.441989,-1.19038 1.9375,-1.86914 0.495512,-0.67875 0.758472,-1.35793 0.660157,-2.15234 a 0.748792,0.748792 0 0 1 -0.0039,-0.041 l -0.01562,-0.2246 -0.0293,-0.19141 c -0.185302,-0.89101 -0.77691,-1.53674 -1.607422,-1.96484 -0.83249,-0.42913 -1.892004,-0.59212 -2.798828,-0.45508 z m 0.548828,13.16797 -0.359375,0.36132 0.359375,0.35938 0.361328,-0.35938 z" id="path1212" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Averia Serif Libre';-inkscape-font-specification:'Averia Serif Libre';letter-spacing:0px;word-spacing:0px;vector-effect:none;fill:#fa93ff;fill-opacity:1;stroke:none;stroke-width:0.26500002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter1222)" inkscape:original="M 12.039062 276.50195 C 10.768518 276.63604 9.6987432 277.09514 9.5664062 278.5625 C 9.4929412 280.28878 10.91685 279.60825 11.855469 278.73438 C 13.048929 277.54298 15.712261 278.57013 15.345703 280.39258 C 15.14755 282.30076 12.562739 283.17532 11.867188 284.82617 C 11.43551 285.75456 11.397815 288.20783 12.966797 287.77148 C 13.623624 286.93909 13.105159 285.83097 14.470703 284.73047 C 15.930011 283.58626 17.599435 282.06176 17.347656 280.02734 L 17.330078 279.77148 L 17.291016 279.51758 C 16.821155 277.23144 14.179998 276.17841 12.039062 276.50195 z M 12.4375 289.61523 L 11.544922 290.50781 L 11.542969 291.0332 L 12.4375 291.92773 L 12.962891 291.92773 L 13.855469 291.0332 L 13.855469 290.50781 L 12.962891 289.61523 L 12.4375 289.61523 z " inkscape:radius="-0.74871713" sodipodi:type="inkscape:offset" /> <path sodipodi:type="inkscape:offset" inkscape:radius="-0.16717836" inkscape:original="M -14.34375 277.00195 C -14.35155 277.00395 -14.359347 277.00486 -14.367188 277.00586 C -14.957202 277.06816 -15.456819 277.209 -15.789062 277.44531 C -16.11815 277.67939 -16.326757 277.98861 -16.384766 278.59375 C -16.396596 278.94282 -16.323264 279.09764 -16.308594 279.11523 C -16.293644 279.13313 -16.320444 279.13779 -16.197266 279.12109 C -15.950904 279.08739 -15.37302 278.75785 -14.949219 278.36328 L -14.960938 278.375 C -14.179326 277.59474 -12.995641 277.56495 -12.070312 277.96875 C -11.152622 278.36922 -10.401691 279.31598 -10.617188 280.46484 C -10.74706 281.62372 -11.562681 282.41436 -12.332031 283.08789 C -13.106385 283.7658 -13.851986 284.37545 -14.125 285.02344 C -14.1275 285.02844 -14.130113 285.03396 -14.132812 285.03906 C -14.279093 285.35366 -14.401281 286.17218 -14.294922 286.71289 C -14.241742 286.98325 -14.139292 287.17048 -14.054688 287.24414 C -14.009657 287.28334 -13.897079 287.25733 -13.828125 287.26562 C -13.685143 287.00234 -13.611598 286.71247 -13.498047 286.23438 C -13.363381 285.66737 -13.078496 284.95798 -12.306641 284.33594 C -12.304641 284.33494 -12.302781 284.33303 -12.300781 284.33203 C -11.583283 283.76946 -10.83545 283.12505 -10.316406 282.41406 C -9.7973609 281.70308 -9.5060504 280.95629 -9.6132812 280.08984 C -9.6147813 280.08084 -9.6160875 280.0716 -9.6171875 280.0625 L -9.6328125 279.82812 L -9.6640625 279.61914 C -9.8643945 278.64441 -10.518063 277.93724 -11.400391 277.48242 C -12.28272 277.02761 -13.384016 276.85692 -14.34375 277.00195 z " xlink:href="#path1199" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Averia Serif Libre';-inkscape-font-specification:'Averia Serif Libre';letter-spacing:0px;word-spacing:0px;vector-effect:none;fill:#ffe2fb;fill-opacity:1;stroke:none;stroke-width:0.26500002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5733" inkscape:href="#path1199" d="m -13.605469,277.11914 c -0.24345,-0.003 -0.482418,0.014 -0.71289,0.0488 -0.01058,0.002 -0.02313,0.003 -0.02734,0.004 a 0.16719508,0.16719508 0 0 1 -0.0039,0 c -0.57365,0.0606 -1.048495,0.20154 -1.341797,0.41015 -0.297394,0.21154 -0.469187,0.45403 -0.525391,1.01758 -3.1e-5,9.2e-4 3e-5,0.001 0,0.002 -0.0052,0.16054 0.0082,0.27296 0.02344,0.3418 0.07849,-0.0163 0.262351,-0.0861 0.46875,-0.20898 0.218361,-0.13004 0.460956,-0.30687 0.662109,-0.49415 a 0.16719508,0.16719508 0 0 1 0.05273,-0.0312 c 0.0011,-0.001 0.0028,-9.3e-4 0.0039,-0.002 0.838825,-0.77648 2.051057,-0.80558 3.001953,-0.39062 0.975575,0.42573 1.78232,1.4453 1.550781,2.67968 -0.141936,1.22067 -0.998063,2.04142 -1.769531,2.7168 -0.776996,0.68022 -1.502751,1.2928 -1.748047,1.875 a 0.16719508,0.16719508 0 0 1 -0.0039,0.01 c -7.97e-4,0.002 -0.0037,0.007 -0.0059,0.0117 -0.05727,0.12317 -0.127965,0.40183 -0.162109,0.70117 -0.03414,0.29933 -0.03604,0.62635 0.01172,0.86914 0.0462,0.2349 0.143766,0.38461 0.177734,0.41992 0.0013,-2e-5 0.0045,4e-5 0.0059,0 0.109999,-0.22507 0.183634,-0.46863 0.28711,-0.9043 0.139671,-0.58808 0.443488,-1.34184 1.248047,-1.99023 a 0.16719508,0.16719508 0 0 1 0.0078,-0.004 c 0.0021,-0.002 0.0037,-0.004 0.0059,-0.006 a 0.16719508,0.16719508 0 0 1 0.002,-0.002 c 0.71045,-0.55752 1.444993,-1.19161 1.945312,-1.87695 0.5037376,-0.69002 0.7731349,-1.38688 0.6718751,-2.20508 -0.00154,-0.01 -0.00277,-0.0199 -0.00391,-0.0293 a 0.16719508,0.16719508 0 0 1 0,-0.008 l -0.015625,-0.22852 -0.029297,-0.19336 -0.00195,-0.004 c -0.1901449,-0.91782 -0.7983519,-1.58039 -1.6464839,-2.01758 -0.635335,-0.32749 -1.398557,-0.50322 -2.128907,-0.51172 z" transform="translate(26.458334)" /> <path style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Averia Serif Libre';-inkscape-font-specification:'Averia Serif Libre';letter-spacing:0px;word-spacing:0px;vector-effect:none;fill:#ffe2fb;fill-opacity:1;stroke:none;stroke-width:0.26500002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="m -14.343099,277.00195 c -0.0078,0.002 -0.0156,0.003 -0.02344,0.004 -0.590014,0.0623 -1.089633,0.20314 -1.421876,0.43945 -0.329088,0.23408 -0.537694,0.5433 -0.595703,1.14844 -0.01183,0.34907 0.0615,0.50389 0.07617,0.52148 0.01495,0.0179 -0.01185,0.0227 0.111328,0.006 0.246362,-0.0337 0.824247,-0.36324 1.248048,-0.75781 l -0.01172,0.0117 c 0.781612,-0.78026 1.965296,-0.81005 2.890625,-0.40625 0.91769,0.40047 1.668621,1.34723 1.453124,2.49609 -0.129872,1.15888 -0.945493,1.94952 -1.714843,2.62305 -0.774354,0.67791 -1.519955,1.28756 -1.792969,1.93555 -0.0025,0.005 -0.0051,0.0105 -0.0078,0.0156 -0.14628,0.3146 -0.268469,1.13312 -0.16211,1.67383 0.05318,0.27036 0.155631,0.45759 0.240235,0.53125 0.04503,0.0392 0.157608,0.0132 0.226562,0.0215 0.142982,-0.26329 0.216528,-0.55315 0.330079,-1.03124 0.134666,-0.56701 0.419551,-1.2764 1.191406,-1.89844 0.002,-10e-4 0.0039,-0.003 0.0059,-0.004 0.717498,-0.56257 1.46533,-1.20698 1.984374,-1.91797 0.5190453,-0.71098 0.8103562,-1.45777 0.7031253,-2.32422 -0.0015,-0.009 -0.0028,-0.0182 -0.0039,-0.0273 l -0.01563,-0.23438 -0.03125,-0.20898 c -0.200332,-0.97473 -0.8539993,-1.6819 -1.7363273,-2.13672 -0.882329,-0.45481 -1.983626,-0.6255 -2.94336,-0.48047 z" id="path1199" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccscccccccccccccccc" /> <path 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.204228,290.65853 -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" id="path1232" inkscape:connector-curvature="0" /> </g> </svg> |
Modified static/style.scss from [2a06f65525] to [9b25bded91].
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 .. 51 52 53 54 55 56 57 58 59 60 61 62 63 64 ... 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 ... 141 142 143 144 145 146 147 148 149 150 151 152 153 154 ... 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 ... 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 ... 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 ... 469 470 471 472 473 474 475 476 477 478 479 480 481 482 ... 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 ... 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 ... 591 592 593 594 595 596 597 |
@extend %sans;
background-color: tone(-55%);
color: tone(25%);
font-size: 14pt;
margin: 0;
padding: 0;
}
a[href] {
color: tone(10%);
text-decoration-color: tone(10%,-0.5);
&:hover {
color: white;
text-shadow: 0 0 15px tone(20%);
text-decoration-color: tone(10%,-0.1);
}
}
a[href^="//"],
a[href^="http://"],
a[href^="https://"] { // external link
&:hover::after {
color: black;
background-color: white;
................................................................................
%glow {
box-shadow: 0 0 20px tone(0%,-0.8);
}
%button {
@extend %sans;
font-size: 14pt;
padding: 0.1in 0.2in;
border: 1px solid black;
color: tone(25%);
text-shadow: 1px 1px black;
text-decoration: none;
text-align: center;
cursor: default;
................................................................................
}
$grad-ui-focus: linear-gradient(to bottom,
tone(-50%),
tone(-35%)
);
input[type='text'], input[type='password'], textarea {
@extend %serif;
padding: 0.08in 0.1in;
box-sizing: border-box;
border: 1px solid black;
background: linear-gradient(to bottom, tone(-55%), tone(-40%));
font-size: 16pt;
color: tone(25%);
................................................................................
color: white;
border-image: linear-gradient(to bottom, tone(-10%), tone(-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: tone(-53%, -0.7);
}
................................................................................
grid-row: 2 / 3;
}
}
> .stats {
grid-column: 3 / 4;
grid-row: 1 / 3;
}
> .menu {
grid-column: 1 / 3; grid-row: 2 / 3;
padding-top: 0.075in;
flex-wrap: wrap;
display: flex;
justify-content: center;
align-items: center;
> a[href] {
@extend %button;
display: block;
margin: 0.025in 0.05in;
}
> hr {
all: unset;
display: block;
height: 0.3in;
................................................................................
}
.epithet {
display: inline-block;
background: tone(20%);
color: tone(-45%);
text-shadow: 0 0 3px tone(-30%, -0.4);
border-radius: 3px;
padding: 6px;
padding-top: 2px;
padding-bottom: 4px;
font-size: 80%;
vertical-align: top;
font-weight: 300;
letter-spacing: 0.5px;
................................................................................
tone(-55%) 10%,
tone(-50%) 80%,
tone(-45%)
);
// outline: 1px solid black;
}
body.error .message {
@extend %box;
width: 4in;
margin:auto;
padding: 0.5in;
text-align: center;
}
div.login {
................................................................................
padding: 0.1in;
padding-left: 0.15in;
>.nym { font-weight: bold; }
color: tone(0%,-0.4);
> span.nym { color: tone(10%) }
> span.handle { color: tone(-5%) }
background: linear-gradient(to right, tone(-55%), transparent);
}
>.content {
grid-column: 2/4; grid-row: 1/2;
padding: 0.2in;
@extend %serif;
font-size: 110%;
text-align: justify;
................................................................................
}
}
body.conf main {
display: grid;
grid-template-columns: 2in 1fr;
grid-template-rows: max-content 1fr;
> .menu {
margin-left: -0.25in;
grid-column: 1/2; grid-row: 1/2;
background: linear-gradient(to bottom, tone(-45%),tone(-55%));
border: 1px solid black;
padding: 0.1in;
> a[href] {
@extend %button;
................................................................................
border-left: none;
text-shadow: 1px 1px 0 black;
}
}
}
form {
.elem {
margin: 0.1in 0;
label { display:block; font-weight: bold; padding: 0.03in 0; }
.txtbox {
@extend %serif;
box-sizing: border-box;
padding: 0.08in 0.1in;
border: 1px solid black;
background: tone(-55%);
}
textarea { resize: vertical; min-height: 2in; }
input, textarea, .txtbox {
display: block;
width: 100%;
}
button { float: right; width: 50%; }
}
}
@keyframes flashup {
0% { opacity: 0; transform: scale(0.8); }
10% { opacity: 1; transform: scale(1.1); }
80% { opacity: 1; transform: scale(1); }
................................................................................
border-radius: 3px;
box-shadow: 0 0 50px tone(-55%);
color: white;
animation: ease forwards flashup;
//cubic-bezier(0.4, 0.63, 0.6, 0.31)
animation-duration: 3s;
}
|
> > > > > > > > > > | > | < | | > > > > > > | > > > > > > > > < > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 .. 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 ... 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 ... 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 ... 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 ... 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 ... 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 ... 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 ... 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 ... 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 ... 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 |
@extend %sans; background-color: tone(-55%); color: tone(25%); font-size: 14pt; margin: 0; padding: 0; } ::selection { color: tone(-60%); background-color: tone(-10%); } ::placeholder { color: tone(0,-0.3); font-style: italic; } a[href] { color: tone(10%); text-decoration-color: tone(10%,-0.5); &:hover { color: white; text-shadow: 0 0 15px tone(20%); text-decoration-color: tone(10%,-0.1); } &.button { @extend %button; } } a[href^="//"], a[href^="http://"], a[href^="https://"] { // external link &:hover::after { color: black; background-color: white; ................................................................................ %glow { box-shadow: 0 0 20px tone(0%,-0.8); } %button { @extend %sans; font-size: 14pt; box-sizing: border-box; padding: 0.1in 0.2in; border: 1px solid black; color: tone(25%); text-shadow: 1px 1px black; text-decoration: none; text-align: center; cursor: default; ................................................................................ } $grad-ui-focus: linear-gradient(to bottom, tone(-50%), tone(-35%) ); input[type='text'], input[type='password'], textarea, select { @extend %serif; padding: 0.08in 0.1in; box-sizing: border-box; border: 1px solid black; background: linear-gradient(to bottom, tone(-55%), tone(-40%)); font-size: 16pt; color: tone(25%); ................................................................................ color: white; border-image: linear-gradient(to bottom, tone(-10%), tone(-30%)) 1 / 1px; background: $grad-ui-focus; outline: none; @extend %glow; } } select { width: 100%; } @mixin glass { @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); background-color: tone(-53%, -0.7); } ................................................................................ grid-row: 2 / 3; } } > .stats { grid-column: 3 / 4; grid-row: 1 / 3; } > form.actions { grid-column: 1 / 3; grid-row: 2 / 3; padding-top: 0.075in; flex-wrap: wrap; display: flex; justify-content: center; align-items: center; > a[href] { display: block; margin: 0.025in 0.05in; } > hr { all: unset; display: block; height: 0.3in; ................................................................................ } .epithet { display: inline-block; background: tone(20%); color: tone(-45%); text-shadow: 0 0 3px tone(-30%, -0.4); border-radius: 2px; padding: 6px; padding-top: 2px; padding-bottom: 4px; font-size: 80%; vertical-align: top; font-weight: 300; letter-spacing: 0.5px; ................................................................................ tone(-55%) 10%, tone(-50%) 80%, tone(-45%) ); // outline: 1px solid black; } //body.error .message { .message { @extend %box; display: block; width: 4in; margin:auto; padding: 0.5in; text-align: center; } div.login { ................................................................................ padding: 0.1in; padding-left: 0.15in; >.nym { font-weight: bold; } color: tone(0%,-0.4); > span.nym { color: tone(10%) } > span.handle { color: tone(-5%) } background: linear-gradient(to right, tone(-55%), transparent); &:hover { > span.nym { color: white; } > span.handle { color: tone(15%) } } } >.content { grid-column: 2/4; grid-row: 1/2; padding: 0.2in; @extend %serif; font-size: 110%; text-align: justify; ................................................................................ } } body.conf main { display: grid; grid-template-columns: 2in 1fr; grid-template-rows: max-content 1fr; > menu { margin-left: -0.25in; grid-column: 1/2; grid-row: 1/2; background: linear-gradient(to bottom, tone(-45%),tone(-55%)); border: 1px solid black; padding: 0.1in; > a[href] { @extend %button; ................................................................................ border-left: none; text-shadow: 1px 1px 0 black; } } } hr { border: none; border-top: 1px solid tone(-30%); border-bottom: 1px solid tone(-55%); } form { margin: 0.15in 0; > p:first-child { margin-top: 0; } > p:last-child { margin-bottom: 0; } .elem { margin: 0.1in 0; label { display:block; font-weight: bold; padding: 0.03in 0; } .txtbox { @extend %serif; box-sizing: border-box; padding: 0.08in 0.1in; border: 1px solid black; background: tone(-55%); } input, textarea, .txtbox { display: block; width: 100%; } textarea { resize: vertical; min-height: 2in; } } .elem + %button { margin-left: 50%; width: 50%; } } menu.choice { display: flex; &.horizontal { flex-flow: row-reverse wrap; justify-content: space-evenly; } &.vertical { flex-flow: column; margin-left: 50%; } &.vertical-float { flex-flow: column; float: right; width: 40%; margin-left: 0.1in; } > %button { display: block; margin: 2px; flex-grow: 1 } } .check-panel { display: flex; flex-flow: row wrap; > label { display: block; box-sizing: border-box; width: calc(50% - 0.2in); padding: 0.1in 0.1in; margin: 0.1in 0.1in; background: tone(-45%); border: 1px solid black; text-shadow: 1px 1px black; flex-grow: 1; &:focus-within { border: 1px inset tone(-10%); background: tone(-50%); } } input[type="checkbox"] { -webkit-appearance: none; padding: 0.5em; background: tone(-35%); border: 1px outset tone(-50%); vertical-align: bottom; box-shadow: 0 1px tone(-50%); &:checked { border: 1px inset tone(-35%); background: tone(-60%); box-shadow: 0 1px tone(-40%); } &:focus { border-color: tone(10%); outline: none; } } } @keyframes flashup { 0% { opacity: 0; transform: scale(0.8); } 10% { opacity: 1; transform: scale(1.1); } 80% { opacity: 1; transform: scale(1); } ................................................................................ border-radius: 3px; box-shadow: 0 0 50px tone(-55%); color: white; animation: ease forwards flashup; //cubic-bezier(0.4, 0.63, 0.6, 0.31) animation-duration: 3s; } form.action-bar { display: flex; > * { flex-grow: 1; flex-basis: 0; margin-left: 0.1in; } > *:first-child { margin-left: 0; } } |
Modified store.t from [f53ab94a55] to [004846cca6].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 ... 227 228 229 230 231 232 233 234 235 236 237 238 239 240 ... 256 257 258 259 260 261 262 263 264 265 266 267 268 269 ... 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 ... 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
-- vim: ft=terra
local m = {
timepoint = int64;
scope = lib.enum {
'public', 'private', 'local';
'personal', 'direct', 'circle';
};
notiftype = lib.enum {
'mention', 'like', 'rt', 'react'
};
relation = lib.enum {
'follow', 'mute', 'block'
};
credset = lib.set {
'pw', 'otp', 'challenge', 'trust'
};
privset = lib.set {
'post', 'edit', 'acct', 'upload', 'censor', 'admin', 'invite'
};
................................................................................
id: uint64
author: uint64
subject: str
body: str
acl: str
posted: m.timepoint
discovered: m.timepoint
mentions: lib.mem.ptr(uint64)
circles: lib.mem.ptr(uint64) --only meaningful if scope is set to circle
convoheaduri: str
parent: uint64
-- ephemera
localpost: bool
source: &m.source
}
local cnf = terralib.memoize(function(ty,rty)
rty = rty or ty
return struct {
enum: {&opaque, uint64, rawstring} -> intptr
get: {&opaque, uint64, rawstring} -> rty
................................................................................
aid: uint64
uid: uint64
aname: str
netmask: m.inet
privs: m.privset
blacklist: bool
}
-- backends only handle content on the local server
struct m.backend { id: rawstring
open: &m.source -> &opaque
close: &m.source -> {}
dbsetup: &m.source -> bool -- creates the schema needed to call conprep (called only once per database e.g. with `parsav db init`)
conprep: {&m.source, m.prepmode.t} -> {} -- prepares queries and similar tasks that require the schema to already be in place
................................................................................
actor_save_privs: {&m.source, &m.actor} -> {}
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, uint64, pstr}
-- handles API authentication
-- origin: inet
-- handle: rawstring
-- key: rawstring (X-API-Key)
actor_auth_record_fetch: {&m.source, uint64} -> lib.mem.ptr(m.auth)
actor_powers_fetch: {&m.source, uint64} -> m.powerset
actor_session_fetch: {&m.source, uint64, m.inet} -> {lib.stat(m.auth), lib.mem.ptr(m.actor)}
-- retrieves an auth record + actor combo suitable by AID suitable
-- for determining session validity & caps
-- aid: uint64
-- origin: inet
actor_auth_register_uid: {&m.source, uint64, uint64} -> {}
-- notifies the backend module of the UID that has been assigned for
-- an authentication ID
-- aid: uint64
-- uid: uint64
actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
................................................................................
auth_create_pw: {&m.source, uint64, bool, lib.mem.ptr(int8)} -> {}
-- uid: uint64
-- reset: bool (delete other passwords?)
-- pw: pstring
auth_purge_pw: {&m.source, uint64, rawstring} -> {}
auth_purge_otp: {&m.source, uint64, rawstring} -> {}
auth_purge_trust: {&m.source, uint64, rawstring} -> {}
post_save: {&m.source, &m.post} -> {}
post_create: {&m.source, &m.post} -> uint64
post_enum_author_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post))
post_attach_ctl: {&m.source, uint64, uint64, bool} -> {}
-- attaches or detaches an existing database artifact
-- post id: uint64
-- artifact id: uint64
-- detach: bool
artifact_instantiate: {&m.source, lib.mem.ptr(uint8), lib.mem.ptr(int8)} -> uint64
|
| | > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > |
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 ... 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 ... 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 ... 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 ... 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 ... 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 |
-- vim: ft=terra
local m = {
timepoint = lib.osclock.time_t;
scope = lib.enum {
'public', 'private', 'local';
'personal', 'direct', 'circle';
};
notiftype = lib.enum {
'mention', 'like', 'rt', 'react'
};
relation = lib.set {
'silence', -- messages will not be accepted
'collapse', -- posts will be collapsed by default
'disemvowel', -- posts will be ritually humiliated, but shown
'avoid', -- posts will be kept out of the timeline but will show on users' posts and in conversations
'follow',
'mute', -- posts will be completely hidden at all times
'block', -- no interactions will be permitted, but posts will remain visible
};
credset = lib.set {
'pw', 'otp', 'challenge', 'trust'
};
privset = lib.set {
'post', 'edit', 'acct', 'upload', 'censor', 'admin', 'invite'
};
................................................................................
id: uint64
author: uint64
subject: str
body: str
acl: str
posted: m.timepoint
discovered: m.timepoint
edited: m.timepoint
chgcount: uint
mentions: lib.mem.ptr(uint64)
circles: lib.mem.ptr(uint64) --only meaningful if scope is set to circle
convoheaduri: str
parent: uint64
-- ephemera
localpost: bool
source: &m.source
-- save :: bool -> {} (defined in acl.t due to dep. hell)
}
local cnf = terralib.memoize(function(ty,rty)
rty = rty or ty
return struct {
enum: {&opaque, uint64, rawstring} -> intptr
get: {&opaque, uint64, rawstring} -> rty
................................................................................
aid: uint64
uid: uint64
aname: str
netmask: m.inet
privs: m.privset
blacklist: bool
}
struct m.relationship {
agent: uint64
patient: uint64
rel: m.relation -- agent → patient
recip: m.relation -- patient → agent
}
-- backends only handle content on the local server
struct m.backend { id: rawstring
open: &m.source -> &opaque
close: &m.source -> {}
dbsetup: &m.source -> bool -- creates the schema needed to call conprep (called only once per database e.g. with `parsav db init`)
conprep: {&m.source, m.prepmode.t} -> {} -- prepares queries and similar tasks that require the schema to already be in place
................................................................................
actor_save_privs: {&m.source, &m.actor} -> {}
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_rel: {&m.source, uint64, uint64} -> m.relationship
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, uint64, pstr}
-- handles API authentication
-- origin: inet
-- handle: rawstring
-- key: rawstring (X-API-Key)
actor_auth_record_fetch: {&m.source, uint64} -> lib.mem.ptr(m.auth)
actor_powers_fetch: {&m.source, uint64} -> m.powerset
actor_session_fetch: {&m.source, uint64, m.inet, m.timepoint} -> {lib.stat(m.auth), lib.mem.ptr(m.actor)}
-- retrieves an auth record + actor combo suitable by AID suitable
-- for determining session validity & caps
-- aid: uint64
-- origin: inet
-- cookie issue time: m.timepoint
actor_auth_register_uid: {&m.source, uint64, uint64} -> {}
-- notifies the backend module of the UID that has been assigned for
-- an authentication ID
-- aid: uint64
-- uid: uint64
actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
................................................................................
auth_create_pw: {&m.source, uint64, bool, lib.mem.ptr(int8)} -> {}
-- uid: uint64
-- reset: bool (delete other passwords?)
-- pw: pstring
auth_purge_pw: {&m.source, uint64, rawstring} -> {}
auth_purge_otp: {&m.source, uint64, rawstring} -> {}
auth_purge_trust: {&m.source, uint64, rawstring} -> {}
auth_sigtime_user_fetch: {&m.source, uint64} -> m.timepoint
-- authentication tokens and accounts have a property that controls
-- whether auth cookies dated to a certain point are valid. cookies
-- that are generated before the timepoint are considered invalid.
-- this is used primarily to lock out untrusted sessions.
-- uid: uint64
auth_sigtime_user_alter: {&m.source, uint64, m.timepoint} -> {}
-- uid: uint64
-- timestamp: timepoint
post_save: {&m.source, &m.post} -> {}
post_create: {&m.source, &m.post} -> uint64
post_fetch: {&m.source, uint64} -> lib.mem.ptr(m.post)
post_enum_author_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post))
post_attach_ctl: {&m.source, uint64, uint64, bool} -> {}
-- attaches or detaches an existing database artifact
-- post id: uint64
-- artifact id: uint64
-- detach: bool
artifact_instantiate: {&m.source, lib.mem.ptr(uint8), lib.mem.ptr(int8)} -> uint64
|
Modified str.t from [f2457b558f] to [265a0fa659].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
-- vim: ft=terra
-- string.t: string classes
local util = lib.util
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local m = {
sz = terralib.externfunction('strlen', rawstring -> intptr);
cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int);
cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
cat = terralib.externfunction('strcat',{rawstring, rawstring} -> rawstring);
ncat = terralib.externfunction('strncat',{rawstring, rawstring, intptr} -> rawstring);
................................................................................
struct m.acc {
buf: rawstring
sz: intptr
run: intptr
space: intptr
}
local terra biggest(a: intptr, b: intptr)
if a > b then return a else return b end
end
terra m.acc:init(run: intptr)
--lib.dbg('initializing string accumulator')
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
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
|
-- vim: ft=terra
-- string.t: string classes
local util = lib.util
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local m = {
t = pstr, ref = pref;
sz = terralib.externfunction('strlen', rawstring -> intptr);
cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int);
cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
cat = terralib.externfunction('strcat',{rawstring, rawstring} -> rawstring);
ncat = terralib.externfunction('strncat',{rawstring, rawstring, intptr} -> rawstring);
................................................................................
struct m.acc {
buf: rawstring
sz: intptr
run: intptr
space: intptr
}
terra m.cdowncase(c: int8)
if c >= @'A' and c <= @'Z' then
return c + (@'a' - @'A')
else return c end
end
terra m.cupcase(c: int8)
if c >= @'a' and c <= @'z' then
return c - (@'a' - @'A')
else return c end
end
local terra biggest(a: intptr, b: intptr)
if a > b then return a else return b end
end
terra m.acc:init(run: intptr)
--lib.dbg('initializing string accumulator')
|
Modified view/conf-profile.tpl from [f1f77d7014] to [d384fd3b9f].
1 2 3 4 5 6 |
<form method="post"> <div class="elem"><label>handle</label> <div class="txtbox">@!handle</div> <div class="elem"><label for="nym">display name</label> <input type="text" name="nym" id="nym" placeholder="j. random poster" value="@:nym"></div> <div class="elem"><label for="bio">bio</label><textarea name="bio" id="bio" placeholder="tall, dark, and mysterious">@!bio</textarea></div> <button>commit</button> </form> |
| |
1 2 3 4 5 6 |
<form method="post">
<div class="elem"><label>handle</label> <div class="txtbox">@!handle</div></div>
<div class="elem"><label for="nym">display name</label> <input type="text" name="nym" id="nym" placeholder="j. random poster" value="@:nym"></div>
<div class="elem"><label for="bio">bio</label><textarea name="bio" id="bio" placeholder="tall, dark, and mysterious">@!bio</textarea></div>
<button>commit</button>
</form>
|
Added view/conf-sec-credmg.tpl version [43efff9618].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
<hr> <form method="post"> <p>your account can currently be accessed with the credentials listed below. if you fear a credential has been compromised, you can revoke or reset it.</p> <select size="6" name="cred"> @credlist </select> <menu class="horizontal choice"> <button name="act" value="reset">reset</button> <button name="act" value="revoke">revoke</button> </menu> </form> <hr> <form method="post"> <p>you can associate extra credentials with your account. you can also limit how much of your authority these credentials can be used to exercise — for instance, it might be useful to create API keys that can read your timeline, but not post as you or access any administrative powers you may have. if you don't select a capability set, the credential will be able to wield the full scope of your powers.</p> <div class="check-panel"> <label><input type="checkbox" name="allow-post"> post</label> <label><input type="checkbox" name="allow-edit"> edit</label> <label><input type="checkbox" name="allow-acct"> manage account</label> <label><input type="checkbox" name="allow-upload"> upload artifacts</label> <label><input type="checkbox" name="allow-censor"> moderation</label> <label><input type="checkbox" name="allow-admin"> other admin powers</label> <label><input type="checkbox" name="allow-invite"> invite</label> </div> <p>you can also specify an IP address range in CIDR format to associate with this credential. if you do so, this credential will only be usable when connecting from an IP address in that range. otherwise, it will be valid when connecting from anywhere on the internet.</p> <div class="elem"> <label for="netmask">netmask</label> <input type="text" name="netmask" id="netmask" placeholder="10.0.0.0/8"> </div> <menu class="vertical choice"> <button name="kind" value="pw">new password</button> <button name="kind" value="otp">new OTP key</button> <button name="kind" value="api">new API token</button> <button name="kind" value="challenge">new challenge key</button> </div> </form> |
Modified view/conf-sec.tpl from [7ba95a81c5] to [de1cf7e8f0].
1 2 3 4 5 6 7 8 9 10 |
<form method="post"> <p>if you are concerned that your account may have been compromised, you can terminate all other login sessions by invalidating their session cookies. note that this will not have any effect on API tokens; these must be revoked separately!</p> <label> sessions valid from <div class="txtbox">@lastreset</div> </label> <button type="submit" name="act" value="invalidate"> invalidate other sessions </button> </form> |
| | | |
1 2 3 4 5 6 7 8 9 10 |
<form method="post"> <p>if you are concerned that your account may have been compromised, you can terminate all other login sessions by invalidating their session cookies. note that this will not have any effect on API tokens; these must be revoked separately!</p> <div class="elem"> <label> sessions valid from </label> <div class="txtbox">@lastreset</div> </div> <button type="submit" name="act" value="invalidate"> invalidate other sessions </button> </form> |
Modified view/conf.tpl from [bd130ad9d3] to [09b447ff32].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<div class="menu"> <a href="/conf/profile">profile</a> <a href="/conf/avi">avatar</a> <a href="/conf/sec">security</a> <a href="/conf/rel">relationships</a> <a href="/conf/qnt">quarantine</a> <a href="/conf/acl">ACL shortcuts</a> <a href="/conf/rooms">chatrooms</a> <a href="/conf/circles">circles</a> @menu </div> <div class="panel"> @panel </div> |
| | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<menu> <a href="/conf/profile">profile</a> <a href="/conf/avi">avatar</a> <a href="/conf/sec">security</a> <a href="/conf/rel">relationships</a> <a href="/conf/qnt">quarantine</a> <a href="/conf/acl">ACL shortcuts</a> <a href="/conf/rooms">chatrooms</a> <a href="/conf/circles">circles</a> @menu </menu> <div class="panel"> @panel </div> |
Added view/confirm.tpl version [9198c794e9].
> > > > > > > > > |
1 2 3 4 5 6 7 8 9 |
<form class="message"> <img class="icon" src="/s/query.webp"> <h1>@title</h1> <p>@query</p> <menu class="horizontal choice"> <a class="button" href="@:cancel">cancel</a> <button name="act" value="confirm">confirm</button> </menu> </form> |
Modified view/load.lua from [f63ef60595] to [212041720e].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
-- because lua can't scan directories, we need a
-- file that indexes the templates manually, and
-- copies them into a data structure we can then
-- create templates from when we return to terra
local path = ...
local sources = {
'docskel';
'tweet';
'profile';
'compose';
'login-username';
'login-challenge';
'conf';
'conf-profile';
}
local ingest = function(filename)
local hnd = io.open(path..'/'..filename)
local txt = hnd:read('*a')
io.close(hnd)
txt = txt:gsub('([^\\])!%b[]', '%1')
|
> > > |
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 |
-- because lua can't scan directories, we need a
-- file that indexes the templates manually, and
-- copies them into a data structure we can then
-- create templates from when we return to terra
local path = ...
local sources = {
'docskel';
'confirm';
'tweet';
'profile';
'compose';
'login-username';
'login-challenge';
'conf';
'conf-profile';
'conf-sec';
'conf-sec-credmg';
}
local ingest = function(filename)
local hnd = io.open(path..'/'..filename)
local txt = hnd:read('*a')
io.close(hnd)
txt = txt:gsub('([^\\])!%b[]', '%1')
|
Modified view/profile.tpl from [d21ccabbe8] to [cfeb837b05].
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<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> |
| | | | | |
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<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> <form class="actions"> <a class="button" href="/@:xid">posts</a> <a class="button" href="/@:xid/media">media</a> <a class="button" href="/@:xid/social">associates</a> <hr> @auxbtn </form> </div> |