Overview
| Comment: | first steps towards litepub support |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
26937ca853a6373e44a47f9114b99bca |
| User & Date: | lexi on 2021-01-25 12:40:08 |
| Other Links: | manifest | tags |
Context
|
2021-01-25
| ||
| 12:41 | commit missing file check-in: bd5794c0cc user: lexi tags: trunk | |
| 12:40 | first steps towards litepub support check-in: 26937ca853 user: lexi tags: trunk | |
|
2021-01-24
| ||
| 23:18 | enable webfinger check-in: 64ae6724c2 user: lexi tags: trunk | |
Changes
Added api/lp/actor.t version [b336ed6430].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local tpl = lib.tpl.mk { sigil = '%'; body = [[{ "@context": "https://%+domain/s/litepub.jsonld", "type": "Person", "id": "%lpid", "url": "https://%+domain/@%+handle", "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, "manuallyApprovedFollowers": %locked, "inbox": "https://%+domain/api/lp/inbox/user/%uid", "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 |
Added api/lp/outbox.t version [7ed4c4752c].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local pstr = lib.str.t local terra lp_outbox(co: &lib.srv.convo, uri: pstr, here: lib.mem.ptr(lib.str.ref)) var path = lib.str.qesc(&co.srv.pool,uri,false) var json = co:stra(512) json:lpush '{"@context": "https://':ppush(co.srv.cfg.domain):lpush'/s/litepub.jsonld","id":"https://' :ppush(co.srv.cfg.domain):ppush(path) :lpush '"' var at = co:pgetv('at') lib.dbg('api path ', {here(0).ptr,here(0).ct}, ' / ', {here(1).ptr,here(1).ct}) json:lpush',"current":"https://':ppush(co.srv.cfg.domain):ppush(path):lpush'?at=top"' if not at then json:lpush',"type":"OrderedCollection","first":"https://':ppush(co.srv.cfg.domain):ppush(path):lpush'?at=top"' else if here(0):cmp 'user' and here.ct > 1 then var uid, uidok = lib.math.shorthand.parse(here(1).ptr, here(1).ct) if not uidok then goto e404 end var user = co.srv:actor_fetch_uid(uid) if not user then goto e404 end var time: lib.store.timepoint if at:cmp('top') then time = lib.osclock.time(nil) else var tp, ok = lib.math.decparse(at) if ok then time = tp end end lib.io.fmt('from-time: %llu\n', time) var posts = co.srv:post_enum_author_uid(uid, lib.store.range { mode = 1; -- time -> idx from_time = time; to_idx = 65; }) var oldest = time json:lpush',"partOf":"https://':ppush(co.srv.cfg.domain):ppush(path) :lpush'","type":"CollectionPage","orderedItems":[' if posts.sz > 0 then defer posts:free() for i=0, lib.math.smallest(posts.sz,64) do if i~=0 then json:lpush',' end json:ppush(lib.api.lp.tweet(co,posts(i).ptr,true)) oldest = lib.math.smallest(posts(i)().posted, oldest) end end json:lpush'],"totalItems":':ipush(posts.sz) if oldest ~= time and oldest > 0 and posts.sz > 64 then json:lpush',"next":"https://':ppush(co.srv.cfg.domain):ppush(path) :lpush'?at=':ipush(oldest-1):lpush'"' end else goto e404 end -- TODO end json:lpush[[}]] co:json(json:finalize()) do return end ::e404:: do co:fail(404) return end end return lp_outbox |
Added api/lp/tweet.t version [e0805dcd2f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local pstr = lib.str.t 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 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 = '', extra = ''; }):poolstr(&co.srv.pool) if act_wrap then return (wrap { domain = co.srv.cfg.domain; 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 = '', obj = tweet; }):poolstr(&co.srv.pool) else return tweet end end return lp_tweet |
Added api/webfinger.t version [c64d390bdc].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra wftpl = lib.tpl.mk [[{ "subject": @$subj, "aliases": [ @$href, @$pfp ], "links": [ { "rel": "self", "type": "application/activity+json", "href": @$href }, { "rel": "self", "type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": @$href }, { "rel": "http://webfinger.net/rel/profile-page", "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) if not svp:cmp(co.srv.cfg.domain) then goto err end end var actor = co.srv:actor_fetch_xid(acct) if not actor then goto err end do defer actor:free() if actor().origin ~= 0 then goto err end var href = co:stra(64) var pfp = co:stra(64) href:lpush'https://':ppush(co.srv.cfg.domain):lpush'/user/':shpush(actor().id) pfp:lpush'https://':ppush(co.srv.cfg.domain):lpush'/@':ppush(acct) var tp = wftpl { subj = res; href = href:finalize(); pfp = pfp:finalize(); } co:json(tp:poolstr(&co.srv.pool)) return end -- error conditions ::err:: do co:json('{}') return end end return webfinger |
Modified config.lua from [74b801cbf1] to [f9545a425d].
62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
{'icon.svg', 'image/svg+xml'};
{'padlock.svg', 'image/svg+xml'};
{'warn.svg', 'image/svg+xml'};
{'query.webp', 'image/webp'};
{'reply.webp', 'image/webp'};
{'file.webp', 'image/webp'};
{'follow.webp', 'image/webp'};
-- keep in mind before you add anything to this list: these are not
-- just files parsav can access, they are files that are *kept in
-- memory* for fast access the entire time parsav is running, and
-- which need to be loaded into memory before the program can even
-- start. it's imperative to keep these as small and few in number
-- as is realistically possible.
};
|
> > |
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
{'icon.svg', 'image/svg+xml'};
{'padlock.svg', 'image/svg+xml'};
{'warn.svg', 'image/svg+xml'};
{'query.webp', 'image/webp'};
{'reply.webp', 'image/webp'};
{'file.webp', 'image/webp'};
{'follow.webp', 'image/webp'};
{'litepub.jsonld', 'application/ld+json; charset=utf-8'};
-- keep in mind before you add anything to this list: these are not
-- just files parsav can access, they are files that are *kept in
-- memory* for fast access the entire time parsav is running, and
-- which need to be loaded into memory before the program can even
-- start. it's imperative to keep these as small and few in number
-- as is realistically possible.
};
|
Modified math.t from [12858be43d] to [60b64e2aea].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
-- vim: ft=terra
local m = {
shorthand = {maxlen = 14};
ll = {
ctpop_u8 = terralib.intrinsic('llvm.ctpop.i8', uint8 -> uint8);
};
}
local pstring = lib.mem.ptr(int8)
-- swap in place -- faster on little endian
m.netswap_ip = macro(function(ty, src, dest)
if ty:astype().type ~= 'integer' then error('bad type') end
local bytes = ty:astype().bytes
................................................................................
var o = n
for i=0,fac do n = n * o end
return n
end
terra m.shorthand.gen(val: uint64, dest: rawstring): ptrdiff
var lst = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz"
var buf: int8[m.shorthand.maxlen]
var ptr = [&int8](buf)
while val ~= 0 do
var v = val % 64
@ptr = lst[v]
ptr = ptr + 1
val = val / 64
end
|
<
<
<
>
|
|
1
2
3
4
5
6
7
8
9
10
11
12
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
-- vim: ft=terra
local m = {
shorthand = {maxlen = 14};
}
m.shorthand.t = int8[m.shorthand.maxlen]
local pstring = lib.mem.ptr(int8)
-- swap in place -- faster on little endian
m.netswap_ip = macro(function(ty, src, dest)
if ty:astype().type ~= 'integer' then error('bad type') end
local bytes = ty:astype().bytes
................................................................................
var o = n
for i=0,fac do n = n * o end
return n
end
terra m.shorthand.gen(val: uint64, dest: rawstring): ptrdiff
var lst = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz"
var buf: m.shorthand.t
var ptr = [&int8](buf)
while val ~= 0 do
var v = val % 64
@ptr = lst[v]
ptr = ptr + 1
val = val / 64
end
|
Modified parsav.t from [dd405c9e82] to [1d36a792dc].
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ... 106 107 108 109 110 111 112 113 114 115 116 117 118 119 ... 496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
for m in l:gmatch('([^:]+)') do path[#path+1]=m end
local tgt = lib
for i=1,#path-1 do
if tgt[path[i]] == nil then tgt[path[i]] = {} end
tgt = tgt[path[i]]
end
local chunk = terralib.loadfile(l:gsub(':','/') .. '.t')
if chunk ~= nil then
tgt[path[#path]:gsub('-','_')] = chunk()
print(' \27[1m[ \27[32mok\27[;1m ]\27[m')
else
print(' \27[1m[\27[31mfail\27[;1m]\27[m')
os.exit(2)
end
end
................................................................................
trn = macro(function(cond, i, e)
return quote
var c: bool = [cond]
var r: i.tree.type
if c == true then r = i else r = e end
in r end
end);
coalesce = macro(function(...)
local args = {...}
local ty = args[1].tree.type
local val = symbol(ty)
local empty
if ty.ptr_basetype then empty = `[ty]{ptr=nil,ct=0}
elseif ty.type == 'integer' then empty = `0
................................................................................
'render:conf:profile';
'render:conf:circles';
'render:conf:sec';
'render:conf:users';
'render:conf:avi';
'render:conf';
'route';
}
do
local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
terra version() lib.io.send(1, p, [#p]) end
end
|
| > > > > > > |
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ... 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 ... 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 |
for m in l:gmatch('([^:]+)') do path[#path+1]=m end
local tgt = lib
for i=1,#path-1 do
if tgt[path[i]] == nil then tgt[path[i]] = {} end
tgt = tgt[path[i]]
end
local chunk = terralib.loadfile(l:gsub(':','/') .. '.t')
if chunk ~= nil then
tgt[path[#path]:gsub('-','_')] = chunk()
print(' \27[1m[ \27[32mok\27[;1m ]\27[m')
else
print(' \27[1m[\27[31mfail\27[;1m]\27[m')
os.exit(2)
end
end
................................................................................
trn = macro(function(cond, i, e)
return quote
var c: bool = [cond]
var r: i.tree.type
if c == true then r = i else r = e end
in r end
end);
typeof = macro(function(exp) return exp.tree.type end);
coalesce = macro(function(...)
local args = {...}
local ty = args[1].tree.type
local val = symbol(ty)
local empty
if ty.ptr_basetype then empty = `[ty]{ptr=nil,ct=0}
elseif ty.type == 'integer' then empty = `0
................................................................................
'render:conf:profile';
'render:conf:circles';
'render:conf:sec';
'render:conf:users';
'render:conf:avi';
'render:conf';
'api:lp:actor';
'api:lp:tweet';
'api:lp:outbox';
'api:webfinger';
'route';
}
do
local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
terra version() lib.io.send(1, p, [#p]) end
end
|
Modified render/profile.t from [c63a8aaea6] to [74b4c77645].
195 196 197 198 199 200 201 202 203 204 205 206 207 208 ... 215 216 217 218 219 220 221 222 223 224 225 226 227 228 ... 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
end
if relationship.recip.follow() then
comments:lpush('<li style="--co:30">follows you</li>')
end
var circpanel: lib.str.acc
if co.aid ~= 0 then
circpanel = co:stra(128)
var allcircs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
if allcircs:ref() then
var mycircs = co.srv:circle_memberships_uid(&co.srv.pool, co.who.id, actor.id)
for i=0, allcircs.ct do
circpanel:lpush '<label><input type="checkbox" name="circle" value="'
................................................................................
end
end
circpanel:lpush '> '
:ppush(allcircs(i).name)
:lpush '</label>'
end
end
end
var profile = data.view.profile {
nym = fullname;
bio = bio;
xid = cs(actor.xid);
avatar = cs(actor.avatar);
................................................................................
nfollowers = sn_followers, nmutuals = sn_mutuals;
tweetday = cs(timestr);
timephrase = lib.trn(actor.origin == 0, pstr 'joined', pstr 'known since');
remarks = '';
auxbtn = auxp;
circles = circpanel:finalize();
relations = relbtns:finalize();
sanctions = sancbtns:finalize();
}
if comments.sz > 0 then profile.remarks = comments:finalize() end
var ret = profile:poolstr(&co.srv.pool)
-- auxp:free()
--if actor.bio ~= nil then bio:free() end
--if comments.sz > 0 then profile.remarks:free() end
return ret
end
return render_profile
|
> > | |
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 ... 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 ... 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
end
if relationship.recip.follow() then
comments:lpush('<li style="--co:30">follows you</li>')
end
var circpanel: lib.str.acc
var circstr = pstr.null()
if co.aid ~= 0 then
circpanel = co:stra(128)
var allcircs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
if allcircs:ref() then
var mycircs = co.srv:circle_memberships_uid(&co.srv.pool, co.who.id, actor.id)
for i=0, allcircs.ct do
circpanel:lpush '<label><input type="checkbox" name="circle" value="'
................................................................................
end
end
circpanel:lpush '> '
:ppush(allcircs(i).name)
:lpush '</label>'
end
end
circstr = circpanel:finalize()
end
var profile = data.view.profile {
nym = fullname;
bio = bio;
xid = cs(actor.xid);
avatar = cs(actor.avatar);
................................................................................
nfollowers = sn_followers, nmutuals = sn_mutuals;
tweetday = cs(timestr);
timephrase = lib.trn(actor.origin == 0, pstr 'joined', pstr 'known since');
remarks = '';
auxbtn = auxp;
circles = circstr;
relations = relbtns:finalize();
sanctions = sancbtns:finalize();
}
if comments.sz > 0 then profile.remarks = comments:finalize() end
var ret = profile:poolstr(&co.srv.pool)
-- auxp:free()
--if actor.bio ~= nil then bio:free() end
--if comments.sz > 0 then profile.remarks:free() end
return ret
end
return render_profile
|
Modified route.t from [325df84c8e] to [db07af2616].
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 ... 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 ... 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 ... 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 ... 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 ... 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 ... 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 ... 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 |
if not go or go(0) ~= @'/' then lib.render.user_page(co, actor, &rel) else co:reroute(go.ptr) end end terra http.actor_profile_xid(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t) var handle = [lib.mem.ptr(int8)] { ptr = &uri.ptr[2], ct = 0 } for i=2,uri.ct do if uri.ptr[i] == @'/' or uri.ptr[i] == 0 then handle.ct = i - 2 break end end if handle.ct == 0 then handle.ct = uri.ct - 2 uri:advance(uri.ct) ................................................................................ var actor = co.srv:actor_fetch_xid(handle) if actor.ptr == nil then co:complain(404,'no such user','no such user known to this server') return end defer actor:free() http.actor_profile(co,actor.ptr,meth) end terra http.actor_profile_uid ( co: &lib.srv.convo, path: lib.mem.ptr(lib.mem.ref(int8)), meth: method.t ) if path.ct < 2 then co:complain(404,'bad url','invalid user url') return end var uid, ok = lib.math.shorthand.parse(path.ptr[1].ptr, path.ptr[1].ct) ................................................................................ var actor = co.srv:actor_fetch_uid(uid) if actor.ptr == nil then co:complain(404, 'no such user', 'no user by that ID is known to this instance') return end defer actor:free() http.actor_profile(co,actor.ptr,meth) end terra http.login_form(co: &lib.srv.convo, meth: method.t) if meth_get(meth) then -- request a username lib.render.login(co, nil, nil, pstring.null()) elseif meth == method.post then ................................................................................ lib.render.login(co, nil, nil, 'authentication failure') else co:installkey('/',aid) end end if act.ptr ~= nil and fakeact == false then act:free() end else ::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end end return end terra http.post_compose(co: &lib.srv.convo, meth: method.t) if not co:assertpow('post') then return end --if co.who.rights.powers.post() == false then ................................................................................ if meth_get(meth) then lib.render.compose(co, nil, nil) elseif meth == method.post then var text, textlen = co:postv("post") var acl, acllen = co:postv("acl") var subj, subjlen = co:postv("subject") if text == nil or acl == nil then co:complain(405, 'invalid post', 'every post must have at least body text and an ACL') return end if subj == nil then subj = '' end var p = lib.store.post { author = co.who.id, acl = acl; body = text, subject = subj; ................................................................................ end if not post then goto badurl end lib.render.tweet_page(co, path, post.ptr) do return end ::badurl:: do co:complain(404, 'invalid URL', 'this URL does not reference extant content or functionality') return end ::badop :: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end ::noauth:: do co:complain(401, 'unauthorized', 'you have not supplied the necessary credentials to perform this operation') 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') ................................................................................ do defer data:free() defer mime:free() co:bytestream(mime,data) return end ::e404:: do co:complain(404, 'artifact not found', 'no such artifact has been uploaded to this instance') return end end local json = {} do wftpl = lib.tpl.mk [[{ "subject": @$subj, "links": [ { "rel": "self", "type": "application/ld+json", "href": @$href } ] }]] terra json.webfinger(co: &lib.srv.convo) var res = co:pgetv('resource') if (not res) or not res:startswith 'acct:' then goto err end -- technically we should look this user up in the database to make sure -- they actually exist, buuut that's costly and i doubt that's actually -- necessary for webfinger to do its job. so we cheat and just do string -- munging so lookups are as cheap as possible. TODO make sure this works -- in practice and doesn't cause any weird security problems var acct = res + 5 var svp = lib.str.find(acct, '@') if svp:ref() then acct.ct = (svp.ptr - acct.ptr) svp:advance(1) if not svp:cmp(co.srv.cfg.domain) then goto err end end var tp = wftpl { subj = res; href = co:qstr('https://', co.srv.cfg.domain, '/@', acct); } co:json(tp:poolstr(&co.srv.pool)) do return end -- error conditions ::err:: do co:json('{}') return end end end -- entry points terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t) lib.dbg('handling URI of form ', {uri.ptr,uri.ct}) co.navbar = lib.render.nav(co) -- some routes are non-hierarchical, and can be resolved with a simple strcmp -- we run through those first before giving up and parsing the URI if uri.ptr == nil or uri.ptr[0] ~= @'/' then co:complain(404, 'what the hell', 'how did you do that') elseif uri.ct == 1 then -- root if (co.srv.cfg.pol_sec == lib.srv.secmode.private or co.srv.cfg.pol_sec == lib.srv.secmode.lockdown) and co.aid == 0 then http.login_form(co, meth) else http.timeline(co, hpath {ptr=nil,ct=0}) end elseif uri.ptr[1] == @'@' then http.actor_profile_xid(co, uri, meth) elseif uri.ptr[1] == @'s' and uri.ptr[2] == @'/' and uri.ct > 3 then if not meth_get(meth) then goto wrongmeth end if not http.static_content(co, uri.ptr + 3, uri.ct - 3) then goto notfound end elseif lib.str.ncmp('/avi/', uri.ptr, 5) == 0 then http.local_avatar(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 5, ct = uri.ct - 5}) elseif lib.str.ncmp('/file/', uri.ptr, 6) == 0 then http.file_serve_raw(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 6, ct = uri.ct - 6}) ................................................................................ if co.aid == 0 then goto notfound 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, meth) 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 json.webfinger(co) end elseif path(0):cmp('media') then if co.aid == 0 then goto unauth end http.media_manager(co, path, meth, co.who.id) elseif path(0):cmp('doc') then if not meth_get(meth) then goto wrongmeth end http.documentation(co, path) elseif path(0):cmp('conf') then if co.aid == 0 then goto unauth end http.configure(co,path,meth) else goto notfound end end do return end ::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end ::notfound:: co:complain(404, 'not found', 'no such resource available') do return end ::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end end |
> > > > > > > > | | | < | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > | | > | > > > > > > > > > | | | > |
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 ... 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 ... 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 ... 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 ... 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 ... 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 ... 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 ... 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 |
if not go or go(0) ~= @'/' then lib.render.user_page(co, actor, &rel) else co:reroute(go.ptr) end end terra http.actor_dispatch_mime(co: &lib.srv.convo, actor: &lib.store.actor) if co:matchmime(lib.http.mime.html) then http.actor_profile(co,actor,co.method) elseif co:matchmime(lib.http.mime.json) then lib.api.lp.actor(co, actor) else co:fail(406) end end terra http.actor_profile_xid(co: &lib.srv.convo, uri: lib.mem.ptr(int8)) var handle = [lib.mem.ptr(int8)] { ptr = &uri.ptr[2], ct = 0 } for i=2,uri.ct do if uri.ptr[i] == @'/' or uri.ptr[i] == 0 then handle.ct = i - 2 break end end if handle.ct == 0 then handle.ct = uri.ct - 2 uri:advance(uri.ct) ................................................................................ var actor = co.srv:actor_fetch_xid(handle) if actor.ptr == nil then co:complain(404,'no such user','no such user known to this server') return end defer actor:free() http.actor_dispatch_mime(co, actor.ptr) end terra http.actor_profile_uid ( co: &lib.srv.convo, path: lib.mem.ptr(lib.mem.ref(int8)) ) if path.ct < 2 then co:complain(404,'bad url','invalid user url') return end var uid, ok = lib.math.shorthand.parse(path.ptr[1].ptr, path.ptr[1].ct) ................................................................................ var actor = co.srv:actor_fetch_uid(uid) if actor.ptr == nil then co:complain(404, 'no such user', 'no user by that ID is known to this instance') return end defer actor:free() http.actor_dispatch_mime(co, actor.ptr) end terra http.login_form(co: &lib.srv.convo, meth: method.t) if meth_get(meth) then -- request a username lib.render.login(co, nil, nil, pstring.null()) elseif meth == method.post then ................................................................................ lib.render.login(co, nil, nil, 'authentication failure') else co:installkey('/',aid) end end if act.ptr ~= nil and fakeact == false then act:free() end else ::wrongmeth:: co:fail(405) do return end end return end terra http.post_compose(co: &lib.srv.convo, meth: method.t) if not co:assertpow('post') then return end --if co.who.rights.powers.post() == false then ................................................................................ if meth_get(meth) then lib.render.compose(co, nil, nil) elseif meth == method.post then var text, textlen = co:postv("post") var acl, acllen = co:postv("acl") var subj, subjlen = co:postv("subject") if text == nil or acl == nil then co:complain(400, 'invalid post', 'every post must have at least body text and an ACL') return end if subj == nil then subj = '' end var p = lib.store.post { author = co.who.id, acl = acl; body = text, subject = subj; ................................................................................ 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') ................................................................................ do defer data:free() defer mime:free() co:bytestream(mime,data) return end ::e404:: do co:complain(404, 'artifact not found', 'no such artifact has been uploaded to this instance') return end end -- entry points terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8)) lib.dbg('handling URI of form ', {uri.ptr,uri.ct}) co.navbar = lib.render.nav(co) -- some routes are non-hierarchical, and can be resolved with a simple strcmp -- we run through those first before giving up and parsing the URI var meth = co.method -- TODO unfuck this legacy bat shit if uri.ptr == nil or uri.ptr[0] ~= @'/' then co:complain(404, 'what the hell', 'how did you do that') elseif uri.ct == 1 then -- root if (co.srv.cfg.pol_sec == lib.srv.secmode.private or co.srv.cfg.pol_sec == lib.srv.secmode.lockdown) and co.aid == 0 then http.login_form(co, meth) else http.timeline(co, hpath {ptr=nil,ct=0}) end elseif uri.ptr[1] == @'@' then http.actor_profile_xid(co, uri) elseif uri.ptr[1] == @'s' and uri.ptr[2] == @'/' and uri.ct > 3 then if not meth_get(meth) then goto wrongmeth end if not http.static_content(co, uri.ptr + 3, uri.ct - 3) then goto notfound end elseif lib.str.ncmp('/avi/', uri.ptr, 5) == 0 then http.local_avatar(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 5, ct = uri.ct - 5}) elseif lib.str.ncmp('/file/', uri.ptr, 6) == 0 then http.file_serve_raw(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 6, ct = uri.ct - 6}) ................................................................................ if co.aid == 0 then goto notfound 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 elseif path(0):cmp('api') then if path(1):cmp('parsav') then -- native API elseif path(1):cmp('v1') then -- mastodon client api :/ elseif path(1):cmp('lp') then -- litepub endpoints if path(2):cmp('outbox') then lib.api.lp.outbox(co,uri,path + 3) elseif path(2):cmp('inbox') then end else goto notfound end elseif path(0):cmp('media') then if co.aid == 0 then goto unauth end http.media_manager(co, path, meth, co.who.id) elseif path(0):cmp('doc') then if not meth_get(meth) then goto wrongmeth end http.documentation(co, path) elseif path(0):cmp('conf') then if co.aid == 0 then goto unauth end http.configure(co,path,meth) else goto notfound end end do return end ::wrongmeth:: co:fail(405) do return end ::nacc :: co:fail(406) do return end ::notfound :: co:fail(404) do return end ::unauth :: co:fail(401) do return end end |
Modified srv.t from [557555fd0b] to [68c9cc33d4].
1 2 3 4 5 6 7 8 9 10 11 ... 178 179 180 181 182 183 184 185 186 187 188 189 190 191 ... 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 ... 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 ... 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 ... 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 ... 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 |
-- 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
credmgd: bool
................................................................................
local usrdefs = {
str = {
['acl-follow' ] = {cfgfld = 'usrdef_pol_follow', fallback = 'local'};
['acl-follow-req'] = {cfgfld = 'usrdef_pol_follow_req', fallback = 'all'};
};
}
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
................................................................................
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/ld+json', 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
................................................................................
p = p + lib.session.cookie_gen(self.srv.cfg.secret, aid, lib.osclock.time(nil), p)
lib.dbg('sending cookie ',{&sesskey[0],15})
p = lib.str.ncpy(p, '; Path=/', 9)
end
self:reroute_cookie(dest, &sesskey[0])
end
terra convo:complain(code: uint16, title: rawstring, msg: rawstring)
if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end
var ti: lib.str.acc ti:compose('error :: ', title)
var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>')
var body = [convo.page] {
title = ti:finalize();
body = bo:finalize();
class = 'error';
cache = false;
}
self:statpage(code, body)
body.title:free()
body.body:free()
end
terra convo:confirm(title: pstring, msg: pstring, cancel: pstring)
var conf = data.view.confirm {
title = title;
query = msg;
cancel = cancel;
................................................................................
class = 'query';
body = body; cache = false;
}
self:stdpage(cf)
--cf.title:free()
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)
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
................................................................................
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), lib.http.method.t} -> {}
local mimetypes = {
{'html', 'text/html'};
{'json', 'application/json'};
{'json', 'application/ld+json'};
{'json', 'application/activity+json'};
{'mkdown', 'text/markdown'};
{'text', 'text/plain'};
{'ansi', 'text/x-ansi'};
}
local mimevar = symbol(lib.mem.ref(int8))
local mimeneg = `lib.http.mime.none
for i, t in ipairs(mimetypes) do
local name, mime = t[1], t[2]
mimeneg = quote
................................................................................
end
bsr:free()
upmap:free()
end
end
end
route.dispatch_http(&co, uri, co.method)
::fail::
if co.uploads.run > 0 then
for i=0,co.uploads.sz do
co.uploads(i).filename:free()
co.uploads(i).field:free()
end
|
> > > > > > > > > > > > > > > | > > > > > > > > > > > > > | | < < < | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < | < < < < < < < < < < | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ... 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 ... 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 ... 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 ... 483 484 485 486 487 488 489 490 491 492 493 494 495 496 ... 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 ... 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 |
-- 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
credmgd: 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
................................................................................
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
................................................................................
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;
................................................................................
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
................................................................................
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
................................................................................
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()
end
|
Modified str.t from [6c699847d5] to [072253bf19].
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
...
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
|
end if self.buf ~= nil and self.space > 0 then lib.mem.heapf(self.buf) end end; terra m.acc:crush() --lib.dbg('crushing string accumulator') if self.pool ~= nil then return self end -- no point unless at end of buffer self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.sz)) self.space = self.sz return self end; terra m.acc:finalize() --lib.dbg('finalizing string accumulator') ................................................................................ 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): m.t -- escape double-quotes var a: m.acc a:pool(pool, str.ct + str.ct/2) a:lpush '"' 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 a:lpush '"' return a:finalize() end return m |
<
>
|
|
|
|
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
...
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
|
end if self.buf ~= nil and self.space > 0 then lib.mem.heapf(self.buf) end end; terra m.acc:crush() if self.pool ~= nil then return self end -- no point unless at end of buffer --lib.dbg('crushing string accumulator', &self.buf[0]) self.buf = [rawstring](lib.mem.heapr_raw(self.buf, self.sz)) self.space = self.sz return self end; terra m.acc:finalize() --lib.dbg('finalizing string accumulator') ................................................................................ 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 |
Modified tpl.t from [ce74a1083d] to [2f4ebceee0].
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 .. 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 ... 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 |
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)
................................................................................
local tallyup = {quote
var [runningtally] = 1 + constlen
end}
local rec = terralib.types.newstruct(string.format('template<%s>',tplspec.id or ''))
local symself = symbol(&rec)
do local kfac = {}
local sanmode = {}
for afterseg,fld in ipairs(fields) do
if not kfac[fld.key] then
rec.entries[#rec.entries + 1] = {
field = fld.key;
type = lib.mem.ptr(int8);
}
end
kfac[fld.key] = (kfac[fld.key] or 0) + 1
sanmode[fld.key] = fld.mode == ':' and 6
or fld.mode == '!' and 5
or fld.mode == '$' and 2 or 1
end
for key, fac in pairs(kfac) do
local sanfac = sanmode[key]
tallyup[#tallyup + 1] = quote
[runningtally] = [runningtally] + ([symself].[key].ct)*fac*sanfac
end
end
end
local copiers = {}
local senders = {}
local appenders = {}
................................................................................
copiers[#copiers+1] = quote [cpypos] = lib.mem.cpy([cpypos], [&opaque]([seg]), [#seg]) end
senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
appenders[#appenders+1] = quote [accumulator]:push([seg], [#seg]) end
if fields[idx] and fields[idx].mode then
local f = fields[idx]
local fp = `symself.[f.key]
local sanexp
if f.mode == '$' then
sanexp = `lib.str.qesc(pool, fp)
else
sanexp = `lib.html.sanitize(pool, fp, [f.mode == ':'])
end
copiers[#copiers+1] = quote
if fp.ct > 0 then
var san = sanexp
[cpypos] = lib.mem.cpy([cpypos], [&opaque](san.ptr), san.ct)
--san:free()
end
end
senders[#senders+1] = quote
if fp.ct > 0 then
var san = sanexp
lib.net.mg_send([destcon], san.ptr, san.ct)
--san:free()
end
end
appenders[#appenders+1] = quote
if fp.ct > 0 then
var san = sanexp
[accumulator]:ppush(san)
--san:free()
end
end
elseif fields[idx] then
local f = fields[idx]
|
| > > | > > > | > < > | > > > > | > > | | < > > > > > > > > > > > | < > > < > | | |
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 .. 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 ... 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 |
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)
................................................................................
local tallyup = {quote
var [runningtally] = 1 + constlen
end}
local rec = terralib.types.newstruct(string.format('template<%s>',tplspec.id or ''))
local symself = symbol(&rec)
do local kfac = {}
local sanmode = {}
local types = { ['^'] = uint64, ['#'] = uint64 }
local recmap = {}
for afterseg,fld in ipairs(fields) do
if not kfac[fld.key] then
rec.entries[#rec.entries + 1] = {
field = fld.key;
type = types[fld.mode] or pstr;
}
recmap[fld.key] = rec.entries[#rec.entries]
end
kfac[fld.key] = (kfac[fld.key] or 0) + 1
sanmode[fld.key] = fld.mode == ':' and 6
or fld.mode == '!' and 5
or (fld.mode == '$' or fld.mode == '+') and 2
or fld.mode == '^' and lib.math.shorthand.maxlen
or fld.mode == '#' and 20
or 1
end
for key, fac in pairs(kfac) do
local sanfac = sanmode[key]
if recmap[key].type ~= pstr then
tallyup[#tallyup + 1] = quote
[runningtally] = [runningtally] + fac*sanfac
end
else
tallyup[#tallyup + 1] = quote
[runningtally] = [runningtally] + ([symself].[key].ct)*fac*sanfac
end
end
end
end
local copiers = {}
local senders = {}
local appenders = {}
................................................................................
copiers[#copiers+1] = quote [cpypos] = lib.mem.cpy([cpypos], [&opaque]([seg]), [#seg]) end
senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
appenders[#appenders+1] = quote [accumulator]:push([seg], [#seg]) end
if fields[idx] 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
sanexp = quote
var shbuf: lib.math.shorthand.t
var len = lib.math.shorthand.gen(fp, &shbuf[0])
in pstr {ptr=&shbuf[0],ct=len} end
else sanexp = `lib.html.sanitize(pool, fp, [f.mode == ':']) end
if f.mode == '^' or f.mode == '#' then nulexp = `true
else nulexp = `fp.ct > 0 end
copiers[#copiers+1] = quote
if [nulexp] then
var san = sanexp
[cpypos] = lib.mem.cpy([cpypos], [&opaque](san.ptr), san.ct)
--san:free()
end
end
senders[#senders+1] = quote
if [nulexp] then
var san = sanexp
lib.net.mg_send([destcon], san.ptr, san.ct)
--san:free()
end
end
appenders[#appenders+1] = quote
if [nulexp] then
var san = sanexp
[accumulator]:ppush(san)
--san:free()
end
end
elseif fields[idx] then
local f = fields[idx]
|