-- vim: ft=terra
local r = lib.srv.route
local method = lib.http.method
local pstring = lib.mem.ptr(int8)
local rstring = lib.mem.ref(int8)
local binblob = lib.mem.ptr(uint8)
local hpath = lib.mem.ptr(rstring)
local http = {}
terra meth_get(meth: method.t) return (meth == method.get) or (meth == method.head) end
terra http.actor_profile(co: &lib.srv.convo, actor: &lib.store.actor, meth: method.t)
var rel: lib.store.relationship
if co.aid ~= 0 then
rel = co.srv:actor_rel_calc(co.who.id, actor.id)
if meth == method.post then
var act = co:ppostv('act')
if act:cmp(lib.str.plit 'follow') and not rel.rel.follow() then
if rel.recip.block() then
co:complain(403,'blocked','you cannot follow a user you are blocked by') return
end
(rel.rel.follow << true)
co.srv:actor_rel_create([lib.store.relation.idvmap.follow], co.who.id, actor.id)
elseif act:cmp(lib.str.plit 'unfollow') and rel.rel.follow() then
(rel.rel.follow << false)
co.srv:actor_rel_destroy([lib.store.relation.idvmap.follow], co.who.id, actor.id)
end
end
else
rel.rel:clear()
rel.recip:clear()
end
lib.render.user_page(co, actor, &rel)
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)
elseif handle.ct + 2 < uri.ct then uri:advance(handle.ct + 2) end
lib.dbg('looking up user by xid "', {handle.ptr,handle.ct} ,'", path: ', {uri.ptr,uri.ct})
var path = lib.http.hier(uri) defer path:free()
for i=0,path.ct do
lib.dbg('got path component ', {path.ptr[i].ptr, path.ptr[i].ct})
end
var actor = co.srv:actor_fetch_xid(handle)
if actor.ptr == nil then
co:complain(404,'no such user','no such user known to this server')
return
end
defer actor:free()
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)
if not ok then
co:complain(400, 'bad user ID', 'that user ID is not valid')
return
end
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, lib.str.plit(nil))
elseif meth == method.post then
var usn, usnl = co:postv('user')
var am, aml = co:postv('authmethod')
var chrs, chrsl = co:postv('response')
var cs, authok = co.srv:actor_auth_how(co.peer, usn)
var act = co.srv:actor_fetch_xid([lib.mem.ptr(int8)] {
ptr = usn, ct = usnl
})
if authok == false then
lib.render.login(co, nil, nil, lib.str.plit'access denied')
return
end
var fakeact = false
var fakeactor: lib.store.actor
if act.ptr == nil then
-- the user is known to us but has not yet claimed an
-- account on the server. create a template for the
-- account that will be created once they log in
fakeact = true
fakeactor = lib.store.actor {
id = 0, handle = usn, nym = nil;
origin = 0, bio = nil;
key = [lib.mem.ptr(uint8)] {ptr=nil, ct=0};
epithet = nil;
}
act.ct = 1
act.ptr = &fakeactor
act.ptr.rights = lib.store.rights_default()
end
if am == nil then
-- pick an auth method
lib.render.login(co, act.ptr, &cs, lib.str.plit(nil))
else var aid: uint64 = 0
lib.dbg('authentication attempt beginning')
-- attempt login with provided method
if lib.str.ncmp('pw', am, lib.math.biggest(2,aml)) == 0 and chrs ~= nil then
aid = co.srv:actor_auth_pw(co.peer,
[lib.mem.ptr(int8)]{ptr=usn,ct=usnl},
[lib.mem.ptr(int8)]{ptr=chrs,ct=chrsl})
elseif lib.str.ncmp('otp', am, lib.math.biggest(2,aml)) == 0 and chrs ~= nil then
lib.dbg('using otp auth')
-- ยทยทยท --
else lib.dbg('invalid auth method') end
-- error out
if aid == 0 then
lib.render.login(co, nil, nil, lib.str.plit 'authentication failure')
else
co:installkey('/',aid)
end
end
if act.ptr ~= nil and fakeact == false then act:free() end
else
::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end
end
return
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
--co:complain(403,'insufficient privileges','you lack the <strong>post</strong> power and cannot perform this action')
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;
parent = 0;
}
var newid = p:publish(co.srv)
var idbuf: int8[lib.math.shorthand.maxlen]
var idlen = lib.math.shorthand.gen(newid, idbuf)
var redirto: lib.str.acc redirto:compose('/post/',{idbuf,idlen}) defer redirto:free()
co:reroute(redirto.buf)
end
end
terra http.timeline(co: &lib.srv.convo, mode: hpath)
lib.render.timeline(co,lib.trn(mode.ptr == nil, rstring{ptr=nil}, mode.ptr[1]))
end
terra http.documentation(co: &lib.srv.convo, path: hpath)
if path.ct == 2 then
lib.render.docpage(co,path(1))
elseif path.ct == 1 then
lib.render.docpage(co, rstring.null())
else
co:complain(404, 'no such documentation', 'invalid documentation URL')
end
end
terra http.tweet_page(co: &lib.srv.convo, path: hpath, meth: method.t)
var pid, ok = lib.math.shorthand.parse(path(1).ptr, path(1).ct)
if not ok then
co:complain(400, 'bad post ID', 'that post ID is not valid')
return
end
var post = co.srv:post_fetch(pid)
var rt: lib.store.notice
if not post then
rt = co.srv:post_act_fetch_notice(pid)
if rt.kind ~= lib.store.noticetype.rt then
co:complain(404, 'post not found', 'no such post is known to this server')
return
elseif rt.who ~= co.who.id then
co:complain(403, 'forbidden', 'you cannot cancel other people\'s retweets')
return
end
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 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 = lib.str.plit 'delete post';
query = lib.str.plit 'are you sure you want to delete this post?';
cancel = lnkp
}
else
conf = data.view.confirm {
title = lib.str.plit 'cancel retweet';
query = lib.str.plit 'are you sure you want to undo this retweet?';
cancel = lib.str.plit'/';
}
end
var body = conf:tostr() defer body:free()
co:stdpage([lib.srv.convo.page] {
title = lib.str.plit 'post :: delete';
class = lib.str.plit 'query';
body = body; cache = false;
})
return
elseif meth == method.post then
var act = co:ppostv('act')
if act:cmp(lib.str.plit 'confirm') then
if post:ref() then
post(0).source:post_destroy(post(0).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(lib.str.plit '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(lib.str.plit '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(lib.str.plit 'rt') then
co.srv:post_retweet(co.who.id, pid, false)
post.ptr.rts = post.ptr.rts + 1
elseif act:cmp(lib.str.plit 'post') then
var replytext = co:ppostv('post')
var acl = co:ppostv('acl')
var subj = co:ppostv('subject')
if not acl then acl = lib.str.plit '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
::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')
lib.dbg('showing credentials')
if act:cmp(lib.str.plit 'invalidate') then
lib.dbg('setting user\'s cookie validation time to now')
co.who.source:auth_sigtime_user_alter(uid, lib.osclock.time(nil))
-- the current session has been invalidated as well, so we need to immediately install a new authentication cookie with the same aid so the user doesn't need to log back in all over again
co:installkey('/conf/sec',co.aid)
return
elseif act:cmp(lib.str.plit 'newcred') then
var cmt = co:ppostv('comment')
var pw = co:ppostv('newpw')
var aid: uint64 = 0
if pw:ref() then
var cpw = co:ppostv('rptpw')
if not pw:cmp(cpw) then
co:complain(400,'enrollment failure','the passwords you supplied do not match')
return
end
aid = co.srv:auth_attach_pw(uid, false, pw, cmt)
else
var key = co:ppostv('newkey')
if key:ref() then
end
end
if aid ~= 0 then
lib.dbg('setting credential restrictions')
var privs = [(function()
local check = quote end
local me = symbol(lib.store.privset)
for i,v in ipairs(lib.store.privset.members) do
check = quote [check]
var val = co:pgetv(['allow-' .. v])
if val:ref() and val:cmp(lib.str.plit'on')
then ([me].[v] << true)
else ([me].[v] << false)
end
end
end
return quote
var [me]
[check]
in [me] end
end)()]
privs:dump()
if privs:sz() > 0 then
lib.dbg('installing credential restrictions')
lib.io.fmt('on priv %llu\n',aid)
co.srv:auth_privs_set(aid, privs)
end
lib.dbg('setting netmask restrictions')
var nm = co:pgetv('netmask')
end
co:reroute('?')
return
end
co:complain(400,'bad request','the operation you have requested is not meaningful in this context')
end
terra http.configure(co: &lib.srv.convo, path: hpath, meth: method.t)
var msg = pstring.null()
-- first things first, do priv checks
if path.ct >= 2 then
if not co.who.rights.powers.config() and (
path(1):cmp(lib.str.lit 'srv') or
path(1):cmp(lib.str.lit 'badge') or
path(1):cmp(lib.str.lit 'emoji')
) then goto nopriv
elseif not co.who.rights.powers.rebrand() and (
path(1):cmp(lib.str.lit 'brand')
) then goto nopriv
elseif not co.who.rights.powers.account() and (
path(1):cmp(lib.str.lit 'profile') or
path(1):cmp(lib.str.lit 'sec') or
path(1):cmp(lib.str.lit 'avi') or
path(1):cmp(lib.str.lit 'ui')
) then goto nopriv
elseif not co.who.rights.powers:affect_users() and (
path(1):cmp(lib.str.lit 'users')
) then goto nopriv end
end
if meth == method.post and path.ct >= 1 then
var user_refresh = false var fail = false
if path(1):cmp(lib.str.lit 'profile') then
lib.dbg('updating profile')
co.who.bio = co:postv('bio')._0
co.who.nym = co:postv('nym')._0
if co.who.bio ~= nil and @co.who.bio == 0 then co.who.bio = nil end
if co.who.nym ~= nil and @co.who.nym == 0 then co.who.nym = nil end
co.who.source:actor_save(co.who)
var act = co:ppostv('act')
var resethue = false
if act:ref() then
resethue = act:cmp(lib.str.plit 'reset-hue')
end
if not resethue then
var shue = co:ppostv('hue')
var nhue, okhue = lib.math.decparse(shue)
if okhue and nhue ~= co.ui_hue then
if nhue == co.srv.cfg.ui_hue
then resethue = true
else co.srv:actor_conf_int_set(co.who.id, 'ui-accent', nhue)
end
co.ui_hue = nhue
end
end
if resethue then
co.srv:actor_conf_int_reset(co.who.id, 'ui-accent')
co.ui_hue = co.srv.cfg.ui_hue
end
msg = lib.str.plit 'profile changes saved'
--user_refresh = true -- not really necessary here, actually
elseif path(1):cmp(lib.str.lit 'sec') then
credsec_for_uid(co, co.who.id)
elseif path(1):cmp(lib.str.lit 'users') then
if path.ct >= 3 then
var userid, ok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
if ok then
var usr = co.srv:actor_fetch_uid(userid)
if usr:ref() then defer usr:free()
if not co.who:overpowers(usr.ptr) then goto nopriv end
end
if path.ct == 4 then
if path(3):cmp(lib.str.lit 'cred') then
credsec_for_uid(co, userid)
end
end
end
elseif path.ct == 2 and meth == method.post then
var act = co:ppostv('act')
if act:cmp(lib.str.plit'create') then
var newname = co:ppostv('handle')
if not newname or not lib.store.actor.handle_validate(newname.ptr) then
co:complain(400,'invalid handle','the handle you have requested is not valid')
end
var tu = co.srv:actor_fetch_xid(newname)
if tu:ref() then tu:free()
co:complain(409,'handle clash','that handle conflicts with one that already exists')
return
end
var kbuf: uint8[lib.crypt.const.maxdersz]
var na = lib.store.actor.mk(&kbuf[0])
na.handle = newname.ptr
var newuid = co.srv:actor_create(&na)
var shid: int8[lib.math.shorthand.maxlen]
var shidlen = lib.math.shorthand.gen(newuid, &shid[0])
var url = lib.str.acc{}:compose('/conf/users/',pstring{&shid[0],shidlen}):finalize() defer url:free()
co:reroute(url.ptr)
return
elseif act:cmp(lib.str.plit'inst') then
else goto badop end
end
end
if user_refresh then -- refresh the user info for the renderer
var usr = co.srv:actor_fetch_uid(co.who.id)
lib.mem.heapf(co.who)
co.who = usr.ptr
end
var go,golen = co:getv('go')
if not fail and go ~= nil then
co:reroute(go)
return
end
end
lib.render.conf(co,path,msg)
do return end
::nopriv:: do co:complain(403,'insufficient privileges','you do not have the necessary powers to perform this action') return end
::badop:: do co:complain(400,'bad request','the operation you have requested is not meaningful in this context') return end
end
terra http.user_notices(co: &lib.srv.convo, meth: method.t)
if meth == method.post then
var act = co:ppostv('act')
if act:cmp(lib.str.plit'clear') then
co.srv:actor_conf_int_set(co.who.id, 'notice-clear-time', lib.osclock.time(nil))
co:reroute('/')
return
else goto badop end
end
lib.render.notices(co)
do return end
::badop:: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end
end
terra http.media_manager(co: &lib.srv.convo, path: hpath, meth: method.t, uid: uint64)
if co.aid ~= 0 and co.who.id == uid and path.ct == 2 and path(1):cmp(lib.str.lit'upload') and co.who.rights.powers.artifact() then
if meth == method.get then
var view = data.view.media_upload {
folders = ''
}
var pg = view:tostr() defer pg:free()
co:stdpage([lib.srv.convo.page] {
title = lib.str.plit'media :: upload';
class = lib.str.plit'media upload';
cache = false; body = pg;
})
elseif meth == method.post_file then
var desc = pstring.null()
var folder = pstring.null()
var mime = pstring.null()
var name = pstring.null()
var body = binblob.null()
for i=0, co.uploads.sz do var up = co.uploads.storage.ptr + i
if up.body.ct > 0 then
if up.field:cmp(lib.str.plit'desc') then
desc = up.body
elseif up.field:cmp(lib.str.plit'folder') then
folder = up.body
elseif up.field:cmp(lib.str.plit'file') then
mime = up.ctype
body = binblob {ptr = [&uint8](up.body.ptr), ct = up.body.ct}
name = up.filename
end
end
end
if not body then goto badop end
if body.ct > co.srv.cfg.maxupsz then
co:complain(403, 'file too long', "the file you have attempted to upload exceeds the maximum length permitted by this server's upload policy. if it is an image or video, try compressing it at a lower quality setting or resolution")
return
end
var id = co.srv:artifact_instantiate(body,mime)
if id == 0 then
co:complain(500,'upload failed','artifact rejected. either the server is running out of space or this file is banned from the server')
return
end
co.srv:artifact_expropriate(co.who.id,id,desc,folder)
var idbuf: int8[lib.math.shorthand.maxlen]
var idlen = lib.math.shorthand.gen(id,&idbuf[0])
var url = lib.str.acc{}:compose('/media/a/',pstring{&idbuf[0],idlen}):finalize()
co:reroute(url.ptr)
url:free()
else goto badop end
elseif co.aid ~= 0 and path.ct == 4 and path(1):cmp(lib.str.lit'a') and meth==method.post then
var act = co:ppostv('act')
if not act or not act:cmp(lib.str.plit'confirm') then goto badop end
var artid, aok = lib.math.shorthand.parse(path(2).ptr,path(2).ct)
if not aok then goto e404 end
var art = co.srv:artifact_fetch(uid,artid)
if not art then goto e404 end
defer art:free()
if path(3):cmp(lib.str.lit'avi') then
-- user wants to set avatar
co.who.avatarid = artid
co.srv:actor_save(co.who)
co:reroute('/conf/avi')
elseif path(3):cmp(lib.str.lit'del') then
co.srv:artifact_disclaim(co.who.id, artid)
co:reroute('/media')
else goto badop end
else
if meth == method.post then goto badop end
lib.render.media_gallery(co,path,uid,nil)
end
do return end
::badop:: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end
::e404:: do co:complain(404, 'artifact not found', 'no such artifact has been uploaded by this user') return end
end
do local branches = quote end
local filename, flen = symbol(&int8), symbol(intptr)
local page = symbol(lib.http.page)
local send = label()
local storage = data.stmap
for i,e in ipairs(config.embeds) do local id,mime = e[1],e[2]
local d = data.static[id]
branches = quote [branches];
if lib.str.ncmp(filename, id, lib.math.biggest([#id], flen)) == 0 then
page.headers.ptr[0].value = mime;
page.body = [lib.mem.ptr(int8)] {
ptr = storage[([i-1])].ptr;
ct = storage[([i-1])].ct;
}
goto [send]
end
end
end
terra http.static_content(co: &lib.srv.convo, [filename], [flen])
var hdrs = array(
lib.http.header{'Content-Type',nil},
lib.http.header{'Cache-Control','max-age=2592000'} -- TODO immutable?
)
var [page] = lib.http.page {
respcode = 200;
headers = [lib.mem.ptr(lib.http.header)] {
ptr = &hdrs[0], ct = 1
}
}
[branches]
do return false end
::[send]:: page:send(co.con) return true
end
end
terra http.local_avatar(co: &lib.srv.convo, handle: lib.mem.ptr(int8))
-- TODO retrieve user avatars
var usr = co.srv:actor_fetch_xid(handle)
if not usr then
goto default end
if usr(0).origin == 0 then
if usr(0).avatarid == 0 then goto default end
var avi, mime = co.srv:artifact_load(usr(0).avatarid)
if not avi then goto default end
defer avi:free() defer mime:free()
co:bytestream(mime,avi)
else
co:reroute(usr(0).avatar)
end
do return end
::default:: co:reroute('/s/default-avatar.webp')
end
terra http.file_serve_raw(co: &lib.srv.convo, id: lib.mem.ptr(int8))
var id, idok = lib.math.shorthand.parse(id.ptr, id.ct)
if not idok then goto e404 end
var data, mime = co.srv:artifact_load(id)
if not data then goto e404 end
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), 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}) 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})
elseif uri:cmp(lib.str.plit '/notices') then
if co.aid == 0 then co:reroute('/login') return end
http.user_notices(co,meth)
elseif uri:cmp(lib.str.plit '/compose') then
if co.aid == 0 then co:reroute('/login') return end
http.post_compose(co,meth)
elseif uri:cmp(lib.str.plit '/login') then
if co.aid == 0
then http.login_form(co, meth)
else co:reroute('/')
end
elseif uri:cmp(lib.str.plit '/logout') then
if co.aid == 0
then goto notfound
else co:reroute_cookie('/','auth=; Path=/')
end
else -- hierarchical routes
var path = lib.http.hier(uri) defer path:free()
if path.ct > 1 and path(0):cmp(lib.str.lit('user')) then
http.actor_profile_uid(co, path, meth)
elseif path.ct > 1 and path(0):cmp(lib.str.lit('post')) then
http.tweet_page(co, path, meth)
elseif path(0):cmp(lib.str.lit('tl')) then
http.timeline(co, path)
elseif path(0):cmp(lib.str.lit('media')) then
if co.aid == 0 then goto unauth end
http.media_manager(co, path, meth, co.who.id)
elseif path(0):cmp(lib.str.lit('doc')) then
if not meth_get(meth) then goto wrongmeth end
http.documentation(co, path)
elseif path(0):cmp(lib.str.lit('conf')) then
if co.aid == 0 then goto unauth end
http.configure(co,path,meth)
else goto notfound end
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