Index: mime.t ================================================================== --- mime.t +++ mime.t @@ -4,10 +4,13 @@ }; ['text/html'] = { ext = 'html', lang = 'html'; unsafe = true; }; + ['text/x-lua'] = { + ext = 'lua', lang = 'lua'; + }; ['text/markdown'] = { formatter = 'smackdown'; ext = 'md', doc = true; }; } Index: render/docpage.t ================================================================== --- render/docpage.t +++ render/docpage.t @@ -87,11 +87,12 @@ local terra render_docpage(co: &lib.srv.convo, pg: pref) var nullprivs: lib.store.powerset nullprivs:clear() if not pg then -- display index - var list: lib.str.acc list:compose('
',san,''):finalize() - san:free() - var pg = view:tostr() - view.text:free() + view.text = co:qstr('
',san,'') + --san:free() + var pg = view:poolstr(&co.srv.pool) + --view.text:free() co:stdpage([lib.srv.convo.page] { title = lib.str.plit'media :: text'; class = lib.str.plit'media viewer text'; cache = false, body = pg; }) - pg:free() + --pg:free() else co:complain(500,'bad file type','this file type is not supported') end elseif path.ct == 4 then var act = path(3) - var curl = lib.str.acc{}:compose(pfx, '/media/a/', path(2)):finalize() - defer curl:free() + var curl = co:qstr('/media/a/', path(2)) + -- defer curl:free() if act:cmp(lib.str.lit'avi') and lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then co:confirm('set avatar', 'are you sure you want this image to be your new avatar?',curl) elseif act:cmp(lib.str.lit'del') then co:confirm('delete', 'are you sure you want to permanently delete this artifact?',curl) else goto e404 end @@ -125,11 +125,11 @@ images = pstr{'',0}; pfx = pfx; } if folders.ct > 0 then - var fa: lib.str.acc fa:pool(&co.srv.pool,128) + var fa = co:stra(128) var fldr = co:pgetv('folder') for i=0,folders.ct do var ule = lib.html.urlenc(&co.srv.pool,folders(i), true) -- defer ule:free() var san = lib.html.sanitize(&co.srv.pool,folders(i), true) -- defer san:free() fa:lpush('tall, dark, and mysterious' if actor.bio ~= nil then bio = lib.smackdown.html(&co.srv.pool,cs(actor.bio),false) end var fullname = lib.render.nym(actor,0,nil,false) defer fullname:free() - var comments: lib.str.acc comments:pool(&co.srv.pool,64) + var comments = co:stra(64) if co.srv.cfg.master == actor.id then var foundertxt = lib.str.plit 'founder' if co.srv.cfg.ui_cue_founder:ref() then if co.srv.cfg.ui_cue_founder.ct == 0 -- empty string, suppress field @@ -101,10 +101,10 @@ 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 + --if comments.sz > 0 then profile.remarks:free() end return ret end return render_profile Index: route.t ================================================================== --- route.t +++ route.t @@ -14,27 +14,27 @@ 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 rel.recip.block() then - if act:cmp(lib.str.plit 'follow') or act:cmp(lib.str.plit 'subscribe') then + if act:cmp( 'follow') or act:cmp( 'subscribe') then co:complain(403,'blocked','you cannot follow a user you are blocked by') return end end - if act:cmp(lib.str.plit 'block') and not rel.rel.block() then + if act:cmp( 'block') and not rel.rel.block() then (rel.rel.block << true) ; (rel.recip.follow << false) co.srv:actor_rel_create([lib.store.relation.idvmap.block], co.who.id, actor.id) co.srv:actor_rel_destroy([lib.store.relation.idvmap.follow], actor.id, co.who.id) else [(function() local tests = quote co:complain(400,'bad request','the action you have attempted on this user is not meaningful') return end for i,v in ipairs(lib.store.relation.members) do tests = quote - if [v ~= 'block'] and act:cmp(lib.str.plit([v])) and not rel.rel.[v]() then -- rely on dead code elimination :/ + if [v ~= 'block'] and act:cmp(([v])) and not rel.rel.[v]() then -- rely on dead code elimination :/ (rel.rel.[v] << true) co.srv:actor_rel_create([lib.store.relation.idvmap[v]], co.who.id, actor.id) - elseif act:cmp(lib.str.plit(['un'..v])) and rel.rel.[v]() then + elseif act:cmp((['un'..v])) and rel.rel.[v]() then (rel.rel.[v] << false) co.srv:actor_rel_destroy([lib.store.relation.idvmap[v]], co.who.id, actor.id) else [tests] end end end @@ -104,21 +104,21 @@ 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)) + lib.render.login(co, nil, nil, pstring.null()) 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') + lib.render.login(co, nil, nil, 'access denied') return end var fakeact = false var fakeactor: lib.store.actor if act.ptr == nil then @@ -136,11 +136,11 @@ 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)) + lib.render.login(co, act.ptr, &cs, pstring.null()) 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, @@ -151,11 +151,11 @@ -- ยทยทยท -- else lib.dbg('invalid auth method') end -- error out if aid == 0 then - lib.render.login(co, nil, nil, lib.str.plit 'authentication failure') + 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 @@ -255,33 +255,33 @@ 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?'; + title = 'delete post'; + query = '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'/'; + title = 'cancel retweet'; + query = 'are you sure you want to undo this retweet?'; + cancel = '/'; } end var fr = co.srv.pool:frame() var body = conf:poolstr(&co.srv.pool) --defer body:free() co:stdpage([lib.srv.convo.page] { - title = lib.str.plit 'post :: delete'; - class = lib.str.plit 'query'; + title = 'post :: delete'; + class = 'query'; body = body; cache = false; }) co.srv.pool:reset(fr) return elseif meth == method.post then var act = co:ppostv('act') - if act:cmp(lib.str.plit 'confirm') then + if act:cmp( '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 @@ -293,24 +293,24 @@ 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 + if act:cmp( 'like') and not co.srv:post_liked_uid(co.who.id,pid) then co.srv:post_like(co.who.id, pid, false) post.ptr.likes = post.ptr.likes + 1 - elseif act:cmp(lib.str.plit 'dislike') and co.srv:post_liked_uid(co.who.id,pid) then + elseif act:cmp( 'dislike') and co.srv:post_liked_uid(co.who.id,pid) then co.srv:post_like(co.who.id, pid, true) post.ptr.likes = post.ptr.likes - 1 - elseif act:cmp(lib.str.plit 'rt') then + elseif act:cmp( '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 + elseif act:cmp( 'post') then var replytext = co:ppostv('post') var acl = co:ppostv('acl') var subj = co:ppostv('subject') - if not acl then acl = lib.str.plit 'all' end + if not acl then acl = 'all' end if not replytext then goto badop end var reply = lib.store.post { author = co.who.id, parent = pid; subject = subj.ptr, acl = acl.ptr, body = replytext.ptr; @@ -332,17 +332,17 @@ 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 + if act:cmp( '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('?',co.aid) return - elseif act:cmp(lib.str.plit 'newcred') then + elseif act:cmp( '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') @@ -363,11 +363,11 @@ 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') + if val:ref() and val:cmp('on') then ([me].[v] << true) else ([me].[v] << false) end end end @@ -395,24 +395,24 @@ 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') + path(1):cmp('srv') or + path(1):cmp('badge') or + path(1):cmp('emoji') ) then goto nopriv elseif not co.who.rights.powers.rebrand() and ( - path(1):cmp(lib.str.lit 'brand') + path(1):cmp('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') + path(1):cmp('profile') or + path(1):cmp('sec') or + path(1):cmp('avi') or + path(1):cmp('ui') ) then goto nopriv elseif not co.who.rights.powers:affect_users() and ( path(1):cmp(lib.str.lit 'users') ) then goto nopriv end @@ -429,11 +429,11 @@ 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') + resethue = act:cmp( 'reset-hue') end if not resethue then var shue = co:ppostv('hue') var nhue, okhue = lib.math.decparse(shue) @@ -448,32 +448,118 @@ 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' + msg = 'profile changes saved' --user_refresh = true -- not really necessary here, actually - elseif path(1):cmp(lib.str.lit 'sec') then + elseif path(1):cmp('sec') then credsec_for_uid(co, co.who.id) - elseif path(1):cmp(lib.str.lit 'users') then + elseif path(1):cmp('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 usr:ref() then --defer usr:free() + if not co.who:overpowers(usr.ptr) then + usr:free() + goto nopriv + end + else goto badop end + defer usr:free() + if path.ct == 4 then if path(3):cmp(lib.str.lit 'cred') then credsec_for_uid(co, userid) end + elseif path.ct == 3 then + var purgestr = co:ppostv("purgestr") + var purgekey = co:ppostv("purgekey") + if purgestr:ref() and purgekey:ref() and purgestr(0) ~= 0 then + if purgestr:cmp(purgekey) then -- destroying account! :O + co.srv:actor_purge_uid(userid) + co:reroute('/conf/users') + return + else msg = 'purge confirmation failed' end + end + + var epithet = co:ppostv("epithet") + var s_rank = co:ppostv("rank") + var s_invites = co:ppostv("invites") + var s_quota = co:ppostv("quota") + var ch_staff = co:ppostv("staff") + var torank: uint16 = usr(0).rights.rank + if ch_staff:ref() and ch_staff:cmp('on') then + if s_rank:ref() then + var rank, rok = lib.math.decparse(s_rank) + if rok and rank <= co.srv.cfg.nranks then + torank = rank + end + elseif usr(0).rights.rank == 0 then + torank = co.who.rights.rank + 1 + end + else torank = 0 end + + if co.who.id ~= userid and co.who.rights.rank > 0 then + if (co.who.rights.powers.elevate() and + (torank < usr(0).rights.rank or usr(0).rights.rank == 0) and + (torank > co.who.rights.rank or co.who.rights.rank == 1)) + or (co.who.rights.powers.demote() and + (torank > usr(0).rights.rank or torank == 0)) + then usr(0).rights.rank = torank end + end + + if s_invites:ref() then + var n_invites, n_invites_ok = lib.math.decparse(s_invites) + if n_invites_ok and n_invites ~= usr(0).rights.invites then + if (n_invites > usr(0).rights.invites and + co.who.rights.powers.elevate() and + co.who.rights.powers.invite()) + or (n_invites < usr(0).rights.invites and + co.who.rights.powers.demote()) + then usr(0).rights.invites = n_invites end + end + end + + if (co.who.id ~= userid or co.who.rights.rank == 1) and s_quota:ref() then + var n_quota, n_quota_ok = lib.math.decparse(s_quota) + if n_quota_ok and n_quota ~= usr(0).rights.quota then + if (co.who.rights.powers.elevate() and + ((n_quota == 0 and co.who.rights.quota == 0 or co.who.rights.rank == 1) or + (n_quota ~= 0 and (n_quota > usr(0).rights.quota and + (co.who.rights.quota == 0 or + co.who.rights.quota >= n_quota or + co.who.rights.rank == 1))))) + or (co.who.rights.powers.demote() and n_quota ~= 0 and + (n_quota < usr(0).rights.quota or + co.who.rights.rank == 1)) + then usr(0).rights.quota = n_quota end + end + end + + if co.who.rights.powers.herald() and + (co.who.id ~= userid or + co.srv.cfg.pol_autoherald or + co.who.rights.rank == 1) then + if epithet:ref() and epithet(0) ~= 0 then + usr(0).epithet = epithet.ptr + else + usr(0).epithet = nil + end + end + + if co.who.id ~= userid then + -- update powers + end + co.srv:actor_save(usr.ptr) + if not msg then msg = 'user record updated' 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 + if act:cmp('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) @@ -488,11 +574,11 @@ 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 + elseif act:cmp('inst') then else goto badop end end end if user_refresh then -- refresh the user info for the renderer @@ -514,11 +600,11 @@ 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 + if act:cmp('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 @@ -535,12 +621,12 @@ var view = data.view.media_upload { folders = '' } var pg = view:poolstr(&co.srv.pool) -- defer pg:free() co:stdpage([lib.srv.convo.page] { - title = lib.str.plit'media :: upload'; - class = lib.str.plit'media upload'; + title = 'media :: upload'; + class = 'media upload'; cache = false; body = pg; }) elseif meth == method.post_file then var desc = pstring.null() var folder = pstring.null() @@ -547,15 +633,15 @@ 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 + if up.field:cmp('desc') then desc = up.body - elseif up.field:cmp(lib.str.plit'folder') then + elseif up.field:cmp('folder') then folder = up.body - elseif up.field:cmp(lib.str.plit'file') then + elseif up.field:cmp('file') then mime = up.ctype body = binblob {ptr = [&uint8](up.body.ptr), ct = up.body.ct} name = up.filename end end @@ -579,11 +665,11 @@ 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 + if not act or not act:cmp('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() @@ -693,22 +779,22 @@ 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 + elseif uri:cmp( '/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 + elseif uri:cmp( '/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 + elseif uri:cmp( '/login') then if co.aid == 0 then http.login_form(co, meth) else co:reroute('/') end - elseif uri:cmp(lib.str.plit '/logout') then + elseif uri:cmp( '/logout') then if co.aid == 0 then goto notfound else co:reroute_cookie('/','auth=; Path=/') end else -- hierarchical routes Index: srv.t ================================================================== --- srv.t +++ srv.t @@ -5,10 +5,11 @@ local struct srv local struct cfgcache { secret: pstring pol_sec: secmode.t pol_reg: bool + pol_autoherald: bool credmgd: bool maxupsz: intptr poolinitsz: intptr instance: pstring overlord: &srv @@ -326,10 +327,15 @@ 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 @@ -952,10 +958,11 @@ terra cfgcache:load() self.instance = self.overlord:conf_get('instance-name') self.secret = self.overlord:conf_get('server-secret') self.pol_reg = self:cfbool('policy-self-register', false) + self.pol_autoherald = self:cfbool('policy-self-herald', true) do self.credmgd = false var sreg = self.overlord:conf_get('credential-store') if sreg:ref() then if lib.str.cmp(sreg.ptr, 'managed') == 0 Index: str.t ================================================================== --- str.t +++ str.t @@ -34,51 +34,48 @@ do local strptr = (lib.mem.ptr(int8)) local strref = (lib.mem.ref(int8)) local byteptr = (lib.mem.ptr(uint8)) - strptr.metamethods.__cast = function(from,to,e) - if from == &int8 then - return `strptr {ptr = e, ct = m.sz(e)} - elseif to == &int8 then - return e.ptr - end - end - - terra strptr:cmp(other: strptr) - if self.ptr == nil and other.ptr == nil then return true end - if self.ptr == nil or other.ptr == nil then return false end - - var sz = lib.math.biggest(self.ct, other.ct) - for i = 0, sz do - if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end - if self.ptr[i] ~= other.ptr[i] then return false end - end - return true - end - terra strref:cmp(other: strref) - if self.ptr == nil and other.ptr == nil then return true end - if self.ptr == nil or other.ptr == nil then return false end - - var sz = lib.math.biggest(self.ct, other.ct) - for i = 0, sz do - if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end - if self.ptr[i] ~= other.ptr[i] then return false end - end - return true - end - terra strptr:ffw() - var newp = m.ffw(self.ptr,self.ct) - var newct = self.ct - (newp - self.ptr) - return strptr { ptr = newp, ct = newct } - end - strptr.methods.cmpl = macro(function(self,other) - return `self:cmp(strptr { ptr = [other:asvalue()], ct = [#(other:asvalue())] }) - end) - strref.methods.cmpl = macro(function(self,other) - return `self:cmp(strref { ptr = [other:asvalue()], ct = [#(other:asvalue())] }) - end) + local function install_funcs(ty) + ty.metamethods.__cast = function(from,to,e) + local v = e:asvalue() + if type(v) == 'string' then + print('hard-coding pstr',v,#v) + return `ty {ptr = v, ct = [#v]} + elseif from == &int8 then + return `ty {ptr = e, ct = m.sz(e)} + elseif to == &int8 then + return e.ptr + end + end + terra ty:cmp(other: ty) + if self.ptr == nil and other.ptr == nil then return true end + if self.ptr == nil or other.ptr == nil then return false end + + var sz = lib.math.biggest(self.ct, other.ct) + for i = 0, sz do + if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end + if self.ptr[i] ~= other.ptr[i] then return false end + end + return true + end + terra ty:ffw() + var newp = m.ffw(self.ptr,self.ct) + var newct = self.ct - (newp - self.ptr) + return ty { ptr = newp, ct = newct } + end + end + install_funcs(strptr) + install_funcs(strref) + + --strptr.methods.cmpl = macro(function(self,other) + -- return `self:cmp(strptr { ptr = [other:asvalue()], ct = [#(other:asvalue())] }) + --end) + --strref.methods.cmpl = macro(function(self,other) + -- return `self:cmp(strref { ptr = [other:asvalue()], ct = [#(other:asvalue())] }) + --end) terra byteptr:cmp(other: byteptr) var sz = lib.math.biggest(self.ct, other.ct) for i = 0, sz do if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end