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
|