| Comment: | wrote mimelib, continued iterating on litepub support; tweets can now be imported into honk |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
c774e2c5a9739cd9fbe1c939815b9a59 |
| User & Date: | lexi on 2021-01-28 00:51:21 |
| Other Links: | manifest | tags |
|
2021-01-28
| ||
| 02:44 | add in a bunch of missing pqclears, because i am a *retard*, and wipe out a fuckton of memory leaks check-in: a4e71fdfda user: lexi tags: trunk | |
| 00:51 | wrote mimelib, continued iterating on litepub support; tweets can now be imported into honk check-in: c774e2c5a9 user: lexi tags: trunk | |
|
2021-01-25
| ||
| 14:38 | iterating check-in: 050ce7d4fc user: lexi tags: trunk | |
Modified api/lp/actor.t from [b336ed6430] to [c96b63a8f1].
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
"preferredUsername": %$handle,
"name": %$nym,
"summary": %$desc,
"alsoKnownAs": ["https://%+domain/@%+handle"],
"publicKey": {
"id": "%lpid#ident-rsa",
"owner": "%lpid",
"publicKeyPem": %rsa
},
"icon": {
"type": "Image",
"url": "https://%+domain%+avi"
},
"capabilities": { "acceptsChatMessages": false },
"discoverable": true,
................................................................................
"outbox": "https://%+domain/api/lp/outbox/user/%uid",
"followers": "https://%+domain/api/lp/rel/%uid/followers",
"following": "https://%+domain/api/lp/rel/%uid/following"
}]];
}
local pstr = lib.str.t
terra cs(s: rawstring) return pstr {s, lib.str.sz(s)} end
local terra
api_lp_actor(co: &lib.srv.convo, actor: &lib.store.actor)
var lpid = co:stra(64)
lpid:lpush'https://':ppush(co.srv.cfg.domain):lpush'/user/':shpush(actor.id)
var uid = co:stra(32) uid:shpush(actor.id) -- dumb hack bc lazy FIXME
var body = tpl {
domain = co.srv.cfg.domain;
uid = uid:finalize();
lpid = lpid:finalize();
handle = cs(actor.handle);
nym = cs(actor.nym);
desc = cs(actor.bio);
avi = cs(actor.avatar);
rsa = '';
locked = 'false';
}
co:json(body:poolstr(&co.srv.pool))
end
return api_lp_actor
|
|
|
>
>
>
>
>
>
>
>
>
>
>
|
>
|
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
|
"preferredUsername": %$handle,
"name": %$nym,
"summary": %$desc,
"alsoKnownAs": ["https://%+domain/@%+handle"],
"publicKey": {
"id": "%lpid#ident-rsa",
"owner": "%lpid",
"publicKeyPem": %$rsa
},
"icon": {
"type": "Image",
"url": "https://%+domain%+avi"
},
"capabilities": { "acceptsChatMessages": false },
"discoverable": true,
................................................................................
"outbox": "https://%+domain/api/lp/outbox/user/%uid",
"followers": "https://%+domain/api/lp/rel/%uid/followers",
"following": "https://%+domain/api/lp/rel/%uid/following"
}]];
}
local pstr = lib.str.t
terra cs(s: rawstring) return pstr {s, lib.trn(s == nil,0,lib.str.sz(s))} end
local terra
api_lp_actor(co: &lib.srv.convo, actor: &lib.store.actor)
var lpid = co:stra(64)
lpid:lpush'https://':ppush(co.srv.cfg.domain):lpush'/user/':shpush(actor.id)
var uid = co:stra(32) uid:shpush(actor.id) -- dumb hack bc lazy FIXME
var upk = lib.crypt.loadpriv(actor.key)
var pem: lib.crypt.pemfile
if not upk.ok then
lib.warn("could not load user's keypair; this is a sign of a bug, a corrupt database, or a problem with mbedtls")
else defer upk.val:free()
if not lib.crypt.pem(true, &upk.val, &pem[0]) then
pem[0] = 0;
lib.warn('could not export actor certificate as PEM file; there is a bug, the database is corrupt, or there is a problem in mbedtls')
end
end
var body = tpl {
domain = co.srv.cfg.domain;
uid = uid:finalize();
lpid = lpid:finalize();
handle = cs(actor.handle);
nym = cs(actor.nym);
desc = cs(actor.bio);
avi = cs(actor.avatar);
rsa = cs(&pem[0]);
locked = 'false';
}
co:json(body:poolstr(&co.srv.pool))
end
return api_lp_actor
|
Modified api/lp/tweet.t from [1ada03554b] to [cbbfbd7e42].
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 |
local obj = lib.tpl.mk [[{
"\@context": "https://@+domain/s/litepub.jsonld",
"type": "Note",
"id": "https://@+domain/post/@^pid",
"content": @$html,
"source": @$raw,
"attributedTo": "https://@+domain/user/@^uid",
"published": "@pubtime"
@extra
}]]
local wrap = lib.tpl.mk [[{
"\@context": "https://@+domain/s/litepub.jsonld",
"type": "@kind",
"actor": "https://@+domain/user/@^uid",
"published": "@pubtime",
"id": "https://@+domain/api/lp/act/@^aid",
"object": @obj
}]]
local terra
lp_tweet(co: &lib.srv.convo, p: &lib.store.post, act_wrap: bool)
var opdate = lib.conv.datetime(&co.srv.pool, p.posted)
var tweet = (obj {
domain = co.srv.cfg.domain, uid = p.author, pid = p.id;
html = lib.smackdown.html(&co.srv.pool, p.body, false);
raw = p.body, pubtime = opdate, extra = '';
}):poolstr(&co.srv.pool)
if act_wrap then
return (wrap {
domain = co.srv.cfg.domain, obj = tweet;
kind = lib.trn(p.rtdby == 0, 'Create', 'Announce');
uid = lib.trn(p.rtdby == 0, p.author, p.rtdby);
aid = lib.trn(p.rtdby == 0, p.id, p.rtact);
pubtime = lib.trn(p.rtdby == 0, opdate,
lib.conv.datetime(&co.srv.pool,p.rtdat));
}):poolstr(&co.srv.pool)
else
return tweet
end
end
return lp_tweet
|
> | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | |
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 |
local obj = lib.tpl.mk [[{
"\@context": "https://@+domain/s/litepub.jsonld",
"type": "Note",
"id": "https://@+domain/post/@^pid",
"content": @$html,
"source": @$raw,
"attributedTo": "https://@+domain/user/@^uid",
"actor": "https://@+domain/user/@^uid",
"published": "@pubtime",
"sensitive": false,
"directMessage": false,
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"summary": @$subj
@extra
}]]
local wrap = lib.tpl.mk [[{
"\@context": "https://@+domain/s/litepub.jsonld",
"type": "@kind",
"actor": "https://@+domain/user/@^uid",
"published": "@pubtime",
"id": "https://@+domain/api/lp/act/@^aid",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"object": @obj
}]]
local terra
lp_tweet(co: &lib.srv.convo, p: &lib.store.post, act_wrap: bool): pstr
var opdate = lib.munge.datetime(&co.srv.pool, p.posted)
var extra: lib.str.acc extra:pool(&co.srv.pool,256)
if p.parent ~= 0 then
extra:lpush ',"inReplyTo":"'
var par = co.srv:post_fetch(p.parent)
if not par then
lib.warn('database integrity violation: broken parent reference')
else defer par:free()
if par().localpost then -- gen uri for parent
extra:lpush'https://':qpush(co.srv.cfg.domain):lpush'/post/':shpush(p.parent)
else extra:push(par().uri,0) end
end
extra:lpush'"'
end
extra:lpush ',"conversation":"'
if p.convoheaduri ~= nil then
extra:qpush(p.convoheaduri)
else
var cid: uint64 = 0
if p.parent ~= 0 then
var top = co.srv:thread_top_find(p.parent)
var tp = co.srv:post_fetch(top)
if not tp then
lib.warn('database integrity violation: missing thread parent')
cid = p.id
else
if tp().convoheaduri ~= nil then
extra:push(tp().convoheaduri,0)
elseif tp().localpost == false then
extra:push(tp().uri,0)
else cid = top end
end
else
cid = p.id
end
if cid ~= 0 then
extra:lpush'https://':qpush(co.srv.cfg.domain)
:lpush'/post/':shpush(cid):lpush'/tree'
end
end
extra:lpush'"'
var tweet = (obj {
domain = co.srv.cfg.domain, uid = p.author, pid = p.id;
html = lib.smackdown.html(&co.srv.pool, p.body, false);
raw = p.body, pubtime = opdate, extra = extra:finalize();
subj = lib.trn(p.subject ~= nil, pstr(p.subject), pstr'');
}):poolstr(&co.srv.pool)
if act_wrap then
return (wrap {
domain = co.srv.cfg.domain, obj = tweet;
kind = lib.trn(p.rtdby == 0, 'Create', 'Announce');
uid = lib.trn(p.rtdby == 0, p.author, p.rtdby);
aid = lib.trn(p.rtdby == 0, p.id, p.rtact);
pubtime = lib.trn(p.rtdby == 0, opdate,
lib.munge.datetime(&co.srv.pool,p.rtdat));
}):poolstr(&co.srv.pool)
else
return tweet
end
end
return lp_tweet
|
Modified api/webfinger.t from [c64d390bdc] to [06153fd078].
10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
"type": "text/html", "href": @$pfp }
]
}]]
local terra
webfinger(co: &lib.srv.convo)
var res = co:pgetv('resource')
if (not res) or not res:startswith 'acct:' then goto err end
var acct = res + 5
var svp = lib.str.find(acct, '@')
if svp:ref() then
acct.ct = (svp.ptr - acct.ptr)
svp:advance(1)
|
> |
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
"type": "text/html", "href": @$pfp }
]
}]]
local terra
webfinger(co: &lib.srv.convo)
var res = co:pgetv('resource')
lib.dbg('got webfinger request for resource ', {res.ptr,res.ct})
if (not res) or not res:startswith 'acct:' then goto err end
var acct = res + 5
var svp = lib.str.find(acct, '@')
if svp:ref() then
acct.ct = (svp.ptr - acct.ptr)
svp:advance(1)
|
Modified backend/pgsql.t from [18ea7fb9d0] to [d6c61590d0].
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 ... 502 503 504 505 506 507 508 509 510 511 512 513 514 515 .... 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 .... 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 .... 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 .... 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 .... 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 .... 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 .... 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 |
where id = $1::bigint
]]
};
post_create = {
params = {
uint64, rawstring, rawstring, rawstring,
uint64, uint64, rawstring
}, sql = [[
insert into parsav_posts (
author, subject, acl, body,
parent, posted, discovered,
circles, mentions, convoheaduri
) values (
$1::bigint, case when $2::text = '' then null else $2::text end,
$3::text, $4::text,
$5::bigint, $6::bigint, $6::bigint,
array[]::bigint[], array[]::bigint[], $7::text
) returning id
]]; -- TODO array handling
};
post_destroy_prepare = {
params = {uint64}, cmd = true, sql = [[
update parsav_posts set
................................................................................
params = {uint64}, sql = [[
select (p.post).*
from pg_temp.parsavpg_known_content as p
where (p.post).parent = $1::bigint and (p.post).rtdby = 0
order by (p.post).posted, (p.post).discovered asc
]];
};
thread_latest_arrival_calc = {
params = {uint64}, sql = [[
with recursive posts(id) as (
select id from parsav_posts where parent = $1::bigint
union
select p.id from parsav_posts as p
................................................................................
if ct == 0 then
lib.pq.PQclear(res)
return pqr {0, nil}
else
return pqr {ct, res}
end
end
end
local terra row_to_artifact(res: &pqr, i: intptr): lib.mem.ptr(lib.store.artifact)
var id = res:int(uint64,i,0)
var idbuf: int8[lib.math.shorthand.maxlen]
var idlen = lib.math.shorthand.gen(id, &idbuf[0])
var desc = res:_string(i,2)
................................................................................
m.ptr.rid = id
return m
end
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)
if r:null(row,6)
then p.ptr.posted = 0
else p.ptr.posted = r:int(uint64,row,6)
end
................................................................................
else p.ptr.edited = r:int(uint64,row,8)
end
p.ptr.parent = r:int(uint64,row,9)
if r:null(row,11)
then p.ptr.chgcount = 0
else p.ptr.chgcount = r:int(uint32,row,11)
end
p.ptr.accent = r:int(int16,row,12)
p.ptr.rtdby = r:int(uint64,row,13)
p.ptr.rtdat = r:int(uint64,row,14)
p.ptr.rtact = r:int(uint64,row,15)
p.ptr.likes = r:int(uint32,row,16)
p.ptr.rts = r:int(uint32,row,17)
p.ptr.isreply = r:bool(row,18)
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)
var av: rawstring, avlen: intptr
var nym: rawstring, nymlen: intptr
var bio: rawstring, biolen: intptr
var epi: rawstring, epilen: intptr
var origin: uint64 = 0
var handle = r:_string(row, 2)
if not r:null(row,3) then origin = r:int(uint64,row,3) end
var avia = lib.str.acc {buf=nil}
if origin == 0 then
avia:compose('/avi/',handle)
................................................................................
bio = r:string(row,4)
biolen = r:len(row,4)+1
end
if r:null(row,9) then epilen = 0 epi = nil else
epi = r:string(row,9)
epilen = r:len(row,9)+1
end
a = [ lib.str.encapsulate(lib.store.actor, {
nym = {`nym, `nymlen};
bio = {`bio, `biolen};
epithet = {`epi, `epilen};
avatar = {`av,`avlen};
handle = {`handle.ptr, `handle.ct + 1};
xid = {`r:string(row, 11); `r:len(row,11) + 1};
}) ]
a.ptr.id = r:int(uint64, row, 0);
a.ptr.rights = lib.store.rights_default();
a.ptr.rights.rank = r:int(uint16, row, 6);
a.ptr.rights.quota = r:int(uint32, row, 7);
a.ptr.rights.invites = r:int(uint32, row, 12);
a.ptr.knownsince = r:int(int64,row, 10);
a.ptr.avatarid = r:int(uint64,row, 13);
if r:null(row,8) then
a.ptr.key.ct = 0 a.ptr.key.ptr = nil
else
a.ptr.key = r:bin(row,8)
end
a.ptr.origin = origin
if avia.buf ~= nil then avia:free() end
return a
end
local privmap = lib.store.powmap
................................................................................
blacklist = res:bool(i, 3);
pubkey = res:bin(i, 4);
}
end]
if rsakeys.sz > 0 then defer rsakeys:free()
for i=0, rsakeys.sz do var props = toprops(&rsakeys, i)
lib.dbg('loading next RSA pubkey')
var pub = lib.crypt.loadpub(props.pubkey.ptr, props.pubkey.ct)
if pub.ok then defer pub.val:free()
lib.dbg('checking pubkey against response')
var vfy, secl = lib.crypt.verify(&pub.val, token.ptr, token.ct, sig.ptr, sig.ct)
if vfy then
lib.dbg('signature verified')
if props.blacklist then lib.dbg('key blacklisted!') goto fail end
var dupname = lib.str.dup(props.name.ptr)
................................................................................
post_create = [terra(
src: &lib.store.source,
post: &lib.store.post
): uint64
var r = queries.post_create.exec(src,
post.author,post.subject,post.acl,post.body,
post.parent,post.posted,post.convoheaduri
)
if r.sz == 0 then return 0 end
defer r:free()
var id = r:int(uint64,0,0)
post.source = src
return id
end];
................................................................................
if n.kind == lib.store.noticetype.react then
var react = r:_string(0,5)
lib.str.ncpy(n.reaction, react.ptr, lib.math.smallest(react.ct,[(`n.reaction).tree.type.N]))
end
return n
end];
thread_latest_arrival_calc = [terra(
src: &lib.store.source,
post: uint64
): lib.store.timepoint
var r = queries.thread_latest_arrival_calc.exec(src,post)
if r.sz == 0 or r:null(0,0) then return 0 end
|
| | | > > > > > > > > > > > > > > > > > > > > | | | | | | | > > > > > > > > < < < < < | | > > > > > > > > > > |
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 ... 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 .... 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 .... 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 .... 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 .... 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 .... 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 .... 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 .... 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 |
where id = $1::bigint
]]
};
post_create = {
params = {
uint64, rawstring, rawstring, rawstring,
uint64, uint64, rawstring, rawstring
}, sql = [[
insert into parsav_posts (
author, subject, acl, body,
parent, posted, discovered,
circles, mentions, convoheaduri, uri
) values (
$1::bigint, case when $2::text = '' then null else $2::text end,
$3::text, $4::text,
$5::bigint, $6::bigint, $6::bigint,
array[]::bigint[], array[]::bigint[], $7::text, $8::text
) returning id
]]; -- TODO array handling
};
post_destroy_prepare = {
params = {uint64}, cmd = true, sql = [[
update parsav_posts set
................................................................................
params = {uint64}, sql = [[
select (p.post).*
from pg_temp.parsavpg_known_content as p
where (p.post).parent = $1::bigint and (p.post).rtdby = 0
order by (p.post).posted, (p.post).discovered asc
]];
};
thread_top_find = {
params = {uint64}, sql = [[
with recursive tree(gen,id,par) as (
select 0, id, parent from parsav_posts where id = $1::bigint
union
select tree.gen + 1, p.id, p.parent from tree
inner join parsav_posts as p on p.id = tree.par
)
select id from tree order by gen desc limit 1
]];
};
thread_latest_arrival_calc = {
params = {uint64}, sql = [[
with recursive posts(id) as (
select id from parsav_posts where parent = $1::bigint
union
select p.id from parsav_posts as p
................................................................................
if ct == 0 then
lib.pq.PQclear(res)
return pqr {0, nil}
else
return pqr {ct, res}
end
end
q.exec.name = 'pgsql.' .. k .. '.exec'
end
local terra row_to_artifact(res: &pqr, i: intptr): lib.mem.ptr(lib.store.artifact)
var id = res:int(uint64,i,0)
var idbuf: int8[lib.math.shorthand.maxlen]
var idlen = lib.math.shorthand.gen(id, &idbuf[0])
var desc = res:_string(i,2)
................................................................................
m.ptr.rid = id
return m
end
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
var uri: rawstring, urilen: 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
if r:null(row,12)
then uri = nil urilen = 0
else uri = r:string(row,12) urilen = r:len(row,12)+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
uri = { `uri, `urilen };
}) ]
p.ptr.id = r:int(uint64,row,1)
p.ptr.author = r:int(uint64,row,2)
if r:null(row,6)
then p.ptr.posted = 0
else p.ptr.posted = r:int(uint64,row,6)
end
................................................................................
else p.ptr.edited = r:int(uint64,row,8)
end
p.ptr.parent = r:int(uint64,row,9)
if r:null(row,11)
then p.ptr.chgcount = 0
else p.ptr.chgcount = r:int(uint32,row,11)
end
p.ptr.accent = r:int(int16,row,13)
p.ptr.rtdby = r:int(uint64,row,14)
p.ptr.rtdat = r:int(uint64,row,15)
p.ptr.rtact = r:int(uint64,row,16)
p.ptr.likes = r:int(uint32,row,17)
p.ptr.rts = r:int(uint32,row,18)
p.ptr.isreply = r:bool(row,19)
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)
var av: rawstring, avlen: intptr
var nym: rawstring, nymlen: intptr
var bio: rawstring, biolen: intptr
var epi: rawstring, epilen: intptr
var key: &uint8, keylen: intptr
var origin: uint64 = 0
var handle = r:_string(row, 2)
if not r:null(row,3) then origin = r:int(uint64,row,3) end
var avia = lib.str.acc {buf=nil}
if origin == 0 then
avia:compose('/avi/',handle)
................................................................................
bio = r:string(row,4)
biolen = r:len(row,4)+1
end
if r:null(row,9) then epilen = 0 epi = nil else
epi = r:string(row,9)
epilen = r:len(row,9)+1
end
if r:null(row,8) then
keylen = 0 key = nil
else
var k = r:bin(row,8)
keylen = k.ct key = k.ptr
end
a = [ lib.str.encapsulate(lib.store.actor, {
nym = {`nym, `nymlen};
bio = {`bio, `biolen};
epithet = {`epi, `epilen};
avatar = {`av,`avlen};
handle = {`handle.ptr, `handle.ct + 1};
xid = {`r:string(row, 11); `r:len(row,11) + 1};
key = {`key,`keylen};
}) ]
a.ptr.id = r:int(uint64, row, 0);
a.ptr.rights = lib.store.rights_default();
a.ptr.rights.rank = r:int(uint16, row, 6);
a.ptr.rights.quota = r:int(uint32, row, 7);
a.ptr.rights.invites = r:int(uint32, row, 12);
a.ptr.knownsince = r:int(int64,row, 10);
a.ptr.avatarid = r:int(uint64,row, 13);
a.ptr.origin = origin
if avia.buf ~= nil then avia:free() end
return a
end
local privmap = lib.store.powmap
................................................................................
blacklist = res:bool(i, 3);
pubkey = res:bin(i, 4);
}
end]
if rsakeys.sz > 0 then defer rsakeys:free()
for i=0, rsakeys.sz do var props = toprops(&rsakeys, i)
lib.dbg('loading next RSA pubkey')
var pub = lib.crypt.loadpub(props.pubkey)
if pub.ok then defer pub.val:free()
lib.dbg('checking pubkey against response')
var vfy, secl = lib.crypt.verify(&pub.val, token.ptr, token.ct, sig.ptr, sig.ct)
if vfy then
lib.dbg('signature verified')
if props.blacklist then lib.dbg('key blacklisted!') goto fail end
var dupname = lib.str.dup(props.name.ptr)
................................................................................
post_create = [terra(
src: &lib.store.source,
post: &lib.store.post
): uint64
var r = queries.post_create.exec(src,
post.author,post.subject,post.acl,post.body,
post.parent,post.posted,post.convoheaduri,post.uri
)
if r.sz == 0 then return 0 end
defer r:free()
var id = r:int(uint64,0,0)
post.source = src
return id
end];
................................................................................
if n.kind == lib.store.noticetype.react then
var react = r:_string(0,5)
lib.str.ncpy(n.reaction, react.ptr, lib.math.smallest(react.ct,[(`n.reaction).tree.type.N]))
end
return n
end];
thread_top_find = [terra(
src: &lib.store.source,
post: uint64
): uint64
var r = queries.thread_top_find.exec(src,post)
if r.sz == 0 then return 0 end
defer r:free()
return r:int(uint64,0,0)
end];
thread_latest_arrival_calc = [terra(
src: &lib.store.source,
post: uint64
): lib.store.timepoint
var r = queries.thread_latest_arrival_calc.exec(src,post)
if r.sz == 0 or r:null(0,0) then return 0 end
|
Modified backend/schema/pgsql-views.sql from [b916bb0a63] to [a4ebd0c2e8].
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
body text,
posted bigint,
discovered bigint,
edited bigint,
parent bigint,
convoheaduri text,
chgcount integer,
-- ephemeral
accent smallint,
rtdby bigint, -- note that these must be 0 if the record
rtdat bigint, -- in question does not represent an RT!
rtid bigint, -- (this one too)
n_likes integer,
n_rts integer,
................................................................................
pg_temp.parsavpg_translate_post(parsav_posts,bigint,bigint,bigint)
returns pg_temp.parsavpg_intern_post as $$
select a.origin is null,
($1).id, ($1).author,
($1).subject,($1).acl, ($1).body,
($1).posted, ($1).discovered, ($1).edited,
($1).parent, ($1).convoheaduri,($1).chgcount,
coalesce(c.value, -1)::smallint,
$2 as rtdby, $3 as rtdat, $4 as rtid,
re.likes, re.rts,
($1).parent in (select id from parsav_posts)
from parsav_actors as a
left join parsav_actor_conf_ints as c
on c.key = 'ui-accent' and
c.uid = a.id
|
>
|
|
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
body text, posted bigint, discovered bigint, edited bigint, parent bigint, convoheaduri text, chgcount integer, uri text, -- ephemeral accent smallint, rtdby bigint, -- note that these must be 0 if the record rtdat bigint, -- in question does not represent an RT! rtid bigint, -- (this one too) n_likes integer, n_rts integer, ................................................................................ pg_temp.parsavpg_translate_post(parsav_posts,bigint,bigint,bigint) returns pg_temp.parsavpg_intern_post as $$ select a.origin is null, ($1).id, ($1).author, ($1).subject,($1).acl, ($1).body, ($1).posted, ($1).discovered, ($1).edited, ($1).parent, ($1).convoheaduri,($1).chgcount, ($1).uri, coalesce(c.value, -1)::smallint, $2 as rtdby, $3 as rtdat, $4 as rtid, re.likes, re.rts, ($1).parent in (select id from parsav_posts) from parsav_actors as a left join parsav_actor_conf_ints as c on c.key = 'ui-accent' and c.uid = a.id |
Modified backend/schema/pgsql.sql from [4c18dab250] to [abc8356ef1].
59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
); create index on parsav_rights (actor); comment on table parsav_rights is 'a backward-compatible list of every non-default privilege or deprivilege granted to a local user'; create table parsav_posts ( id <def:uniq>, 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 bigint not null, discovered bigint not null, chgcount integer not null default 0, |
> |
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
);
create index on parsav_rights (actor);
comment on table parsav_rights is
'a backward-compatible list of every non-default privilege or deprivilege granted to a local user';
create table parsav_posts (
id <def:uniq>,
uri text, -- null if local
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 bigint not null,
discovered bigint not null,
chgcount integer not null default 0,
|
Added convo.t version [ff68c1adfe].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local srv = ... local pstring = lib.str.t 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 method: lib.http.method.t live_last: lib.store.timepoint uploads: lib.mem.vec(lib.http.upload) body: pstring -- cache ui_hue: uint16 navbar: pstring actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries -- private varbuf: pstring vbofs: &int8 } struct convo.page { title: pstring body: pstring class: pstring cache: bool } local usrdefs = { str = { ['acl-follow' ] = {cfgfld = 'usrdef_pol_follow', fallback = 'local'}; ['acl-follow-req'] = {cfgfld = 'usrdef_pol_follow_req', fallback = 'all'}; }; } terra convo:matchmime(mime: lib.http.mime.t): bool return self.reqtype == [lib.http.mime.none] or self.reqtype == mime end terra convo:usercfg_str(uid: uint64, setting: pstring): pstring var set = self.srv:actor_conf_str_get(&self.srv.pool, uid, setting) if not set then [(function() local q = quote return pstring.null() end for key, dfl in pairs(usrdefs.str) do local rv if dfl.cfgfld then rv = quote var cf = self.srv.cfg.[dfl.cfgfld] in terralib.select(not cf, pstring([dfl.fallback]), cf) end elseif dfl.lit then rv = dfl.lit end q = quote if setting:cmp([key]) then return [rv] else [q] end end end return q end)()] else return set end end terra convo:uid2actor_live(uid: uint64) var actor = self.srv:actor_fetch_uid(uid) if actor:ref() then if self.aid ~= 0 and self.who.id ~= uid then actor(0).relationship = self.srv:actor_rel_calc(self.who.id, uid) else -- defensive branch actor(0).relationship = lib.store.relationship { agent = 0, patient = uid; rel = [lib.store.relation.null], recip = [lib.store.relation.null], } end end return actor end terra convo:uid2actor(uid: uint64) var actor: &lib.store.actor = nil for j = 0, self.actorcache.top do if uid == self.actorcache(j).ptr.id then actor = self.actorcache(j).ptr break end end if actor == nil then actor = self.actorcache:insert(self:uid2actor_live(uid)).ptr end return actor end terra convo:rawpage(code: uint16, pg: convo.page, hdrs: lib.mem.ptr(lib.http.header)) var doc = data.view.docskel { instance = self.srv.cfg.instance; title = pg.title; body = pg.body; class = pg.class; navlinks = self.navbar; attr = ''; } var attrbuf: int8[32] if self.aid ~= 0 and self.ui_hue ~= 323 then var hdecbuf: int8[21] var hdec = lib.math.decstr(self.ui_hue, &hdecbuf[20]) lib.str.cpy(&attrbuf[0], ' style="--hue:') lib.str.cpy(&attrbuf[14], hdec) var len = &hdecbuf[20] - hdec lib.str.cpy(&attrbuf[14] + len, '"') doc.attr = &attrbuf[0] end if self.method == [lib.http.method.head] then doc:head(self.con,code,hdrs) else doc:send(self.con,code,hdrs) end end terra convo:statpage(code: uint16, pg: convo.page) var hdrs = array( lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' }, lib.http.header { key = 'Cache-Control', value = 'no-store' } ) self:rawpage(code,pg, [lib.mem.ptr(lib.http.header)] { ptr = &hdrs[0]; ct = [hdrs.type.N] - lib.trn(pg.cache,1,0); }) end terra convo:livepage(pg: convo.page, lastup: lib.store.timepoint) var nbuf: int8[21] var hdrs = array( lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' }, lib.http.header { key = 'Cache-Control', value = 'no-store' }, lib.http.header { key = 'X-Live-Newest-Artifact'; value = lib.math.decstr(lastup, &nbuf[20]); }, lib.http.header { key = 'Content-Length', value = '0' } ) if self.live_last ~= 0 and self.live_last == lastup then lib.net.mg_printf(self.con, 'HTTP/1.1 %s', lib.http.codestr(200)) for i = 0, [hdrs.type.N] do lib.net.mg_printf(self.con, '%s: %s\r\n', hdrs[i].key, hdrs[i].value) end lib.net.mg_printf(self.con, '\r\n') else self:rawpage(200, pg, [lib.mem.ptr(lib.http.header)] { ptr = &hdrs[0], ct = 3 }) end end terra convo:stdpage(pg: convo.page) self:statpage(200, pg) end terra convo:bytestream_trusted(lockdown: bool, mime: pstring, data: lib.mem.ptr(uint8)) var lockhdr = "Content-Security-Policy: sandbox; default-src 'none'; form-action 'none'; navigate-to 'none';\r\n" if not lockdown then lockhdr = "" end lib.net.mg_printf(self.con, "HTTP/1.1 200 OK\r\nContent-Type: %.*s\r\nContent-Length: %llu\r\n%sX-Content-Options: nosniff\r\n\r\n", mime.ct, mime.ptr, data.ct + 2, lockhdr) lib.net.mg_send(self.con, data.ptr, data.ct) lib.net.mg_send(self.con, '\r\n', 2) end terra convo:json(data: pstring) self:bytestream_trusted(false, 'application/activity+json; charset=utf-8', data:blob()) end terra convo:bytestream(mime: pstring, data: lib.mem.ptr(uint8)) var ty = lib.mime.lookup(mime) if ty == nil then lib.dbg("mime type ", {mime.ptr,mime.ct}, ' not in database!') mime = 'application/x-octet-stream' else if not ty.safe then lib.dbg("mime type ", {mime.ptr,mime.ct}, ' not safe!') if ty.binary then mime = 'application/x-octet-stream' else mime = 'text/plain' end end end self:bytestream_trusted(true, mime, data) end terra convo:reroute_cookie(dest: rawstring, cookie: rawstring) var hdrs = array( lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' }, lib.http.header { key = 'Location', value = dest }, lib.http.header { key = 'Set-Cookie', value = cookie } ) var body = data.view.docskel { instance = self.srv.cfg.instance.ptr; title = 'rerouting'; body = 'you are being redirected'; class = 'error'; navlinks = ''; attr = ''; } 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:stra(sz: intptr) -- convenience function var s: lib.str.acc s:pool(&self.srv.pool,sz) return s end convo.methods.qstr = macro(function(self, ...) -- convenience string builder local exp = {...} return `lib.str.acc{}:pcompose(&self.srv.pool, [exp]):finalize() end) terra convo:complain(code: uint16, title: rawstring, msg: rawstring) if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end if self:matchmime(lib.http.mime.html) then var body = [convo.page] { title = self:qstr('error :: ', title); body = self:qstr('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>'); class = 'error'; cache = false; } self:statpage(code, body) else var pg = lib.http.page { respcode = code, body = pstring.null() } var ctt = lib.http.mime.none if self:matchmime(lib.http.mime.json) then ctt = lib.http.mime.json pg.body = ([lib.tpl.mk'{"_parsav_error":@$ekind, "_parsav_error_desc":@$edesc}'] {ekind = title, edesc = msg}):poolstr(&self.srv.pool) elseif self:matchmime(lib.http.mime.text) then ctt = lib.http.mime.text pg.body = self:qstr('error: ',title,'\n',msg) elseif self:matchmime(lib.http.mime.mkdown) then ctt = lib.http.mime.mkdown pg.body = self:qstr('# error :: ',title,'\n\n',msg) elseif self:matchmime(lib.http.mime.ansi) then ctt = lib.http.mime.ansi pg.body = self:qstr('\27[1;31merror :: ',title,'\27[m\n',msg) end var cthdr = lib.http.header { 'Content-Type', 'text/plain' } if ctt == lib.http.mime.none then pg.headers.ct = 0 else pg.headers = lib.typeof(pg.headers) { &cthdr, 1 } switch ctt do escape for key,ty in ipairs(lib.mime.types) do if key ~= 'none' and lib.http.mime[key] ~= nil then emit quote case [ctt.type](lib.http.mime.[key]) then cthdr.value = [ty.id[1]] end end end end end end end pg:send(self.con) end end terra convo:fail(code: uint16) switch code do escape local stderrors = { {400, 'bad request', "the action you have attempted on this resource is not meaningful"}; {401, 'unauthorized', "this resource is not available at your clearance level"}; {403, 'forbidden', "we can neither confirm nor deny the existence of this resource"}; {404, 'resource not found', "that resource is not extant on or known to this server"}; {405, 'method not allowed', "the method you have attempted on this resource is not meaningful"}; {406, 'not acceptable', "none of the suggested content types are a viable representation of this resource"}; {500, 'internal server error', "parsav did a fucksy wucksy"}; } for i,v in ipairs(stderrors) do emit quote case uint16([v[1]]) then self:complain([v]) end end end end else self:complain(500,'unknown error','an unrecognized error was thrown. this is a bug') end end terra convo:confirm(title: pstring, msg: pstring, cancel: pstring) var conf = data.view.confirm { title = title; query = msg; cancel = cancel; } var ti: lib.str.acc ti:pcompose(&self.srv.pool,'confirm :: ', title) var body = conf:poolstr(&self.srv.pool) -- defer body:free() var cf = [convo.page] { title = ti:finalize(); class = 'query'; body = body; cache = false; } self:stdpage(cf) --cf.title:free() end convo.methods.assertpow = macro(function(self, pow) return quote var ok = true if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then ok = false self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action']) end in ok end end) local pstr2mg, mg2pstr do -- aaaaaaaaaaaaaaaaaaaaaaaa mgstr = lib.util.find(lib.net.mg_http_message.entries, function(v) if v.field == 'body' or v[1] == 'body' then return v.type end end) terra pstr2mg(p: pstring): mgstr return mgstr { ptr = p.ptr, len = p.ct } end terra mg2pstr(m: mgstr): pstring return pstring { ptr = m.ptr, ct = m.len } end end -- CALL ONLY ONCE PER VAR terra convo:postv_next(name: pstring, start: &pstring) if self.varbuf.ptr == nil then self.varbuf = self.srv.pool:alloc(int8, self.msg.body.len + self.msg.query.len) self.vbofs = self.varbuf.ptr end var conv = pstr2mg(@start) var o = lib.net.mg_http_get_var( &conv, name.ptr, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr) ) if o > 0 then start:advance(name.ct + o + 2) 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:postv(name: pstring) var start = mg2pstr(self.msg.body) return self:postv_next(name, &start) end terra convo:ppostv(name: pstring) var s,l = self:postv(name) return pstring { ptr = s, ct = l } end do local struct postiter { co: &convo where: pstring name: pstring } terra convo:eachpostv(name: pstring) return postiter { co = self, where = mg2pstr(self.msg.body), name = name } end postiter.metamethods.__for = function(self, body) return quote while true do var str, len = self.co:postv_next(self.name, &self.where) if str == nil then break end [ body(`pstring {str, len}) ] end end end end terra convo:getv(name: rawstring) if self.varbuf.ptr == nil then self.varbuf = self.srv.pool:alloc(int8, self.msg.query.len + self.msg.body.len) self.vbofs = self.varbuf.ptr end var o = lib.net.mg_http_get_var(&self.msg.query, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr)) if o > 0 then var r = self.vbofs self.vbofs = self.vbofs + o + 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 return convo |
Modified crypt.t from [a12c25b6dd] to [530b761d29].
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 .. 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 ... 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 |
const.maxdersz = const.maxpemsz -- FIXME this is a safe value but obvs not the correct one
local ctx = lib.pk.mbedtls_pk_context
terra ctx:free() lib.pk.mbedtls_pk_free(self) end
local struct hashalg { id: uint8 bytes: intptr }
local m = {
pemfile = uint8[const.maxpemsz];
const = const;
algsz = {
sha1 = 160/8;
sha256 = 256/8;
sha512 = 512/8;
sha384 = 384/8;
sha224 = 224/8;
................................................................................
v = v % (to - from) + from -- only works with unsigned!!
in v end
end)
terra callbacks.randomize(ctx: &opaque, dest: &uint8, sz: intptr)
return m.spray(dest,sz) end
terra m.pem(pub: bool, key: &ctx, buf: &uint8): bool
if pub then
return lib.pk.mbedtls_pk_write_pubkey_pem(key, buf, const.maxpemsz) == 0
else
return lib.pk.mbedtls_pk_write_key_pem(key, buf, const.maxpemsz) == 0
end
end
local binblob = lib.mem.ptr(uint8)
terra m.der(pub: bool, key: &ctx, buf: &uint8): binblob
var ofs: ptrdiff
if pub then
................................................................................
lib.pk.mbedtls_pk_setup(&pk, lib.pk.mbedtls_pk_info_from_type(lib.pk.MBEDTLS_PK_RSA))
var rsa = [&lib.rsa.mbedtls_rsa_context](pk.pk_ctx)
lib.rsa.mbedtls_rsa_gen_key(rsa, callbacks.randomize, nil, const.keybits, 65537)
return pk
end
terra m.loadpriv(buf: &uint8, len: intptr): lib.stat(ctx)
lib.dbg('parsing saved private key')
var pk: ctx
lib.pk.mbedtls_pk_init(&pk)
var rt = lib.pk.mbedtls_pk_parse_key(&pk, buf, len + 1, nil, 0)
if rt == 0 then
return [lib.stat(ctx)] { ok = true, val = pk }
else
lib.pk.mbedtls_pk_free(&pk)
return [lib.stat(ctx)] { ok = false }
end
end
terra m.loadpub(buf: &uint8, len: intptr): lib.stat(ctx)
lib.dbg('parsing saved key')
var pk: ctx
lib.pk.mbedtls_pk_init(&pk)
var rt = lib.pk.mbedtls_pk_parse_public_key(&pk, buf, len)
if rt == 0 then
return [lib.stat(ctx)] { ok = true, val = pk }
else
lib.pk.mbedtls_pk_free(&pk)
return [lib.stat(ctx)] { ok = false, error = rt }
end
end
|
> | | | | > | | | | | |
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 .. 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 ... 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 |
const.maxdersz = const.maxpemsz -- FIXME this is a safe value but obvs not the correct one
local ctx = lib.pk.mbedtls_pk_context
terra ctx:free() lib.pk.mbedtls_pk_free(self) end
local struct hashalg { id: uint8 bytes: intptr }
local m = {
pemfile = int8[const.maxpemsz];
derfile = uint8[const.maxdersz];
const = const;
algsz = {
sha1 = 160/8;
sha256 = 256/8;
sha512 = 512/8;
sha384 = 384/8;
sha224 = 224/8;
................................................................................
v = v % (to - from) + from -- only works with unsigned!!
in v end
end)
terra callbacks.randomize(ctx: &opaque, dest: &uint8, sz: intptr)
return m.spray(dest,sz) end
terra m.pem(pub: bool, key: &ctx, buf: &int8): bool
if pub then
return lib.pk.mbedtls_pk_write_pubkey_pem(key, [&uint8](buf), const.maxpemsz) == 0
else
return lib.pk.mbedtls_pk_write_key_pem(key, [&uint8](buf), const.maxpemsz) == 0
end
end
local binblob = lib.mem.ptr(uint8)
terra m.der(pub: bool, key: &ctx, buf: &uint8): binblob
var ofs: ptrdiff
if pub then
................................................................................
lib.pk.mbedtls_pk_setup(&pk, lib.pk.mbedtls_pk_info_from_type(lib.pk.MBEDTLS_PK_RSA))
var rsa = [&lib.rsa.mbedtls_rsa_context](pk.pk_ctx)
lib.rsa.mbedtls_rsa_gen_key(rsa, callbacks.randomize, nil, const.keybits, 65537)
return pk
end
local binblob = lib.mem.ptr(uint8)
terra m.loadpriv(buf: binblob): lib.stat(ctx)
lib.dbg('parsing saved private key')
var pk: ctx
lib.pk.mbedtls_pk_init(&pk)
var rt = lib.pk.mbedtls_pk_parse_key(&pk, buf.ptr, buf.ct, nil, 0)
if rt == 0 then
return [lib.stat(ctx)] { ok = true, val = pk }
else
lib.pk.mbedtls_pk_free(&pk)
return [lib.stat(ctx)] { ok = false, error = rt }
end
end
terra m.loadpub(buf: binblob): lib.stat(ctx)
lib.dbg('parsing saved key')
var pk: ctx
lib.pk.mbedtls_pk_init(&pk)
var rt = lib.pk.mbedtls_pk_parse_public_key(&pk, buf.ptr, buf.ct)
if rt == 0 then
return [lib.stat(ctx)] { ok = true, val = pk }
else
lib.pk.mbedtls_pk_free(&pk)
return [lib.stat(ctx)] { ok = false, error = rt }
end
end
|
Modified mgtool.t from [63607cdb2b] to [aa223ca2fb].
421 422 423 424 425 426 427 428 429 430 431 432 433 434 ... 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 ... 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
[ lib.emit(false, 1, 'usage: ', `argv[0], ' actor ', umode.type.helptxt.flags, ' <xid> <cmd> [<args>…]', umode.type.helptxt.opts, cmdhelp {
{ 'actor <xid> rank <value>', 'set an actor\'s rank to <value> (remote actors cannot exercise rank-related powers, but benefit from rank immunities)' };
{ 'actor <xid> degrade', 'alias for `actor <xid> rank 0`' };
{ 'actor <xid> bestow <epithet>', 'bestow an epithet upon an actor' };
{ 'actor <xid> instantiate', 'instantiate a remote actor, retrieving their profile and posts even if no one follows them' };
{ 'actor <xid> proscribe', 'globally ban an actor from interacting with your server' };
{ 'actor <xid> rehabilitate', 'lift a proscription on an actor' };
{ 'actor <xid> purge-all <confirm-str>', 'remove all traces of a user from the database (except local user credentials -- use \27[1mauth all purge\27[m to prevent a user from accessing the instance)' };
}) ]
return 1
end
if umode.arglist.ct >= 2 then
var degrade = lib.str.cmp(umode.arglist(1),'degrade') == 0
var xid = umode.arglist(0)
................................................................................
lib.warn('completely purging actor ', usr.ptr.xid, ' and all related content from database')
dlg:actor_purge_uid(usr.ptr.id)
lib.report('actor purged')
else goto cmderr end
else goto cmderr end
else goto cmderr end
elseif lib.str.cmp(mode.arglist(0),'user') == 0 then
var umode: pbasic umode:parse(mode.arglist.ct, &mode.arglist(0))
if umode.help then
[ lib.emit(false, 1, 'usage: ', `argv[0], ' user ', umode.type.helptxt.flags, ' <handle> <cmd> [<args>…]', umode.type.helptxt.opts, cmdhelp {
{ 'user <handle> create', 'add a new user' };
{ 'user <handle> auth <type> new', '(where applicable, managed auth only) create a new authentication token of the given type for a user' };
{ 'user <handle> auth <type> reset', '(where applicable, managed auth only) delete all of a user\'s authentication tokens of the given type and issue a new one' };
{ 'user <handle> auth (<type>|all) purge', 'delete all credentials that would allow this user to log in (where possible)' };
{ 'user <handle> (grant|revoke) (<priv>|all)', 'grant or revoke a specific power to or from a user' };
{ 'user <handle> emasculate', 'strip all administrative powers and rank from a user' };
{ 'user <handle> forgive', 'restore all default powers to a user' };
{ 'user <handle> suspend [<timespec>]', '(e.g. \27[1muser jokester suspend 5d 6h 7m 3s\27[m to suspend "jokester" for five days, six hours, seven minutes, and three seconds) suspend a user'};
}) ]
return 1
end
var handle = umode.arglist(0)
var usr = dlg:actor_fetch_xid(pstr {ptr=handle, ct=lib.str.sz(handle)})
if umode.arglist.ct == 2 and lib.str.cmp(umode.arglist(1),'create')==0 then
if usr:ref() then lib.bail('that user already exists') end
................................................................................
end
end
end
end
usr.ptr.rights.powers = newprivs
dlg:actor_save_privs(usr.ptr)
elseif lib.str.cmp(umode.arglist(1),'auth') == 0 and umode.arglist.ct == 4 then
var reset = lib.str.cmp(umode.arglist(3),'reset') == 0
if reset or lib.str.cmp(umode.arglist(3),'new') == 0 then
-- FIXME enable resetting pws for users who have
-- not logged in yet
if not usr then lib.bail('unknown handle') end
if lib.str.cmp(umode.arglist(2),'pw') == 0 then
|
> > > > > > > > > > > > > > > > > > > > > |
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 ... 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 ... 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 |
[ lib.emit(false, 1, 'usage: ', `argv[0], ' actor ', umode.type.helptxt.flags, ' <xid> <cmd> [<args>…]', umode.type.helptxt.opts, cmdhelp {
{ 'actor <xid> rank <value>', 'set an actor\'s rank to <value> (remote actors cannot exercise rank-related powers, but benefit from rank immunities)' };
{ 'actor <xid> degrade', 'alias for `actor <xid> rank 0`' };
{ 'actor <xid> bestow <epithet>', 'bestow an epithet upon an actor' };
{ 'actor <xid> instantiate', 'instantiate a remote actor, retrieving their profile and posts even if no one follows them' };
{ 'actor <xid> proscribe', 'globally ban an actor from interacting with your server' };
{ 'actor <xid> rehabilitate', 'lift a proscription on an actor' };
{ 'actor <xid> xkey [pem|der]', 'extract an actor\'s public key in either PEM or DER form' };
{ 'actor <xid> purge-all <confirm-str>', 'remove all traces of a user from the database (except local user credentials -- use \27[1mauth all purge\27[m to prevent a user from accessing the instance)' };
}) ]
return 1
end
if umode.arglist.ct >= 2 then
var degrade = lib.str.cmp(umode.arglist(1),'degrade') == 0
var xid = umode.arglist(0)
................................................................................
lib.warn('completely purging actor ', usr.ptr.xid, ' and all related content from database')
dlg:actor_purge_uid(usr.ptr.id)
lib.report('actor purged')
else goto cmderr end
else goto cmderr end
else goto cmderr end
elseif lib.str.cmp(mode.arglist(0),'user') == 0 then
if mode.arglist.ct < 3 then goto cmderr end
var umode: pbasic umode:parse(mode.arglist.ct, &mode.arglist(0))
if umode.help then
[ lib.emit(false, 1, 'usage: ', `argv[0], ' user ', umode.type.helptxt.flags, ' <handle> <cmd> [<args>…]', umode.type.helptxt.opts, cmdhelp {
{ 'user <handle> create', 'add a new user' };
{ 'user <handle> auth <type> new', '(where applicable, managed auth only) create a new authentication token of the given type for a user' };
{ 'user <handle> auth <type> reset', '(where applicable, managed auth only) delete all of a user\'s authentication tokens of the given type and issue a new one' };
{ 'user <handle> auth (<type>|all) purge', 'delete all credentials that would allow this user to log in (where possible)' };
{ 'user <handle> (grant|revoke) (<priv>|all)', 'grant or revoke a specific power to or from a user' };
{ 'user <handle> emasculate', 'strip all administrative powers and rank from a user' };
{ 'user <handle> forgive', 'restore all default powers to a user' };
{ 'user <handle> suspend [<timespec>]', '(e.g. \27[1muser jokester suspend 5d 6h 7m 3s\27[m to suspend "jokester" for five days, six hours, seven minutes, and three seconds) suspend a user'};
{ 'user <handle> xkey [pem|der]', 'extract an user\'s *private* key in either PEM or DER form' };
}) ]
return 1
end
var handle = umode.arglist(0)
var usr = dlg:actor_fetch_xid(pstr {ptr=handle, ct=lib.str.sz(handle)})
if umode.arglist.ct == 2 and lib.str.cmp(umode.arglist(1),'create')==0 then
if usr:ref() then lib.bail('that user already exists') end
................................................................................
end
end
end
end
usr.ptr.rights.powers = newprivs
dlg:actor_save_privs(usr.ptr)
elseif lib.str.cmp(umode.arglist(1),'xkey') == 0 and umode.arglist.ct == 3 then
if not usr then lib.bail('unknown handle') end
if lib.str.cmp(umode.arglist(2),'pem') == 0 then
var pk = lib.crypt.loadpriv(usr().key)
if not pk.ok then
lib.bail('could not parse key! this is probably a bug')
end
var pem: lib.crypt.pemfile
if not lib.crypt.pem(false, &pk.val, &pem[0]) then
lib.bail('could not convert key to PEM! this is probably a bug')
end
lib.io.send(1, pem, lib.str.sz(&pem[0]))
pk.val:free()
elseif lib.str.cmp(umode.arglist(2),'der') == 0 then
-- TODO avoid dumping binary to tty
lib.warn('dumping user\'s \x1b[1mprivate\x1b[m key!')
lib.io.send(1, [&int8](usr().key.ptr), usr().key.ct)
else lib.bail('invalid key format') end
elseif lib.str.cmp(umode.arglist(1),'auth') == 0 and umode.arglist.ct == 4 then
var reset = lib.str.cmp(umode.arglist(3),'reset') == 0
if reset or lib.str.cmp(umode.arglist(3),'new') == 0 then
-- FIXME enable resetting pws for users who have
-- not logged in yet
if not usr then lib.bail('unknown handle') end
if lib.str.cmp(umode.arglist(2),'pw') == 0 then
|
Modified mime.t from [b6a24abaaf] to [8a0a5cf230].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
local knowntypes = {
['text/csrc'] = {
ext = 'c', lang = 'c';
};
['text/html'] = {
ext = 'html', lang = 'html';
unsafe = true;
};
['text/x-lua'] = {
ext = 'lua', lang = 'lua';
};
['text/markdown'] = {
formatter = 'smackdown';
ext = 'md', doc = true;
};
}
|
> < | < > | | | | < < > > | > > > > > > < > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local knowntypes = { html = { ext = 'html', kind = 'markup', unsafe = true, id = { 'text/html'; 'application/xhtml+xml'; 'application/vnd.wap.xhtml+xml'; }; }; flash = { ext = 'swf', kind = 'vm_prog', id = 'application/x-shockwave-flash', unsafe = true, binary = true }; java = { ext = 'java', kind = 'vm_prog', id = 'application/java', unsafe = true, binary = true }; css = { ext = 'css', kind = 'lang', id = 'text/css'}; text = { ext = 'txt', kind = 'text', id = 'text/plain' }; c = { ext = 'c', kind = 'prog_lang', id = 'text/csrc' }; xml = { ext = 'xml', kind = 'markup', unsafe = true, id = 'text/xml' }; lua = { ext = 'lua', kind = 'prog_lang', id = 'text/x-lua' }; ansi = { ext = 'ans', kind = 'text', id = 'text/x-ansi', doc = true, binary = true}; mkdown = { ext = 'md', kind = 'text', doc = true; id = 'text/markdown'; formatter = 'smackdown'; }; json = { ext = 'json', kind = 'lang', id = { 'application/json'; 'application/activity+json'; 'application/ld+json'; 'application/jrd+json'; }; }; svg = { ext = 'svg', kind = 'image', id = 'image/svg+xml' }; webp = { ext = 'webp', kind = 'image', id = 'image/webp', binary = true }; png = { ext = 'png', kind = 'image', id = 'image/png', binary = true }; jpeg = { ext = 'jpg', kind = 'image', id = 'image/jpeg', binary = true }; -- wildcard none = { id = '*/*' }; } local idcache = {} local pstr = lib.str.t local filekind = lib.enum [[none image text lang prog_lang markup vm_prog]] local struct mime { key: pstr canonical: pstr safe: bool binary: bool ext: pstr kind: filekind.t output: lib.http.mime.t } local typestore = {} for typecode, ty in pairs(knowntypes) do ty.key = typecode if type(ty.id) == 'string' then ty.id = {ty.id} end for i, mime in ipairs(ty.id) do idcache[mime] = ty end local op = lib.http.mime[typecode] if op == nil then op = lib.http.mime.none end print(typecode,op) ty.offset = #typestore typestore[#typestore + 1] = `mime { key = typecode; canonical = [ty.id[1]]; safe = [not ty.unsafe]; ext = [ty.ext or `pstr{nil,0}]; kind = [ty.kind and filekind[ty.kind] or filekind.none]; binary = [ty.binary or false]; output = [op]; } end local typedex = global(`array([typestore])) local struct mimemapping { string: pstr type: &mime } local typemap_l = {} for mime, ty in pairs(idcache) do typemap_l[#typemap_l + 1] = `mimemapping { string = mime; type = &typedex[ [ty.offset] ]; } end local typemap = global(`array([typemap_l])); return { type = mime; types = knowntypes; tbl = idcache; typedex = typedex; lookup = terra(m: pstr): &mime for i=0, [#typemap_l] do if m:cmp(typemap[i].string) then lib.io.fmt('returning type %s %u\n', typemap[i].type.key, typemap[i].type.output) return typemap[i].type end end return nil end; } |
Name change from conv.t to munge.t.
Modified parsav.t from [76fc393228] to [3c7c1240d3].
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
|
if #tbl >= 2^32 then ty = uint64 -- hey, can't be too safe
elseif #tbl >= 2^16 then ty = uint32
elseif #tbl >= 2^8 then ty = uint16 end
local o = { t = ty, members = tbl }
local strings = {}
for i, name in ipairs(tbl) do
o[name] = `[ty]([i - 1])
strings[i] = `[lib.mem.ref(int8)]{ptr=[name], ct=[#name]}
end
o._str = terra(val: ty)
var l = array([strings])
return l[val]
end
return o
end
................................................................................
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.jc = lib.loadlib('json-c','json.h')
lib.load {
'mem', 'math', 'str', 'file', 'crypt', 'ipc';
'http', 'html', 'session', 'tpl', 'store', 'acl';
'smackdown'; -- md-alike parser
'conv'; -- miscellaneous conversion/munging functions
}
local be = {}
for _, b in pairs(config.backends) do
be[#be+1] = terralib.loadfile(string.format('backend/%s.t',b))()
end
lib.store.backends = global(`array([be]))
|
|
>
|
|
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
|
if #tbl >= 2^32 then ty = uint64 -- hey, can't be too safe
elseif #tbl >= 2^16 then ty = uint32
elseif #tbl >= 2^8 then ty = uint16 end
local o = { t = ty, members = tbl }
local strings = {}
for i, name in ipairs(tbl) do
o[name] = `[ty]([i - 1])
strings[i] = `[lib.str.t]{ptr=[name], ct=[#name]}
end
o._str = terra(val: ty)
var l = array([strings])
return l[val]
end
return o
end
................................................................................
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.jc = lib.loadlib('json-c','json.h')
lib.load {
'mem', 'math', 'str', 'file', 'crypt', 'ipc';
'http', 'html', 'session', 'tpl', 'store', 'acl';
'mime'; -- mimetype database & whitelist
'smackdown'; -- md-alike parser
'munge'; -- miscellaneous conversion/munging functions
}
local be = {}
for _, b in pairs(config.backends) do
be[#be+1] = terralib.loadfile(string.format('backend/%s.t',b))()
end
lib.store.backends = global(`array([be]))
|
Modified route.t from [56f7ddd740] to [1bb0eb41f3].
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 ... 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 ... 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 ... 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 ... 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 ... 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 |
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) var rt: lib.store.notice ................................................................................ end defer post:free() -- NOP on null if path.ct == 3 then var lnk: lib.str.acc lnk:compose('/post/', path(1)) var lnkp = lnk:finalize() defer lnkp:free() if post:ref() and path(2):cmp(lib.str.lit 'snitch') then if meth_get(meth) then var ui = data.view.report { badtweet = lib.render.tweet(co, post.ptr, nil); clnk = lnkp; } co:stdpage([lib.srv.convo.page] { title = 'post :: report'; ................................................................................ end return elseif post:ref() and post(0).author ~= co.who.id then co:complain(403, 'forbidden', 'you cannot alter other people\'s posts') return elseif post:ref() and path(2):cmp(lib.str.lit 'edit') then if not co:assertpow('edit') then return end if meth_get(meth) 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) co:reroute(lnkp.ptr) end return elseif path(2):cmp(lib.str.lit 'del') then if meth_get(meth) then var conf: data.view.confirm if post:ref() then conf = data.view.confirm { title = 'delete post'; query = 'are you sure you want to delete this post?'; cancel = lnkp } else conf = data.view.confirm { title = 'cancel retweet'; query = 'are you sure you want to undo this retweet?'; cancel = '/'; } end var body = conf:poolstr(&co.srv.pool) --defer body:free() co:stdpage([lib.srv.convo.page] { title = 'post :: delete'; class = 'query'; body = body; cache = false; }) return elseif meth == method.post then var act = co:ppostv('act') if act:cmp('confirm') then if post:ref() then post().source:post_destroy(post().id) elseif rt.kind ~= 0 then co.srv:post_act_cancel(pid) end ................................................................................ co:reroute('/') -- TODO maybe return to parent or conversation if possible return else goto badop end end else goto badurl end end if post:ref() and meth == method.post then if co.aid == 0 then goto noauth end var act = co:ppostv('act') if act:cmp( 'like') and not co.srv:post_liked_uid(co.who.id,pid) then co.srv:post_like(co.who.id, pid, false) post.ptr.likes = post.ptr.likes + 1 elseif act:cmp( 'dislike') and co.srv:post_liked_uid(co.who.id,pid) then co.srv:post_like(co.who.id, pid, true) post.ptr.likes = post.ptr.likes - 1 elseif act:cmp( 'rt') then co.srv:post_retweet(co.who.id, pid, false) post.ptr.rts = post.ptr.rts + 1 elseif act:cmp( 'post') then var replytext = co:ppostv('post') var acl = co:ppostv('acl') var subj = co:ppostv('subject') if not acl then acl = 'all' end if not replytext then goto badop end var reply = lib.store.post { author = co.who.id, parent = pid; subject = subj.ptr, acl = acl.ptr, body = replytext.ptr; } reply:publish(co.srv) else goto badop end end if not post then goto badurl end lib.render.tweet_page(co, path, post.ptr) do return end ::noauth:: do co:fail(401) return end ::badurl:: do co:fail(404) return end ::badop :: do co:fail(405) return end end local terra credsec_for_uid(co: &lib.srv.convo, uid: uint64) var act = co:ppostv('act') if not act then return true end lib.dbg('handling credential action') ................................................................................ var fr = co.srv.pool:frame() var hmac = lib.crypt.hmacp(&co.srv.pool, lib.crypt.alg.sha256, co.srv.cfg.secret:blob(), nonce) if not lib.math.truncate64(hmac.ptr, hmac.ct) == noncevld then co:complain(403,'nice try','what exactly are you trying to accomplish here, buddy') return false end var pkres = lib.crypt.loadpub(rsapub.ptr,rsapub.ct+1) -- needs NUL if not pkres.ok then co:complain(400,'invalid key','the key you have supplied is not a valid PEM or DER file') return false end var pk = pkres.val defer pk:free() ................................................................................ else co:reroute_cookie('/','auth=; Path=/') end else -- hierarchical routes var path = lib.http.hier(&co.srv.pool, uri) --defer path:free() if path.ct > 1 and path(0):cmp('user') then http.actor_profile_uid(co, path) elseif path.ct > 1 and path(0):cmp('post') then http.tweet_page(co, path, meth) elseif path(0):cmp('tl') then http.timeline(co, path) elseif path(0):cmp('.well-known') then if path(1):cmp('webfinger') then if not co:matchmime(lib.http.mime.json) then goto nacc end lib.api.webfinger(co) end |
| | | | | | | | | | | | | | | | | | > | > > > > | | |
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 ... 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 ... 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 ... 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 ... 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 ... 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 |
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) 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) var rt: lib.store.notice ................................................................................ end defer post:free() -- NOP on null if path.ct == 3 then var lnk: lib.str.acc lnk:compose('/post/', path(1)) var lnkp = lnk:finalize() defer lnkp:free() if post:ref() and path(2):cmp(lib.str.lit 'snitch') then if meth_get(co.method) then var ui = data.view.report { badtweet = lib.render.tweet(co, post.ptr, nil); clnk = lnkp; } co:stdpage([lib.srv.convo.page] { title = 'post :: report'; ................................................................................ end return elseif post:ref() and post(0).author ~= co.who.id then co:complain(403, 'forbidden', 'you cannot alter other people\'s posts') return elseif post:ref() and path(2):cmp(lib.str.lit 'edit') then if not co:assertpow('edit') then return end if meth_get(co.method) then lib.render.compose(co, post.ptr, nil) return elseif co.method == 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) co:reroute(lnkp.ptr) end return elseif path(2):cmp(lib.str.lit 'del') then if meth_get(co.method) then var conf: data.view.confirm if post:ref() then conf = data.view.confirm { title = 'delete post'; query = 'are you sure you want to delete this post?'; cancel = lnkp } else conf = data.view.confirm { title = 'cancel retweet'; query = 'are you sure you want to undo this retweet?'; cancel = '/'; } end var body = conf:poolstr(&co.srv.pool) --defer body:free() co:stdpage([lib.srv.convo.page] { title = 'post :: delete'; class = 'query'; body = body; cache = false; }) return elseif co.method == method.post then var act = co:ppostv('act') if act:cmp('confirm') then if post:ref() then post().source:post_destroy(post().id) elseif rt.kind ~= 0 then co.srv:post_act_cancel(pid) end ................................................................................ co:reroute('/') -- TODO maybe return to parent or conversation if possible return else goto badop end end else goto badurl end end if post:ref() and co.method == method.post then if co.aid == 0 then goto noauth end var act = co:ppostv('act') if act:cmp('like') and not co.srv:post_liked_uid(co.who.id,pid) then co.srv:post_like(co.who.id, pid, false) post.ptr.likes = post.ptr.likes + 1 elseif act:cmp('dislike') and co.srv:post_liked_uid(co.who.id,pid) then co.srv:post_like(co.who.id, pid, true) post.ptr.likes = post.ptr.likes - 1 elseif act:cmp('rt') then co.srv:post_retweet(co.who.id, pid, false) post.ptr.rts = post.ptr.rts + 1 elseif act:cmp('post') then var replytext = co:ppostv('post') var acl = co:ppostv('acl') var subj = co:ppostv('subject') if not acl then acl = 'all' end if not replytext then goto badop end var reply = lib.store.post { author = co.who.id, parent = pid; subject = subj.ptr, acl = acl.ptr, body = replytext.ptr; } reply:publish(co.srv) else goto badop end end if not post then goto badurl end if co:matchmime(lib.http.mime.html) then lib.render.tweet_page(co, path, post.ptr) elseif co:matchmime(lib.http.mime.json) then co:json(lib.api.lp.tweet(co, post.ptr, false)) else goto notacc end do return end ::noauth:: do co:fail(401) return end ::badurl:: do co:fail(404) return end ::badop :: do co:fail(405) return end ::notacc:: do co:fail(406) return end end local terra credsec_for_uid(co: &lib.srv.convo, uid: uint64) var act = co:ppostv('act') if not act then return true end lib.dbg('handling credential action') ................................................................................ var fr = co.srv.pool:frame() var hmac = lib.crypt.hmacp(&co.srv.pool, lib.crypt.alg.sha256, co.srv.cfg.secret:blob(), nonce) if not lib.math.truncate64(hmac.ptr, hmac.ct) == noncevld then co:complain(403,'nice try','what exactly are you trying to accomplish here, buddy') return false end var pkres = lib.crypt.loadpub(binblob{rsapub.ptr,rsapub.ct+1}) -- needs NUL if not pkres.ok then co:complain(400,'invalid key','the key you have supplied is not a valid PEM or DER file') return false end var pk = pkres.val defer pk:free() ................................................................................ else co:reroute_cookie('/','auth=; Path=/') end else -- hierarchical routes var path = lib.http.hier(&co.srv.pool, uri) --defer path:free() if path.ct > 1 and path(0):cmp('user') then http.actor_profile_uid(co, path) elseif path.ct > 1 and path(0):cmp('post') then http.tweet_page(co, path) elseif path(0):cmp('tl') then http.timeline(co, path) elseif path(0):cmp('.well-known') then if path(1):cmp('webfinger') then if not co:matchmime(lib.http.mime.json) then goto nacc end lib.api.webfinger(co) end |
Modified srv.t from [68c9cc33d4] to [dafa2dc374].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... 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 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 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 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 ... 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 ... 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 ... 878 879 880 881 882 883 884 885 886 887 888 889 890 891 |
-- vim: ft=terra
local util = lib.util
local secmode = lib.enum { 'public', 'private', 'lockdown', 'isolate' }
local pstring = lib.mem.ptr(int8)
local mimetypes = {
{'html', 'text/html'};
{'json', 'application/json'};
{'json', 'application/activity+json'};
{'json', 'application/ld+json'};
{'mkdown', 'text/markdown'};
{'text', 'text/plain'};
{'ansi', 'text/x-ansi'};
}
local struct srv
local struct cfgcache {
secret: pstring
pol_sec: secmode.t
pol_reg: bool
pol_autoherald: bool
................................................................................
terra lib.store.post:publish(s: &srv)
self:comp()
self.posted = lib.osclock.time(nil)
self.discovered = self.posted
self.chgcount = 0
self.edited = 0
self.id = s:post_create(self)
return self.id
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
method: lib.http.method.t
live_last: lib.store.timepoint
uploads: lib.mem.vec(lib.http.upload)
body: lib.str.t
-- cache
ui_hue: uint16
navbar: lib.mem.ptr(int8)
actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
-- private
varbuf: lib.mem.ptr(int8)
vbofs: &int8
}
struct convo.page {
title: pstring
body: pstring
class: pstring
cache: bool
}
local usrdefs = {
str = {
['acl-follow' ] = {cfgfld = 'usrdef_pol_follow', fallback = 'local'};
['acl-follow-req'] = {cfgfld = 'usrdef_pol_follow_req', fallback = 'all'};
};
}
terra convo:matchmime(mime: lib.http.mime.t): bool
return self.reqtype == [lib.http.mime.none]
or self.reqtype == mime
end
terra convo:usercfg_str(uid: uint64, setting: pstring): pstring
var set = self.srv:actor_conf_str_get(&self.srv.pool, uid, setting)
if not set then
[(function()
local q = quote return pstring.null() end
for key, dfl in pairs(usrdefs.str) do
local rv
if dfl.cfgfld then
rv = quote
var cf = self.srv.cfg.[dfl.cfgfld]
in terralib.select(not cf, pstring([dfl.fallback]), cf) end
elseif dfl.lit then rv = dfl.lit end
q = quote
if setting:cmp([key]) then return [rv] else [q] end
end
end
return q
end)()]
else return set end
end
-- this is unfortunately necessary to work around a terra bug
-- it can't seem to handle forward-declarations of structs in C
local getpeer
do local struct strucheader {
next: &lib.net.mg_connection
mgr: &lib.net.mg_mgr
................................................................................
peer: lib.net.mg_addr
}
terra getpeer(con: &lib.net.mg_connection)
return [&strucheader](con).peer
end
end
terra convo:uid2actor_live(uid: uint64)
var actor = self.srv:actor_fetch_uid(uid)
if actor:ref() then
if self.aid ~= 0 and self.who.id ~= uid then
actor(0).relationship = self.srv:actor_rel_calc(self.who.id, uid)
else -- defensive branch
actor(0).relationship = lib.store.relationship {
agent = 0, patient = uid;
rel = [lib.store.relation.null],
recip = [lib.store.relation.null],
}
end
end
return actor
end
terra convo:uid2actor(uid: uint64)
var actor: &lib.store.actor = nil
for j = 0, self.actorcache.top do
if uid == self.actorcache(j).ptr.id then
actor = self.actorcache(j).ptr
break
end
end
if actor == nil then
actor = self.actorcache:insert(self:uid2actor_live(uid)).ptr
end
return actor
end
terra convo:rawpage(code: uint16, pg: convo.page, hdrs: lib.mem.ptr(lib.http.header))
var doc = data.view.docskel {
instance = self.srv.cfg.instance;
title = pg.title;
body = pg.body;
class = pg.class;
navlinks = self.navbar;
attr = '';
}
var attrbuf: int8[32]
if self.aid ~= 0 and self.ui_hue ~= 323 then
var hdecbuf: int8[21]
var hdec = lib.math.decstr(self.ui_hue, &hdecbuf[20])
lib.str.cpy(&attrbuf[0], ' style="--hue:')
lib.str.cpy(&attrbuf[14], hdec)
var len = &hdecbuf[20] - hdec
lib.str.cpy(&attrbuf[14] + len, '"')
doc.attr = &attrbuf[0]
end
if self.method == [lib.http.method.head]
then doc:head(self.con,code,hdrs)
else doc:send(self.con,code,hdrs)
end
end
terra convo:statpage(code: uint16, pg: convo.page)
var hdrs = array(
lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
lib.http.header { key = 'Cache-Control', value = 'no-store' }
)
self:rawpage(code,pg, [lib.mem.ptr(lib.http.header)] {
ptr = &hdrs[0];
ct = [hdrs.type.N] - lib.trn(pg.cache,1,0);
})
end
terra convo:livepage(pg: convo.page, lastup: lib.store.timepoint)
var nbuf: int8[21]
var hdrs = array(
lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
lib.http.header { key = 'Cache-Control', value = 'no-store' },
lib.http.header {
key = 'X-Live-Newest-Artifact';
value = lib.math.decstr(lastup, &nbuf[20]);
},
lib.http.header { key = 'Content-Length', value = '0' }
)
if self.live_last ~= 0 and self.live_last == lastup then
lib.net.mg_printf(self.con, 'HTTP/1.1 %s', lib.http.codestr(200))
for i = 0, [hdrs.type.N] do
lib.net.mg_printf(self.con, '%s: %s\r\n', hdrs[i].key, hdrs[i].value)
end
lib.net.mg_printf(self.con, '\r\n')
else
self:rawpage(200, pg, [lib.mem.ptr(lib.http.header)] {
ptr = &hdrs[0], ct = 3
})
end
end
terra convo:stdpage(pg: convo.page) self:statpage(200, pg) end
terra convo:bytestream_trusted(lockdown: bool, mime: pstring, data: lib.mem.ptr(uint8))
var lockhdr = "Content-Security-Policy: sandbox; default-src 'none'; form-action 'none'; navigate-to 'none';\r\n"
if not lockdown then lockhdr = "" end
lib.net.mg_printf(self.con, "HTTP/1.1 200 OK\r\nContent-Type: %.*s\r\nContent-Length: %llu\r\n%sX-Content-Options: nosniff\r\n\r\n", mime.ct, mime.ptr, data.ct + 2, lockhdr)
lib.net.mg_send(self.con, data.ptr, data.ct)
lib.net.mg_send(self.con, '\r\n', 2)
end
terra convo:json(data: pstring)
self:bytestream_trusted(false, 'application/activity+json; charset=utf-8', data:blob())
end
terra convo:bytestream(mime: pstring, data: lib.mem.ptr(uint8))
-- TODO this is not a satisfactory solution; it's a bandaid on a gaping
-- chest wound. ultimately we need to compile a whitelist of safe mime
-- types as part of mimelib, but that is no small task. for now, this
-- will keep the patient from immediately bleeding out
if mime:cmp('text/html') or
mime:cmp('text/xml') or
mime:cmp('application/xhtml+xml') or
mime:cmp('application/vnd.wap.xhtml+xml')
then -- danger will robinson
mime = 'text/plain'
elseif mime:cmp('application/x-shockwave-flash') then
mime = 'application/octet-stream'
end
self:bytestream_trusted(true, mime, data)
end
terra convo:reroute_cookie(dest: rawstring, cookie: rawstring)
var hdrs = array(
lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
lib.http.header { key = 'Location', value = dest },
lib.http.header { key = 'Set-Cookie', value = cookie }
)
var body = data.view.docskel {
instance = self.srv.cfg.instance.ptr;
title = 'rerouting';
body = 'you are being redirected';
class = 'error';
navlinks = '';
attr = '';
}
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:stra(sz: intptr) -- convenience function
var s: lib.str.acc
s:pool(&self.srv.pool,sz)
return s
end
convo.methods.qstr = macro(function(self, ...) -- convenience string builder
local exp = {...}
return `lib.str.acc{}:pcompose(&self.srv.pool, [exp]):finalize()
end)
terra convo:complain(code: uint16, title: rawstring, msg: rawstring)
if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end
if self:matchmime(lib.http.mime.html) then
var body = [convo.page] {
title = self:qstr('error :: ', title);
body = self:qstr('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>');
class = 'error';
cache = false;
}
self:statpage(code, body)
else
var pg = lib.http.page { respcode = code, body = pstring.null() }
var ctt = lib.http.mime.none
if self:matchmime(lib.http.mime.json) then ctt = lib.http.mime.json
pg.body = ([lib.tpl.mk'{"_parsav_error":@$ekind, "_parsav_error_desc":@$edesc}']
{ekind = title, edesc = msg}):poolstr(&self.srv.pool)
elseif self:matchmime(lib.http.mime.text) then ctt = lib.http.mime.text
pg.body = self:qstr('error: ',title,'\n',msg)
elseif self:matchmime(lib.http.mime.mkdown) then ctt = lib.http.mime.mkdown
pg.body = self:qstr('# error :: ',title,'\n\n',msg)
elseif self:matchmime(lib.http.mime.ansi) then ctt = lib.http.mime.ansi
pg.body = self:qstr('\27[1;31merror :: ',title,'\27[m\n',msg)
end
var cthdr = lib.http.header { 'Content-Type', 'text/plain' }
if ctt == lib.http.mime.none then
pg.headers.ct = 0
else
pg.headers = lib.typeof(pg.headers) { &cthdr, 1 }
switch ctt do
case [ctt.type](lib.http.mime.json) then
cthdr.value = 'application/json'
end
escape
for i,v in ipairs(mimetypes) do local key,mime = v[1],v[2]
if key ~= 'json' then
emit quote case [ctt.type](lib.http.mime.[key]) then cthdr.value = [mime] end end
end
end
end
end
end
pg:send(self.con)
end
end
terra convo:fail(code: uint16)
switch code do
escape
local stderrors = {
{400, 'bad request', "the action you have attempted on this resource is not meaningful"};
{401, 'unauthorized', "this resource is not available at your clearance level"};
{403, 'forbidden', "we can neither confirm nor deny the existence of this resource"};
{404, 'resource not found', "that resource is not extant on or known to this server"};
{405, 'method not allowed', "the method you have attempted on this resource is not meaningful"};
{406, 'not acceptable', "none of the suggested content types are a viable representation of this resource"};
{500, 'internal server error', "parsav did a fucksy wucksy"};
}
for i,v in ipairs(stderrors) do
emit quote case uint16([v[1]]) then
self:complain([v])
end end
end
end
else self:complain(500,'unknown error','an unrecognized error was thrown. this is a bug')
end
end
terra convo:confirm(title: pstring, msg: pstring, cancel: pstring)
var conf = data.view.confirm {
title = title;
query = msg;
cancel = cancel;
}
var ti: lib.str.acc ti:pcompose(&self.srv.pool,'confirm :: ', title)
var body = conf:poolstr(&self.srv.pool) -- defer body:free()
var cf = [convo.page] {
title = ti:finalize();
class = 'query';
body = body; cache = false;
}
self:stdpage(cf)
--cf.title:free()
end
convo.methods.assertpow = macro(function(self, pow)
return quote
var ok = true
if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then
ok = false
self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action'])
end
in ok end
end)
local pstr2mg, mg2pstr
do -- aaaaaaaaaaaaaaaaaaaaaaaa
mgstr = lib.util.find(lib.net.mg_http_message.entries, function(v)
if v.field == 'body' or v[1] == 'body' then return v.type end
end)
terra pstr2mg(p: pstring): mgstr
return mgstr { ptr = p.ptr, len = p.ct }
end
terra mg2pstr(m: mgstr): pstring
return pstring { ptr = m.ptr, ct = m.len }
end
end
-- CALL ONLY ONCE PER VAR
terra convo:postv_next(name: pstring, start: &pstring)
if self.varbuf.ptr == nil then
self.varbuf = self.srv.pool:alloc(int8, self.msg.body.len + self.msg.query.len)
self.vbofs = self.varbuf.ptr
end
var conv = pstr2mg(@start)
var o = lib.net.mg_http_get_var(
&conv,
name.ptr, self.vbofs,
self.varbuf.ct - (self.vbofs - self.varbuf.ptr)
)
if o > 0 then
start:advance(name.ct + o + 2)
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:postv(name: pstring)
var start = mg2pstr(self.msg.body)
return self:postv_next(name, &start)
end
terra convo:ppostv(name: pstring)
var s,l = self:postv(name)
return pstring { ptr = s, ct = l }
end
do
local struct postiter { co: &convo where: pstring name: pstring }
terra convo:eachpostv(name: pstring)
return postiter { co = self, where = mg2pstr(self.msg.body), name = name }
end
postiter.metamethods.__for = function(self, body)
return quote
while true do
var str, len = self.co:postv_next(self.name, &self.where)
if str == nil then break end
[ body(`pstring {str, len}) ]
end
end
end
end
terra convo:getv(name: rawstring)
if self.varbuf.ptr == nil then
self.varbuf = self.srv.pool:alloc(int8, self.msg.query.len + self.msg.body.len)
self.vbofs = self.varbuf.ptr
end
var o = lib.net.mg_http_get_var(&self.msg.query, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr))
if o > 0 then
var r = self.vbofs
self.vbofs = self.vbofs + o + 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 route = {} -- these are defined in route.t, as they need access to renderers
terra route.dispatch_http :: {&convo, lib.mem.ptr(int8)} -> {}
local mimevar = symbol(lib.mem.ref(int8))
local mimeneg = `lib.http.mime.none
for i, t in ipairs(mimetypes) do
local name, mime = t[1], t[2]
mimeneg = quote
var ret: lib.http.mime.t
if lib.str.ncmp(mimevar.ptr, mime, lib.math.biggest(mimevar.ct, [#mime])) == 0 then
ret = [lib.http.mime[name]]
else ret = [mimeneg] end
in ret end
end
local handle = {
http = terra(con: &lib.net.mg_connection, event_kind: int, event: &opaque, userdata: &opaque)
var server = [&srv](userdata)
var mgpeer = getpeer(con)
-- var pbuf: int8[128]
-- the peer property is currently broken and there is precious
................................................................................
co.body.ptr = msg.body.ptr co.body.ct = msg.body.len
-- first, check for an accept header. if it's there, we need to
-- iterate over the values and pick the highest-priority one
do var acc = lib.http.findheader(msg, 'Accept')
-- TODO handle q-value
if acc ~= nil and acc.ptr ~= nil then
var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr }
var i = 0 while i < acc.ct do
if acc.ptr[i] == @',' or acc.ptr[i] == @';' then
mimevar.ct = (acc.ptr+i) - mimevar.ptr
var t = [mimeneg]
if t ~= lib.http.mime.none then
co.reqtype = t
goto foundtype
end
if acc.ptr[i] == @';' then -- fast-forward over q
for j=i+1,acc.ct do i=j
if acc.ptr[j] == @',' then break end
end
................................................................................
mimevar.ptr = acc.ptr + i + 1
end
i=i+1
end
if co.reqtype == lib.http.mime.none then
mimevar.ct = acc.ct - (mimevar.ptr - acc.ptr)
co.reqtype = [mimeneg]
if co.reqtype == lib.http.mime.none then
co.reqtype = lib.http.mime.html
end
end
else co.reqtype = lib.http.mime.html end
::foundtype::end
-- we need to check if there's any cookies sent with the request,
-- and if so, whether they contain any credentials. this will be
-- used to set the auth parameters in the http conversation
var cookies_p = lib.http.findheader(msg, 'Cookie')
if cookies_p ~= nil and cookies_p.ptr ~= nil then
................................................................................
end
bsr:free()
upmap:free()
end
end
end
route.dispatch_http(&co, uri)
::fail::
if co.uploads.run > 0 then
for i=0,co.uploads.sz do
co.uploads(i).filename:free()
co.uploads(i).field:free()
|
< < < < < < < < < > > < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > | | | | | | | > > |
1 2 3 4 5 6 7 8 9 10 11 ... 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 ... 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 ... 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 ... 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
-- 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: pstring
pol_sec: secmode.t
pol_reg: bool
pol_autoherald: bool
................................................................................
terra lib.store.post:publish(s: &srv)
self:comp()
self.posted = lib.osclock.time(nil)
self.discovered = self.posted
self.chgcount = 0
self.edited = 0
self.uri = nil -- only for foreign posts
self.convoheaduri = nil -- ditto
self.id = s:post_create(self)
return self.id
end
local convo = terralib.loadfile 'convo.t'(srv)
-- this is unfortunately necessary to work around a terra bug
-- it can't seem to handle forward-declarations of structs in C
local getpeer
do local struct strucheader {
next: &lib.net.mg_connection
mgr: &lib.net.mg_mgr
................................................................................
peer: lib.net.mg_addr
}
terra getpeer(con: &lib.net.mg_connection)
return [&strucheader](con).peer
end
end
local route = {} -- these are defined in route.t, as they need access to renderers
terra route.dispatch_http :: {&convo, lib.mem.ptr(int8)} -> {}
local handle = {
http = terra(con: &lib.net.mg_connection, event_kind: int, event: &opaque, userdata: &opaque)
var server = [&srv](userdata)
var mgpeer = getpeer(con)
-- var pbuf: int8[128]
-- the peer property is currently broken and there is precious
................................................................................
co.body.ptr = msg.body.ptr co.body.ct = msg.body.len
-- first, check for an accept header. if it's there, we need to
-- iterate over the values and pick the highest-priority one
do var acc = lib.http.findheader(msg, 'Accept')
-- TODO handle q-value
if acc ~= nil and acc.ptr ~= nil then
var mimevar = [pstring] { ptr = acc.ptr }
lib.dbg('accept header is ', {acc.ptr,acc.ct})
var i = 0 while i < acc.ct do
if acc.ptr[i] == @',' or acc.ptr[i] == @';' then
mimevar.ct = (acc.ptr+i) - mimevar.ptr
var mk = lib.mime.lookup(mimevar)
if mk ~= nil and mk.output ~= lib.http.mime.none then
co.reqtype = mk.output
goto foundtype
end
if acc.ptr[i] == @';' then -- fast-forward over q
for j=i+1,acc.ct do i=j
if acc.ptr[j] == @',' then break end
end
................................................................................
mimevar.ptr = acc.ptr + i + 1
end
i=i+1
end
if co.reqtype == lib.http.mime.none then
mimevar.ct = acc.ct - (mimevar.ptr - acc.ptr)
var mk = lib.mime.lookup(mimevar)
if mk ~= nil and mk.output ~= lib.http.mime.none then
co.reqtype = mk.output
end
end
end
::foundtype::end
-- we need to check if there's any cookies sent with the request,
-- and if so, whether they contain any credentials. this will be
-- used to set the auth parameters in the http conversation
var cookies_p = lib.http.findheader(msg, 'Cookie')
if cookies_p ~= nil and cookies_p.ptr ~= nil then
................................................................................
end
bsr:free()
upmap:free()
end
end
end
var mtt = lib.http.mime._str(co.reqtype)
lib.dbg('routing with negotiated type of ', {mtt.ptr,mtt.ct})
route.dispatch_http(&co, uri)
::fail::
if co.uploads.run > 0 then
for i=0,co.uploads.sz do
co.uploads(i).filename:free()
co.uploads(i).field:free()
|
Modified store.t from [33ecd773b1] to [6e43eba049].
213
214
215
216
217
218
219
220
221
222
223
224
225
226
...
489
490
491
492
493
494
495
496
497
498
499
500
501
502
|
elseif self.mode == 3 then
return 0,0,self.to_idx,self.from_idx
else lib.bail('invalid mode on timeline range!') end
end
struct m.post {
id: uint64
author: uint64
subject: str
body: str
acl: str
posted: m.timepoint
discovered: m.timepoint
edited: m.timepoint
................................................................................
circle_destroy: {&m.source, uint64, uint64} -> {}
circle_members_fetch_cid: {&m.source, &lib.mem.pool, uint64} -> lib.mem.ptr(uint64)
circle_members_fetch_name: {&m.source, &lib.mem.pool, uint64, pstring} -> lib.mem.ptr(uint64)
circle_members_add_uid: {&m.source, uint64, uint64} -> {}
circle_members_del_uid: {&m.source, uint64, uint64} -> {}
circle_memberships_uid: {&m.source, &lib.mem.pool, uint64, uint64} -> lib.mem.ptr(m.circle)
thread_latest_arrival_calc: {&m.source, uint64} -> m.timepoint
artifact_instantiate: {&m.source, lib.mem.ptr(uint8), lib.mem.ptr(int8)} -> uint64
-- instantiate an artifact in the database, either installing a new
-- artifact or returning the id of an existing artifact with the same hash
-- artifact: bytea
-- mime: pstring
|
>
>
>
>
>
>
|
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
|
elseif self.mode == 3 then
return 0,0,self.to_idx,self.from_idx
else lib.bail('invalid mode on timeline range!') end
end
struct m.post {
id: uint64
uri: str
author: uint64
subject: str
body: str
acl: str
posted: m.timepoint
discovered: m.timepoint
edited: m.timepoint
................................................................................
circle_destroy: {&m.source, uint64, uint64} -> {}
circle_members_fetch_cid: {&m.source, &lib.mem.pool, uint64} -> lib.mem.ptr(uint64)
circle_members_fetch_name: {&m.source, &lib.mem.pool, uint64, pstring} -> lib.mem.ptr(uint64)
circle_members_add_uid: {&m.source, uint64, uint64} -> {}
circle_members_del_uid: {&m.source, uint64, uint64} -> {}
circle_memberships_uid: {&m.source, &lib.mem.pool, uint64, uint64} -> lib.mem.ptr(m.circle)
thread_top_find: {&m.source, uint64} -> uint64
-- NOTE: this won't work if conversations are broken across multiple data sources!
-- if this is a thing that's likely to happen, the overlord-side wrapper for this
-- function (srv.t) should implement a more sophisticated algorithm over all the
-- data sources, instead of just stopping when one parent is found
thread_latest_arrival_calc: {&m.source, uint64} -> m.timepoint
artifact_instantiate: {&m.source, lib.mem.ptr(uint8), lib.mem.ptr(int8)} -> uint64
-- instantiate an artifact in the database, either installing a new
-- artifact or returning the id of an existing artifact with the same hash
-- artifact: bytea
-- mime: pstring
|
Modified str.t from [072253bf19] to [bff3416286].
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 |
var add, cont = disemvowel_codepoint(cur) if add:ref() then acc:ppush(add) end cur = cont end return acc:finalize() end terra m.qesc(pool: &lib.mem.pool, str: m.t, wrap: bool): m.t -- escape double-quotes var a: m.acc a:pool(pool, str.ct + str.ct/2) if wrap then a:lpush '"' end for i=0, str.ct do if str(i) == @'"' then a:lpush '\\"' elseif str(i) == @'\\' then a:lpush '\\\\' elseif str(i) < 0x20 then -- for json var hex = lib.math.hexbyte(str(i)) a:lpush('\\u00'):push(&hex[0], 2) else a:push(str.ptr + i,1) end end if wrap then a:lpush '"' end return a:finalize() end return m |
| < | | | > > | | | > > > > > > > > > > > > |
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 |
var add, cont = disemvowel_codepoint(cur) if add:ref() then acc:ppush(add) end cur = cont end return acc:finalize() end terra m.acc:qesc(str: m.t, wrap: bool) -- escape double-quotes if wrap then self:lpush '"' end for i=0, str.ct do if str(i) == @'"' then self:lpush '\\"' elseif str(i) == @'\\' then self:lpush '\\\\' elseif str(i) == @'\n' then self:lpush '\\n' elseif str(i) == @'\t' then self:lpush '\\t' elseif str(i) < 0x20 then -- for json var hex = lib.math.hexbyte(str(i)) self:lpush('\\u00'):push(&hex[0], 2) else self:push(str.ptr + i,1) end end if wrap then self:lpush '"' end return self end terra m.qesc(pool: &lib.mem.pool, str: m.t, wrap: bool): m.t -- convenience function var a: m.acc a:pool(pool, 2 + str.ct + str.ct/2) a:qesc(str,wrap) return a:finalize() end terra m.acc:qpush(str: m.t) -- convenience adaptor return self:qesc(str, false) end return m |
Modified tpl.t from [b153c48352] to [f930d7a874].
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
str = str:gsub('%s+[\n$]','')
str = str:gsub('\n','')
str = str:gsub('</a><a ','</a> <a ') -- keep nav links from getting smooshed
str = str:gsub(tplchar .. '%?([-%w]+)', function(file)
if not docs[file] then docs[file] = data.doc[file] end
return string.format('<a href="#help-%s" class="help">?</a>', file)
end)
for start, mode, key, stop in string.gmatch(str,'()'..tplchar..'([+:!$#%^]?)([-a-zA-Z0-9_]+):?()') do
if string.sub(str,start-1,start-1) ~= '\\' then
segs[#segs+1] = string.sub(str,last,start-1)
fields[#segs] = { key = key:gsub('-','_'), mode = (mode ~= '' and mode or nil) }
last = stop
end
end
segs[#segs+1] = string.sub(str,last)
for i, s in ipairs(segs) do
segs[i] = string.gsub(s, '\\'..tplchar, tplchar_o)
constlen = constlen + string.len(segs[i])
end
for n,d in pairs(docs) do
................................................................................
senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
appenders[#appenders+1] = quote [accumulator]:push([seg], [#seg]) end
if fields[idx] and fields[idx].mode then
local f = fields[idx]
local fp = `symself.[f.key]
local sanexp
local nulexp
if f.mode == '$' then sanexp = `lib.str.qesc(pool, fp, true)
elseif f.mode == '+' then sanexp = `lib.str.qesc(pool, fp, false)
elseif f.mode == '#' then
sanexp = quote
var ibuf: int8[21]
var ptr = lib.math.decstr(fp, &ibuf[20])
in pstr {ptr=ptr, ct=&ibuf[20] - ptr} end
elseif f.mode == '^' then
|
>
>
>
|
>
>
|
|
>
>
>
|
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
...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
str = str:gsub('%s+[\n$]','')
str = str:gsub('\n','')
str = str:gsub('</a><a ','</a> <a ') -- keep nav links from getting smooshed
str = str:gsub(tplchar .. '%?([-%w]+)', function(file)
if not docs[file] then docs[file] = data.doc[file] end
return string.format('<a href="#help-%s" class="help">?</a>', file)
end)
local detritus = ""
for start, mode, key, stop in string.gmatch(str,'()'..tplchar..'([+:!$#%^]?)([-a-zA-Z0-9_]+):?()') do
if string.sub(str,start-1,start-1) ~= '\\' then
local suffix = ""
if mode == '$' then suffix = '"' end
segs[#segs+1] = detritus .. string.sub(str,last,start-1) .. suffix
detritus = ''
fields[#segs] = { key = key:gsub('-','_'), mode = (mode ~= '' and mode or nil) }
last = stop
if mode == '$' then detritus = '"' end
end
end
segs[#segs+1] = detritus .. string.sub(str,last)
for i, s in ipairs(segs) do
segs[i] = string.gsub(s, '\\'..tplchar, tplchar_o)
constlen = constlen + string.len(segs[i])
end
for n,d in pairs(docs) do
................................................................................
senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
appenders[#appenders+1] = quote [accumulator]:push([seg], [#seg]) end
if fields[idx] and fields[idx].mode then
local f = fields[idx]
local fp = `symself.[f.key]
local sanexp
local nulexp
if f.mode == '$' then sanexp = `lib.str.qesc(pool, fp, false)
-- we use the detritus mechanism rather than the quote-wrap mechanism bc, apart
-- from being faster, 0-length strings cannot be sanitized into -- >0-length
-- strings due to how nullity is indicated (to wit, if fp == 0, ptr can be wild)
elseif f.mode == '+' then sanexp = `lib.str.qesc(pool, fp, false)
elseif f.mode == '#' then
sanexp = quote
var ibuf: int8[21]
var ptr = lib.math.decstr(fp, &ibuf[20])
in pstr {ptr=ptr, ct=&ibuf[20] - ptr} end
elseif f.mode == '^' then
|