parsav  Check-in [e1ff4f301e]

Overview
Comment:get some user admin shit working, general cleanups
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e1ff4f301edd5ee416d023d88a3270904f2b7397c13939ee62c2fcb2232784a4
User & Date: lexi on 2021-01-10 14:26:47
Other Links: manifest | tags
Context
2021-01-10
16:44
add avatar panel check-in: 8398fcda5a user: lexi tags: trunk
14:26
get some user admin shit working, general cleanups check-in: e1ff4f301e user: lexi tags: trunk
11:17
add follow notices check-in: 00a6815988 user: lexi tags: trunk
Changes

Modified mime.t from [2e40a434e4] to [b6a24abaaf].

     2      2   	['text/csrc'] = {
     3      3   		ext = 'c', lang = 'c';
     4      4   	};
     5      5   	['text/html'] = {
     6      6   		ext = 'html', lang = 'html';
     7      7   		unsafe = true;
     8      8   	};
            9  +	['text/x-lua'] = {
           10  +		ext = 'lua', lang = 'lua';
           11  +	};
     9     12   	['text/markdown'] = {
    10     13   		formatter = 'smackdown';
    11     14   		ext = 'md', doc = true;
    12     15   	};
    13     16   }

Modified render/docpage.t from [8016afcf69] to [9f2f406de7].

    85     85   	if started then list:lpush('</ol>') end
    86     86   end
    87     87   
    88     88   local terra 
    89     89   render_docpage(co: &lib.srv.convo, pg: pref)
    90     90   	var nullprivs: lib.store.powerset nullprivs:clear()
    91     91   	if not pg then -- display index
    92         -		var list: lib.str.acc list:compose('<ul>')
           92  +		var list = co:stra(256)
           93  +		list:lpush('<ul>')
    93     94   		var [pages] = array([allpages])
    94     95   		for i=0,[pages.type.N] do
    95     96   			if pages[i].parent == 0 and (pages[i].priv:sz() == 0 or
    96     97   				(co.aid ~= 0 and (co.who.rights.powers
    97     98   					and pages[i].priv):sz() > 0)) then
    98     99   				list:lpush('<li><a href="/doc/'):rpush(pages[i].name):lpush('">')
    99    100   					:rpush(pages[i].title):lpush('</a>')
................................................................................
   103    104   					pushbranches(&list, i, nullprivs)
   104    105   				end
   105    106   				list:lpush('</li>')
   106    107   			end
   107    108   		end
   108    109   		list:lpush('</ul>')
   109    110   
   110         -		var bp = list:finalize()
   111    111   		co:stdpage(page {
   112    112   			title = 'documentation';
   113         -			body = bp;
          113  +			body = list:finalize();
   114    114   			class = P'doc listing';
   115    115   			cache = false;
   116    116   		})
   117         -		bp:free()
   118    117   	else showpage(co, pg) end
   119    118   end
   120    119   
   121    120   return render_docpage

Modified render/media-gallery.t from [5c4e9df122] to [18e4a71c1d].

    73     73   					lib.str.cmp(art(0).mime, 'application/sql') == 0
    74     74   					-- and so on (we need a mimelib at some point) --
    75     75   				then
    76     76   					var view = data.view.media_text(viewerprops)
    77     77   					var text, mime = co.srv:artifact_load(id) mime:free()
    78     78   					var san = lib.html.sanitize(&co.srv.pool,pstr{[rawstring](text.ptr),text.ct}, false)
    79     79   					text:free()
    80         -					view.text = lib.str.acc{}:compose('<pre>',san,'</pre>'):finalize()
    81         -					san:free()
    82         -					var pg = view:tostr()
    83         -					view.text:free()
           80  +					view.text = co:qstr('<pre>',san,'</pre>')
           81  +					--san:free()
           82  +					var pg = view:poolstr(&co.srv.pool)
           83  +					--view.text:free()
    84     84   					co:stdpage([lib.srv.convo.page] {
    85     85   						title = lib.str.plit'media :: text';
    86     86   						class = lib.str.plit'media viewer text';
    87     87   						cache = false, body = pg;
    88     88   					})
    89         -					pg:free()
           89  +					--pg:free()
    90     90   				else co:complain(500,'bad file type','this file type is not supported') end
    91     91   			elseif path.ct == 4 then
    92     92   				var act = path(3)
    93         -				var curl = lib.str.acc{}:compose(pfx, '/media/a/', path(2)):finalize()
    94         -				defer curl:free()
           93  +				var curl = co:qstr('/media/a/', path(2))
           94  +				-- defer curl:free()
    95     95   				if act:cmp(lib.str.lit'avi') and lib.str.ncmp(art(0).mime, 'image/', 6) == 0 then
    96     96   					co:confirm('set avatar', 'are you sure you want this image to be your new avatar?',curl)
    97     97   				elseif act:cmp(lib.str.lit'del') then
    98     98   					co:confirm('delete', 'are you sure you want to permanently delete this artifact?',curl)
    99     99   				else goto e404 end
   100    100   			end
   101    101   		else
................................................................................
   123    123   				folders = pstr{'',0};
   124    124   				directory = pstr{'',0};
   125    125   				images = pstr{'',0};
   126    126   				pfx = pfx;
   127    127   			}
   128    128   
   129    129   			if folders.ct > 0 then
   130         -				var fa: lib.str.acc fa:pool(&co.srv.pool,128)
          130  +				var fa = co:stra(128)
   131    131   				var fldr = co:pgetv('folder')
   132    132   				for i=0,folders.ct do
   133    133   					var ule = lib.html.urlenc(&co.srv.pool,folders(i), true) -- defer ule:free()
   134    134   					var san = lib.html.sanitize(&co.srv.pool,folders(i), true) -- defer san:free()
   135    135   					fa:lpush('<a href="'):ppush(pfx):lpush('/media?folder='):ppush(ule)
   136    136   						:lpush('">'):ppush(san):lpush('</a>')
   137    137   					lib.dbg('checking folder ',{fldr.ptr,fldr.ct},' against ',{folders(i).ptr,folders(i).ct})

Modified render/profile.t from [cdb26ba3d8] to [849244d87f].

    37     37   		var sn_followers = cs(lib.math.decstr_friendly(stats.followers, sn_follows.ptr - 1))
    38     38   		var sn_mutuals   = cs(lib.math.decstr_friendly(stats.mutuals, sn_followers.ptr - 1))
    39     39   	var bio = lib.str.plit '<em style="opacity:0.6">tall, dark, and mysterious</em>'
    40     40   	if actor.bio ~= nil then
    41     41   		bio = lib.smackdown.html(&co.srv.pool,cs(actor.bio),false)
    42     42   	end
    43     43   	var fullname = lib.render.nym(actor,0,nil,false) defer fullname:free()
    44         -	var comments: lib.str.acc comments:pool(&co.srv.pool,64)
           44  +	var comments = co:stra(64)
    45     45   
    46     46   	if co.srv.cfg.master == actor.id then
    47     47   		var foundertxt = lib.str.plit 'founder'
    48     48   		if co.srv.cfg.ui_cue_founder:ref() then
    49     49   			if co.srv.cfg.ui_cue_founder.ct == 0 -- empty string, suppress field
    50     50   				then foundertxt = pstr.null()
    51     51   				else foundertxt = co.srv.cfg.ui_cue_founder
................................................................................
    99     99   		auxbtn = auxp;
   100    100   	}
   101    101   	if comments.sz > 0 then profile.remarks = comments:finalize() end
   102    102   
   103    103   	var ret = profile:poolstr(&co.srv.pool)
   104    104   	-- auxp:free() 
   105    105   	--if actor.bio ~= nil then bio:free() end
   106         -	if comments.sz > 0 then profile.remarks:free() end
          106  +	--if comments.sz > 0 then profile.remarks:free() end
   107    107   	return ret
   108    108   end
   109    109   
   110    110   return render_profile

Modified route.t from [47b3eeec8d] to [efbc5e4a8e].

    12     12   terra http.actor_profile(co: &lib.srv.convo, actor: &lib.store.actor, meth: method.t)
    13     13   	var rel: lib.store.relationship
    14     14   	if co.aid ~= 0 then
    15     15   		rel = co.srv:actor_rel_calc(co.who.id, actor.id)
    16     16   		if meth == method.post then
    17     17   			var act = co:ppostv('act')
    18     18   			if rel.recip.block() then
    19         -				if act:cmp(lib.str.plit 'follow') or act:cmp(lib.str.plit 'subscribe') then
           19  +				if act:cmp( 'follow') or act:cmp( 'subscribe') then
    20     20   					co:complain(403,'blocked','you cannot follow a user you are blocked by') return
    21     21   				end
    22     22   			end
    23         -			if act:cmp(lib.str.plit 'block') and not rel.rel.block() then
           23  +			if act:cmp( 'block') and not rel.rel.block() then
    24     24   				(rel.rel.block << true) ; (rel.recip.follow << false)
    25     25   				co.srv:actor_rel_create([lib.store.relation.idvmap.block], co.who.id, actor.id)
    26     26   				co.srv:actor_rel_destroy([lib.store.relation.idvmap.follow], actor.id, co.who.id)
    27     27   			else
    28     28   				[(function()
    29     29   					local tests = quote co:complain(400,'bad request','the action you have attempted on this user is not meaningful') return end
    30     30   					for i,v in ipairs(lib.store.relation.members) do
    31     31   						tests = quote
    32         -							if [v ~= 'block'] and act:cmp(lib.str.plit([v])) and not rel.rel.[v]() then -- rely on dead code elimination :/
           32  +							if [v ~= 'block'] and act:cmp(([v])) and not rel.rel.[v]() then -- rely on dead code elimination :/
    33     33   								(rel.rel.[v] << true)
    34     34   								co.srv:actor_rel_create([lib.store.relation.idvmap[v]], co.who.id, actor.id)
    35         -							elseif act:cmp(lib.str.plit(['un'..v])) and rel.rel.[v]() then
           35  +							elseif act:cmp((['un'..v])) and rel.rel.[v]() then
    36     36   								(rel.rel.[v] << false)
    37     37   								co.srv:actor_rel_destroy([lib.store.relation.idvmap[v]], co.who.id, actor.id)
    38     38   							else [tests] end
    39     39   						end
    40     40   					end
    41     41   					return tests
    42     42   				end)()]
................................................................................
   102    102   
   103    103   	http.actor_profile(co,actor.ptr,meth)
   104    104   end
   105    105   
   106    106   terra http.login_form(co: &lib.srv.convo, meth: method.t)
   107    107   	if meth_get(meth) then
   108    108   		-- request a username
   109         -		lib.render.login(co, nil, nil, lib.str.plit(nil))
          109  +		lib.render.login(co, nil, nil, pstring.null())
   110    110   	elseif meth == method.post then
   111    111   		var usn, usnl = co:postv('user')
   112    112   		var am, aml = co:postv('authmethod')
   113    113   		var chrs, chrsl = co:postv('response')
   114    114   		var cs, authok = co.srv:actor_auth_how(co.peer, usn)
   115    115   		var act = co.srv:actor_fetch_xid([lib.mem.ptr(int8)] {
   116    116   			ptr = usn, ct = usnl
   117    117   		})
   118    118   		if authok == false then
   119         -			lib.render.login(co, nil, nil, lib.str.plit'access denied')
          119  +			lib.render.login(co, nil, nil, 'access denied')
   120    120   			return
   121    121   		end
   122    122   		var fakeact = false
   123    123   		var fakeactor: lib.store.actor
   124    124   		if act.ptr == nil then
   125    125   			-- the user is known to us but has not yet claimed an
   126    126   			-- account on the server. create a template for the
................................................................................
   134    134   			}
   135    135   			act.ct = 1
   136    136   			act.ptr = &fakeactor
   137    137   			act.ptr.rights = lib.store.rights_default()
   138    138   		end
   139    139   		if am == nil then
   140    140   			-- pick an auth method
   141         -			lib.render.login(co, act.ptr, &cs, lib.str.plit(nil))
          141  +			lib.render.login(co, act.ptr, &cs, pstring.null())
   142    142   		else var aid: uint64 = 0
   143    143   			lib.dbg('authentication attempt beginning')
   144    144   			-- attempt login with provided method
   145    145   			if lib.str.ncmp('pw', am, lib.math.biggest(2,aml)) == 0 and chrs ~= nil then
   146    146   				aid = co.srv:actor_auth_pw(co.peer,
   147    147   					[lib.mem.ptr(int8)]{ptr=usn,ct=usnl},
   148    148   					[lib.mem.ptr(int8)]{ptr=chrs,ct=chrsl})
................................................................................
   149    149   			elseif lib.str.ncmp('otp', am, lib.math.biggest(2,aml)) == 0 and chrs ~= nil then
   150    150   				lib.dbg('using otp auth')
   151    151   				-- ยทยทยท --
   152    152   			else lib.dbg('invalid auth method') end
   153    153   
   154    154   			-- error out
   155    155   			if aid == 0 then
   156         -				lib.render.login(co, nil, nil, lib.str.plit 'authentication failure')
          156  +				lib.render.login(co, nil, nil,  'authentication failure')
   157    157   			else
   158    158   				co:installkey('/',aid)
   159    159   			end
   160    160   		end
   161    161   		if act.ptr ~= nil and fakeact == false then act:free() end
   162    162   	else
   163    163   		::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end
................................................................................
   253    253   			end
   254    254   			return
   255    255   		elseif path(2):cmp(lib.str.lit 'del') then
   256    256   			if meth_get(meth) then
   257    257   				var conf: data.view.confirm
   258    258   				if post:ref() then
   259    259   					conf = data.view.confirm {
   260         -						title = lib.str.plit 'delete post';
   261         -						query = lib.str.plit 'are you sure you want to delete this post?';
          260  +						title =  'delete post';
          261  +						query =  'are you sure you want to delete this post?';
   262    262   						cancel = lnkp
   263    263   					}
   264    264   				else
   265    265   					conf = data.view.confirm {
   266         -						title = lib.str.plit 'cancel retweet';
   267         -						query = lib.str.plit 'are you sure you want to undo this retweet?';
   268         -						cancel = lib.str.plit'/';
          266  +						title =  'cancel retweet';
          267  +						query =  'are you sure you want to undo this retweet?';
          268  +						cancel = '/';
   269    269   					}
   270    270   				end
   271    271   				var fr = co.srv.pool:frame()
   272    272   				var body = conf:poolstr(&co.srv.pool) --defer body:free()
   273    273   				co:stdpage([lib.srv.convo.page] {
   274         -					title = lib.str.plit 'post :: delete';
   275         -					class = lib.str.plit 'query';
          274  +					title =  'post :: delete';
          275  +					class =  'query';
   276    276   					body = body; cache = false;
   277    277   				})
   278    278   				co.srv.pool:reset(fr)
   279    279   				return
   280    280   			elseif meth == method.post then
   281    281   				var act = co:ppostv('act')
   282         -				if act:cmp(lib.str.plit 'confirm') then
          282  +				if act:cmp( 'confirm') then
   283    283   					if post:ref() then
   284    284   						post(0).source:post_destroy(post(0).id)
   285    285   					elseif rt.kind ~= 0 then
   286    286   						co.srv:post_act_cancel(pid)
   287    287   					end
   288    288   					co:reroute('/') -- TODO maybe return to parent or conversation if possible
   289    289   					return
................................................................................
   291    291   			end
   292    292   		else goto badurl end
   293    293   	end
   294    294   
   295    295   	if post:ref() and meth == method.post then
   296    296   		if co.aid == 0 then goto noauth end
   297    297   		var act = co:ppostv('act')
   298         -		if act:cmp(lib.str.plit 'like') and not co.srv:post_liked_uid(co.who.id,pid) then
          298  +		if act:cmp( 'like') and not co.srv:post_liked_uid(co.who.id,pid) then
   299    299   			co.srv:post_like(co.who.id, pid, false)
   300    300   			post.ptr.likes = post.ptr.likes + 1
   301         -		elseif act:cmp(lib.str.plit 'dislike') and co.srv:post_liked_uid(co.who.id,pid) then
          301  +		elseif act:cmp( 'dislike') and co.srv:post_liked_uid(co.who.id,pid) then
   302    302   			co.srv:post_like(co.who.id, pid, true)
   303    303   			post.ptr.likes = post.ptr.likes - 1
   304         -		elseif act:cmp(lib.str.plit 'rt') then
          304  +		elseif act:cmp( 'rt') then
   305    305   			co.srv:post_retweet(co.who.id, pid, false)
   306    306   			post.ptr.rts = post.ptr.rts + 1
   307         -		elseif act:cmp(lib.str.plit 'post') then
          307  +		elseif act:cmp( 'post') then
   308    308   			var replytext = co:ppostv('post')
   309    309   			var acl = co:ppostv('acl')
   310    310   			var subj = co:ppostv('subject')
   311         -			if not acl then acl = lib.str.plit 'all' end
          311  +			if not acl then acl =  'all' end
   312    312   			if not replytext then goto badop end
   313    313   			
   314    314   			var reply = lib.store.post {
   315    315   				author = co.who.id, parent = pid;
   316    316   				subject = subj.ptr, acl = acl.ptr, body = replytext.ptr;
   317    317   			}
   318    318   
................................................................................
   330    330   	::noauth:: do co:complain(401, 'unauthorized', 'you have not supplied the necessary credentials to perform this operation') return end
   331    331   end
   332    332   
   333    333   local terra 
   334    334   credsec_for_uid(co: &lib.srv.convo, uid: uint64)
   335    335   	var act = co:ppostv('act')
   336    336   	lib.dbg('showing credentials')
   337         -	if act:cmp(lib.str.plit 'invalidate') then
          337  +	if act:cmp( 'invalidate') then
   338    338   		lib.dbg('setting user\'s cookie validation time to now')
   339    339   		co.who.source:auth_sigtime_user_alter(uid, lib.osclock.time(nil))
   340    340   		-- 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
   341    341   		co:installkey('?',co.aid)
   342    342   		return
   343         -	elseif act:cmp(lib.str.plit 'newcred') then
          343  +	elseif act:cmp( 'newcred') then
   344    344   		var cmt = co:ppostv('comment')
   345    345   		var pw = co:ppostv('newpw')
   346    346   		var aid: uint64 = 0
   347    347   		if pw:ref() then
   348    348   			var cpw = co:ppostv('rptpw')
   349    349   			if not pw:cmp(cpw) then
   350    350   				co:complain(400,'enrollment failure','the passwords you supplied do not match')
................................................................................
   361    361   			lib.dbg('setting credential restrictions')
   362    362   			var privs = [(function()
   363    363   				local check = quote end
   364    364   				local me = symbol(lib.store.privset)
   365    365   				for i,v in ipairs(lib.store.privset.members) do
   366    366   					check = quote [check]
   367    367   						var val = co:pgetv(['allow-' .. v])
   368         -						if val:ref() and val:cmp(lib.str.plit'on')
          368  +						if val:ref() and val:cmp('on')
   369    369   							then ([me].[v] << true)
   370    370   							else ([me].[v] << false)
   371    371   						end
   372    372   					end
   373    373   				end
   374    374   				return quote
   375    375   					var [me]
................................................................................
   393    393   end
   394    394   
   395    395   terra http.configure(co: &lib.srv.convo, path: hpath, meth: method.t)
   396    396   	var msg = pstring.null()
   397    397   	-- first things first, do priv checks
   398    398   	if path.ct >= 2 then
   399    399   		if not co.who.rights.powers.config() and (
   400         -			path(1):cmp(lib.str.lit 'srv')   or
   401         -			path(1):cmp(lib.str.lit 'badge') or
   402         -			path(1):cmp(lib.str.lit 'emoji')
          400  +			path(1):cmp('srv')   or
          401  +			path(1):cmp('badge') or
          402  +			path(1):cmp('emoji')
   403    403   		) then goto nopriv
   404    404   
   405    405   		elseif not co.who.rights.powers.rebrand() and (
   406         -			path(1):cmp(lib.str.lit 'brand')
          406  +			path(1):cmp('brand')
   407    407   		) then goto nopriv
   408    408   
   409    409   		elseif not co.who.rights.powers.account() and (
   410         -			path(1):cmp(lib.str.lit 'profile') or
   411         -			path(1):cmp(lib.str.lit 'sec') or
   412         -			path(1):cmp(lib.str.lit 'avi') or
   413         -			path(1):cmp(lib.str.lit 'ui')
          410  +			path(1):cmp('profile') or
          411  +			path(1):cmp('sec') or
          412  +			path(1):cmp('avi') or
          413  +			path(1):cmp('ui')
   414    414   		) then goto nopriv
   415    415   
   416    416   		elseif not co.who.rights.powers:affect_users() and (
   417    417   			path(1):cmp(lib.str.lit 'users')
   418    418   		) then goto nopriv end
   419    419   	end
   420    420   
................................................................................
   427    427   			if co.who.bio ~= nil and @co.who.bio == 0 then co.who.bio = nil end
   428    428   			if co.who.nym ~= nil and @co.who.nym == 0 then co.who.nym = nil end
   429    429   			co.who.source:actor_save(co.who)
   430    430   
   431    431   			var act = co:ppostv('act')
   432    432   			var resethue = false
   433    433   			if act:ref() then
   434         -				resethue = act:cmp(lib.str.plit 'reset-hue')
          434  +				resethue = act:cmp( 'reset-hue')
   435    435   			end
   436    436   
   437    437   			if not resethue then
   438    438   				var shue = co:ppostv('hue')
   439    439   				var nhue, okhue = lib.math.decparse(shue)
   440    440   				if okhue and nhue ~= co.ui_hue then
   441    441   					if nhue == co.srv.cfg.ui_hue
................................................................................
   446    446   				end
   447    447   			end
   448    448   			if resethue then
   449    449   				co.srv:actor_conf_int_reset(co.who.id, 'ui-accent')
   450    450   				co.ui_hue = co.srv.cfg.ui_hue
   451    451   			end
   452    452   
   453         -			msg = lib.str.plit 'profile changes saved'
          453  +			msg = 'profile changes saved'
   454    454   			--user_refresh = true -- not really necessary here, actually
   455    455   
   456         -		elseif path(1):cmp(lib.str.lit 'sec') then
          456  +		elseif path(1):cmp('sec') then
   457    457   			credsec_for_uid(co, co.who.id)
   458         -		elseif path(1):cmp(lib.str.lit 'users') then
          458  +		elseif path(1):cmp('users') then
   459    459   			if path.ct >= 3 then
   460    460   				var userid, ok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
   461    461   				if ok then
   462    462   					var usr = co.srv:actor_fetch_uid(userid)
   463         -					if usr:ref() then defer usr:free()
   464         -						if not co.who:overpowers(usr.ptr) then goto nopriv end
   465         -					end
          463  +					if usr:ref() then --defer usr:free()
          464  +						if not co.who:overpowers(usr.ptr) then
          465  +							usr:free()
          466  +							goto nopriv
          467  +						end
          468  +					else goto badop end
          469  +					defer usr:free()
          470  +
   466    471   					if path.ct == 4 then
   467    472   						if path(3):cmp(lib.str.lit 'cred') then
   468    473   							credsec_for_uid(co, userid)
   469    474   						end
          475  +					elseif path.ct == 3 then
          476  +						var purgestr = co:ppostv("purgestr")
          477  +						var purgekey = co:ppostv("purgekey")
          478  +						if purgestr:ref() and purgekey:ref() and purgestr(0) ~= 0 then
          479  +							if purgestr:cmp(purgekey) then -- destroying account! :O
          480  +								co.srv:actor_purge_uid(userid)
          481  +								co:reroute('/conf/users')
          482  +								return
          483  +							else msg = 'purge confirmation failed' end
          484  +						end
          485  +
          486  +						var epithet = co:ppostv("epithet")
          487  +						var s_rank = co:ppostv("rank")
          488  +						var s_invites = co:ppostv("invites")
          489  +						var s_quota = co:ppostv("quota")
          490  +						var ch_staff = co:ppostv("staff")
          491  +						var torank: uint16 = usr(0).rights.rank
          492  +						if ch_staff:ref() and ch_staff:cmp('on') then
          493  +							if s_rank:ref() then
          494  +								var rank, rok = lib.math.decparse(s_rank)
          495  +								if rok and rank <= co.srv.cfg.nranks then
          496  +									torank = rank
          497  +								end
          498  +							elseif usr(0).rights.rank == 0 then
          499  +								torank = co.who.rights.rank + 1
          500  +							end
          501  +						else torank = 0 end
          502  +
          503  +						if co.who.id ~= userid and co.who.rights.rank > 0 then
          504  +							if (co.who.rights.powers.elevate() and
          505  +								 (torank < usr(0).rights.rank or usr(0).rights.rank == 0) and
          506  +								  (torank > co.who.rights.rank or co.who.rights.rank == 1))
          507  +							or (co.who.rights.powers.demote()  and
          508  +								 (torank > usr(0).rights.rank or             torank == 0))
          509  +							then usr(0).rights.rank = torank end
          510  +						end
          511  +
          512  +						if s_invites:ref() then
          513  +							var n_invites, n_invites_ok = lib.math.decparse(s_invites)
          514  +							if n_invites_ok and n_invites ~= usr(0).rights.invites then
          515  +								if (n_invites > usr(0).rights.invites and
          516  +									co.who.rights.powers.elevate() and
          517  +									co.who.rights.powers.invite())
          518  +								or (n_invites < usr(0).rights.invites and
          519  +									co.who.rights.powers.demote())
          520  +								then usr(0).rights.invites = n_invites end
          521  +							end
          522  +						end
          523  +						
          524  +						if (co.who.id ~= userid or co.who.rights.rank == 1) and s_quota:ref() then
          525  +							var n_quota, n_quota_ok = lib.math.decparse(s_quota)
          526  +							if n_quota_ok and n_quota ~= usr(0).rights.quota then
          527  +								if (co.who.rights.powers.elevate() and 
          528  +									((n_quota == 0 and  co.who.rights.quota == 0 or co.who.rights.rank == 1) or
          529  +									 (n_quota ~= 0 and (n_quota > usr(0).rights.quota and
          530  +										(co.who.rights.quota == 0 or
          531  +										 co.who.rights.quota >= n_quota or
          532  +										 co.who.rights.rank  == 1)))))
          533  +								or (co.who.rights.powers.demote() and n_quota ~= 0 and 
          534  +									(n_quota < usr(0).rights.quota or
          535  +									 co.who.rights.rank == 1))
          536  +								then usr(0).rights.quota = n_quota end
          537  +							end
          538  +						end
          539  +
          540  +						if co.who.rights.powers.herald() and
          541  +						  (co.who.id ~= userid or
          542  +						   co.srv.cfg.pol_autoherald or
          543  +						   co.who.rights.rank == 1) then 
          544  +							if epithet:ref() and epithet(0) ~= 0 then
          545  +								usr(0).epithet = epithet.ptr
          546  +							else
          547  +								usr(0).epithet = nil
          548  +							end
          549  +						end
          550  +
          551  +						if co.who.id ~= userid then
          552  +							-- update powers
          553  +						end
          554  +						co.srv:actor_save(usr.ptr)
          555  +						if not msg then msg = 'user record updated' end
   470    556   					end
   471    557   				end
   472    558   			elseif path.ct == 2 and meth == method.post then
   473    559   				var act = co:ppostv('act')
   474         -				if act:cmp(lib.str.plit'create') then
          560  +				if act:cmp('create') then
   475    561   					var newname = co:ppostv('handle')
   476    562   					if not newname or not lib.store.actor.handle_validate(newname.ptr) then
   477    563   						co:complain(400,'invalid handle','the handle you have requested is not valid')
   478    564   					end
   479    565   					var tu = co.srv:actor_fetch_xid(newname)
   480    566   					if tu:ref() then tu:free()
   481    567   						co:complain(409,'handle clash','that handle conflicts with one that already exists')
................................................................................
   486    572   					na.handle = newname.ptr
   487    573   					var newuid = co.srv:actor_create(&na)
   488    574   					var shid: int8[lib.math.shorthand.maxlen]
   489    575   					var shidlen = lib.math.shorthand.gen(newuid, &shid[0])
   490    576   					var url = lib.str.acc{}:compose('/conf/users/',pstring{&shid[0],shidlen}):finalize() defer url:free()
   491    577   					co:reroute(url.ptr)
   492    578   					return
   493         -				elseif act:cmp(lib.str.plit'inst') then
          579  +				elseif act:cmp('inst') then
   494    580   				else goto badop end
   495    581   			end
   496    582   		end
   497    583   
   498    584   		if user_refresh then -- refresh the user info for the renderer
   499    585   			var usr = co.srv:actor_fetch_uid(co.who.id)
   500    586   			lib.mem.heapf(co.who)
................................................................................
   512    598   	::nopriv:: do co:complain(403,'insufficient privileges','you do not have the necessary powers to perform this action') return end
   513    599   	::badop:: do co:complain(400,'bad request','the operation you have requested is not meaningful in this context') return end
   514    600   end
   515    601   
   516    602   terra http.user_notices(co: &lib.srv.convo, meth: method.t)
   517    603   	if meth == method.post then
   518    604   		var act = co:ppostv('act')
   519         -		if act:cmp(lib.str.plit'clear') then
          605  +		if act:cmp('clear') then
   520    606   			co.srv:actor_conf_int_set(co.who.id, 'notice-clear-time', lib.osclock.time(nil))
   521    607   			co:reroute('/')
   522    608   			return
   523    609   		else goto badop end
   524    610   	end
   525    611   
   526    612   	lib.render.notices(co)
................................................................................
   533    619   	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
   534    620   		if meth == method.get then
   535    621   			var view = data.view.media_upload {
   536    622   				folders = ''
   537    623   			}
   538    624   			var pg = view:poolstr(&co.srv.pool) -- defer pg:free()
   539    625   			co:stdpage([lib.srv.convo.page] {
   540         -				title = lib.str.plit'media :: upload';
   541         -				class = lib.str.plit'media upload';
          626  +				title = 'media :: upload';
          627  +				class = 'media upload';
   542    628   				cache = false; body = pg;
   543    629   			})
   544    630   		elseif meth == method.post_file then
   545    631   			var desc = pstring.null()
   546    632   			var folder = pstring.null()
   547    633   			var mime = pstring.null()
   548    634   			var name = pstring.null()
   549    635   			var body = binblob.null()
   550    636   			for i=0, co.uploads.sz do var up = co.uploads.storage.ptr + i
   551    637   				if up.body.ct > 0 then
   552         -					if up.field:cmp(lib.str.plit'desc') then
          638  +					if up.field:cmp('desc') then
   553    639   						desc = up.body
   554         -					elseif up.field:cmp(lib.str.plit'folder') then
          640  +					elseif up.field:cmp('folder') then
   555    641   						folder = up.body
   556         -					elseif up.field:cmp(lib.str.plit'file') then
          642  +					elseif up.field:cmp('file') then
   557    643   						mime = up.ctype
   558    644   						body = binblob {ptr = [&uint8](up.body.ptr), ct = up.body.ct}
   559    645   						name = up.filename
   560    646   					end
   561    647   				end
   562    648   			end
   563    649   			if not body then goto badop end
................................................................................
   577    663   
   578    664   			var url = lib.str.acc{}:compose('/media/a/',pstring{&idbuf[0],idlen}):finalize()
   579    665   			co:reroute(url.ptr)
   580    666   			url:free()
   581    667   		else goto badop end
   582    668   	elseif co.aid ~= 0 and path.ct == 4 and path(1):cmp(lib.str.lit'a') and meth==method.post then 
   583    669   		var act = co:ppostv('act')
   584         -		if not act or not act:cmp(lib.str.plit'confirm') then goto badop end
          670  +		if not act or not act:cmp('confirm') then goto badop end
   585    671   		var artid, aok = lib.math.shorthand.parse(path(2).ptr,path(2).ct)
   586    672   		if not aok then goto e404 end
   587    673   		var art = co.srv:artifact_fetch(uid,artid)
   588    674   		if not art then goto e404 end
   589    675   		defer art:free()
   590    676   
   591    677   		if path(3):cmp(lib.str.lit'avi') then
................................................................................
   691    777   	elseif uri.ptr[1] == @'s' and uri.ptr[2] == @'/' and uri.ct > 3 then
   692    778   		if not meth_get(meth) then goto wrongmeth end
   693    779   		if not http.static_content(co, uri.ptr + 3, uri.ct - 3) then goto notfound end
   694    780   	elseif lib.str.ncmp('/avi/', uri.ptr, 5) == 0 then
   695    781   		http.local_avatar(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 5, ct = uri.ct - 5})
   696    782   	elseif lib.str.ncmp('/file/', uri.ptr, 6) == 0 then
   697    783   		http.file_serve_raw(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 6, ct = uri.ct - 6})
   698         -	elseif uri:cmp(lib.str.plit '/notices') then
          784  +	elseif uri:cmp( '/notices') then
   699    785   		if co.aid == 0 then co:reroute('/login') return end
   700    786   		http.user_notices(co,meth)
   701         -	elseif uri:cmp(lib.str.plit '/compose') then
          787  +	elseif uri:cmp( '/compose') then
   702    788   		if co.aid == 0 then co:reroute('/login') return end
   703    789   		http.post_compose(co,meth)
   704         -	elseif uri:cmp(lib.str.plit '/login') then
          790  +	elseif uri:cmp( '/login') then
   705    791   		if co.aid == 0
   706    792   			then http.login_form(co, meth)
   707    793   			else co:reroute('/')
   708    794   		end
   709         -	elseif uri:cmp(lib.str.plit '/logout') then
          795  +	elseif uri:cmp( '/logout') then
   710    796   		if co.aid == 0
   711    797   			then goto notfound
   712    798   			else co:reroute_cookie('/','auth=; Path=/')
   713    799   		end
   714    800   	else -- hierarchical routes
   715    801   		var path = lib.http.hier(&co.srv.pool, uri) --defer path:free()
   716    802   		if path.ct > 1 and path(0):cmp(lib.str.lit('user')) then

Modified srv.t from [854410a8ca] to [baac561cfe].

     3      3   local secmode = lib.enum { 'public', 'private', 'lockdown', 'isolate' }
     4      4   local pstring = lib.mem.ptr(int8)
     5      5   local struct srv
     6      6   local struct cfgcache {
     7      7   	secret: pstring
     8      8   	pol_sec: secmode.t
     9      9   	pol_reg: bool
           10  +	pol_autoherald: bool
    10     11   	credmgd: bool
    11     12   	maxupsz: intptr
    12     13   	poolinitsz: intptr
    13     14   	instance: pstring
    14     15   	overlord: &srv
    15     16   	ui_cue_staff: pstring
    16     17   	ui_cue_founder: pstring
................................................................................
   324    325   end
   325    326   
   326    327   terra convo:stra(sz: intptr) -- convenience function
   327    328   	var s: lib.str.acc
   328    329   	s:pool(&self.srv.pool,sz)
   329    330   	return s
   330    331   end
          332  +
          333  +convo.methods.qstr = macro(function(self, ...) -- convenience string builder
          334  +	local exp = {...}
          335  +	return `lib.str.acc{}:pcompose(&self.srv.pool, [exp]):finalize()
          336  +end)
   331    337   
   332    338   convo.methods.assertpow = macro(function(self, pow)
   333    339   	return quote
   334    340   		var ok = true
   335    341   		if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then
   336    342   			ok = false
   337    343   			self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action'])
................................................................................
   950    956   end
   951    957   
   952    958   terra cfgcache:load()
   953    959   	self.instance = self.overlord:conf_get('instance-name')
   954    960   	self.secret = self.overlord:conf_get('server-secret')
   955    961   
   956    962   	self.pol_reg = self:cfbool('policy-self-register', false)
          963  +	self.pol_autoherald = self:cfbool('policy-self-herald', true)
   957    964   
   958    965   	do self.credmgd = false
   959    966   	var sreg = self.overlord:conf_get('credential-store')
   960    967   	if sreg:ref() then
   961    968   		if lib.str.cmp(sreg.ptr, 'managed') == 0
   962    969   			then self.credmgd = true
   963    970   			else self.credmgd = false

Modified str.t from [89921fa130] to [c8b5d8bfdc].

    32     32   	return str
    33     33   end
    34     34   
    35     35   
    36     36   do local strptr = (lib.mem.ptr(int8))
    37     37   	local strref = (lib.mem.ref(int8))
    38     38   	local byteptr = (lib.mem.ptr(uint8))
    39         -	strptr.metamethods.__cast = function(from,to,e)
    40         -		if from == &int8 then
    41         -			return `strptr {ptr = e, ct = m.sz(e)}
    42         -		elseif to == &int8 then
    43         -			return e.ptr
    44         -		end
    45         -	end
    46         -
    47         -	terra strptr:cmp(other: strptr)
    48         -		if self.ptr == nil and other.ptr == nil then return true end
    49         -		if self.ptr == nil or other.ptr == nil then return false end
    50         -
    51         -		var sz = lib.math.biggest(self.ct, other.ct)
    52         -		for i = 0, sz do
    53         -			if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end
    54         -			if self.ptr[i] ~= other.ptr[i] then return false end
    55         -		end
    56         -		return true
    57         -	end
    58         -	terra strref:cmp(other: strref)
    59         -		if self.ptr == nil and other.ptr == nil then return true end
    60         -		if self.ptr == nil or other.ptr == nil then return false end
    61         -
    62         -		var sz = lib.math.biggest(self.ct, other.ct)
    63         -		for i = 0, sz do
    64         -			if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end
    65         -			if self.ptr[i] ~= other.ptr[i] then return false end
    66         -		end
    67         -		return true
    68         -	end
    69         -	terra strptr:ffw()
    70         -		var newp = m.ffw(self.ptr,self.ct)
    71         -		var newct = self.ct - (newp - self.ptr)
    72         -		return strptr { ptr = newp, ct = newct }
    73         -	end
    74         -	strptr.methods.cmpl = macro(function(self,other)
    75         -		return `self:cmp(strptr { ptr = [other:asvalue()], ct = [#(other:asvalue())] })
    76         -	end)
    77         -	strref.methods.cmpl = macro(function(self,other)
    78         -		return `self:cmp(strref { ptr = [other:asvalue()], ct = [#(other:asvalue())] })
    79         -	end)
           39  +	local function install_funcs(ty)
           40  +		ty.metamethods.__cast = function(from,to,e)
           41  +			local v = e:asvalue()
           42  +			if type(v) == 'string' then
           43  +				print('hard-coding pstr',v,#v)
           44  +				return `ty {ptr = v, ct = [#v]}
           45  +			elseif from == &int8 then
           46  +				return `ty {ptr = e, ct = m.sz(e)}
           47  +			elseif to == &int8 then
           48  +				return e.ptr
           49  +			end
           50  +		end
           51  +		terra ty:cmp(other: ty)
           52  +			if self.ptr == nil and other.ptr == nil then return true end
           53  +			if self.ptr == nil or other.ptr == nil then return false end
           54  +
           55  +			var sz = lib.math.biggest(self.ct, other.ct)
           56  +			for i = 0, sz do
           57  +				if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end
           58  +				if self.ptr[i] ~= other.ptr[i] then return false end
           59  +			end
           60  +			return true
           61  +		end
           62  +		terra ty:ffw()
           63  +			var newp = m.ffw(self.ptr,self.ct)
           64  +			var newct = self.ct - (newp - self.ptr)
           65  +			return ty { ptr = newp, ct = newct }
           66  +		end
           67  +	end
           68  +	install_funcs(strptr)
           69  +	install_funcs(strref)
           70  +
           71  +	--strptr.methods.cmpl = macro(function(self,other)
           72  +	--	return `self:cmp(strptr { ptr = [other:asvalue()], ct = [#(other:asvalue())] })
           73  +	--end)
           74  +	--strref.methods.cmpl = macro(function(self,other)
           75  +	--	return `self:cmp(strref { ptr = [other:asvalue()], ct = [#(other:asvalue())] })
           76  +	--end)
    80     77   
    81     78   	terra byteptr:cmp(other: byteptr)
    82     79   		var sz = lib.math.biggest(self.ct, other.ct)
    83     80   		for i = 0, sz do
    84     81   			if self.ptr[i] == 0 and other.ptr[i] == 0 then return true end
    85     82   			if self.ptr[i] ~= other.ptr[i] then return false end
    86     83   		end