parsav  Diff

Differences From Artifact [325df84c8e]:

To Artifact [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