103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
|
if punct ~= nil then a:push(punct, 1) end
a:ipush(rnd(uint16,0,65535))
end
if xXx then a:lpush('_xXx') end
end
local push_num_field = macro(function(acc,name,lbl,min,max,value,disable)
name = name:asvalue()
lbl = lbl:asvalue()
local start = '<div class="elem small">'
local enabled = start .. string.format('<label for="%s">%s</label><input type="number" id="%s" name="%s" min="', name, lbl, name, name)
local disabled = start .. string.format('<label>%s</label><div class="txtbox">', lbl)
................................................................................
local push_checkbox = input_pusher('checkbox',true,false)
local push_pickbox = input_pusher('checkbox',false,false)
local push_radio = input_pusher('radio',false,true)
local mode_local, mode_remote, mode_staff, mode_peers, mode_peons, mode_all = 0,1,2,3,4,5
local terra
render_conf_users(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
if path.ct == 3 then
var uid, ok = lib.math.shorthand.parse(path(2).ptr,path(2).ct)
if not ok then goto e404 end
var user = co.srv:actor_fetch_uid(uid)
-- FIXME allow xids as well, for manual queries
if not user then goto e404 end
defer user:free()
if not co.who:overpowers(user.ptr) then goto e403 end
var cinp: lib.str.acc cinp:init(256)
var clnk: lib.str.acc clnk:init(512)
cinp:lpush('<div class="elem-group">')
if user.ptr.rights.rank > 0 and (co.who.rights.powers.elevate() or co.who.rights.powers.demote()) then
var max = co.who.rights.rank
if not co.who.rights.powers.elevate() then max = user.ptr.rights.rank end
var min = co.srv.cfg.nranks
if not co.who.rights.powers.demote() then min = user.ptr.rights.rank end
push_num_field(cinp, 'rank', 'rank', max, min, user.ptr.rights.rank, user.ptr.id == co.who.id)
end
if co.who.rights.powers.herald() then
var sanitized: pstr
if user.ptr.epithet == nil
then sanitized = pstr {ptr='', ct=0}
else sanitized = lib.html.sanitize(cs(user.ptr.epithet),true)
end
cinp:lpush('<div class="elem"><label for="epithet">epithet</label><input type="text" id="epithet" name="epithet" value="'):ppush(sanitized):lpush('"></div>')
if user.ptr.epithet ~= nil then sanitized:free() end
end
if co.who.rights.powers.invite() or co.who.rights.powers.discipline() then
var min: uint32 = 0
if not (co.who.rights.powers.discipline() or
co.who.rights.powers.demote() and co.who.rights.powers.invite())
then min = user.ptr.rights.invites end
var max: uint32 = co.srv.cfg.maxinvites
if not co.who.rights.powers.invite() then max = user.ptr.rights.invites end
push_num_field(cinp, 'invites', 'invites', min, max, user.ptr.rights.invites, false)
end
if co.who.rights.powers.elevate() or co.who.rights.powers.demote() then
var max: uint32 = 5000
if not co.who.rights.powers.elevate() then max = user.ptr.rights.quota end
var min: uint32 = 0
if not co.who.rights.powers.demote() then min = user.ptr.rights.quota end
push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
end
cinp:lpush('</div><div class="elem"><div class="check-panel">')
if user.ptr.id ~= co.who.id and
((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
(user.ptr.rights.rank > 0 and co.who.rights.powers.demote())) then
push_checkbox(&cinp, 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
end
cinp:lpush('</div></div>')
if (co.who.rights.powers.elevate() or
co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
var map = array([lib.store.privmap])
cinp:lpush('<details><summary>powers</summary><div class="pick-list">')
for i=0, [map.type.N] do
if (co.who.rights.powers and map[i].priv):sz() > 0 then
var on = (user.ptr.rights.powers and map[i].priv):sz() > 0
var enabled = ( on and co.who.rights.powers.demote() ) or
((not on) and co.who.rights.powers.elevate())
var namea: lib.str.acc namea:compose('power-', map[i].name)
var name = namea:finalize()
push_pickbox(&cinp, name, pstr.null(), map[i].name, on, enabled, pstr.null())
name:free()
end
end
cinp:lpush('</div></details>')
end
-- TODO black mark system? e.g. resolution option for badthink reports
-- adds a black mark to the offending user; they can be automatically banned
-- or brought up for review after a certain number of offenses; possibly lower
-- set of default privs for marked users
var cinpp = cinp:finalize() defer cinpp:free()
var clnkp: pstr
if clnk.sz > 0 then clnkp = clnk:finalize() else
clnk:free()
clnkp = pstr { ptr='', ct=0 }
end
var unym: lib.str.acc unym:init(64)
unym:lpush('<a href="/')
if user(0).origin ~= 0 then unym:lpush('@') end
do var sanxid = lib.html.sanitize(user(0).xid, true)
unym:ppush(sanxid)
sanxid:free() end
unym:lpush('" class="id">')
lib.render.nym(user.ptr,0,&unym,false)
unym:lpush('</a>')
var pg = data.view.conf_user_ctl {
name = unym:finalize();
inputcontent = cinpp;
linkcontent = clnkp;
}
var ret = pg:tostr()
pg.name:free()
if clnkp.ct > 0 then clnkp:free() end
return ret
else
var modes = array(P'local', P'remote', P'staff', P'titled', P'peons', P'all')
var idbuf: int8[lib.math.shorthand.maxlen]
var ulst: lib.str.acc ulst:init(256)
var mode: uint8 = mode_local
var modestr = co:pgetv('show')
ulst:lpush('<div style="text-align: right"><em>showing ')
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
<
<
<
<
<
|
|
|
|
|
|
|
|
|
|
|
|
<
>
|
<
>
>
>
>
>
|
<
>
>
|
>
|
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
|
if punct ~= nil then a:push(punct, 1) end
a:ipush(rnd(uint16,0,65535))
end
if xXx then a:lpush('_xXx') end
end
local terra
suggest_domain(a: &lib.str.acc)
var tlds = array('tld','club','town','space','xxx')
end
local push_num_field = macro(function(acc,name,lbl,min,max,value,disable)
name = name:asvalue()
lbl = lbl:asvalue()
local start = '<div class="elem small">'
local enabled = start .. string.format('<label for="%s">%s</label><input type="number" id="%s" name="%s" min="', name, lbl, name, name)
local disabled = start .. string.format('<label>%s</label><div class="txtbox">', lbl)
................................................................................
local push_checkbox = input_pusher('checkbox',true,false)
local push_pickbox = input_pusher('checkbox',false,false)
local push_radio = input_pusher('radio',false,true)
local mode_local, mode_remote, mode_staff, mode_peers, mode_peons, mode_all = 0,1,2,3,4,5
local terra
render_conf_users(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
if path.ct >= 3 then
var uid, ok = lib.math.shorthand.parse(path(2).ptr,path(2).ct)
if not ok then goto e404 end
var user = co.srv:actor_fetch_uid(uid)
-- FIXME allow xids as well, for manual queries
if not user then goto e404 end
defer user:free()
if not co.who:overpowers(user.ptr) then goto e403 end
if path.ct == 4 then
if path(3):cmp(lib.str.lit'cred') then
var pg: lib.str.acc pg:init(1024)
pg:lpush('<div class="context">editing credentials for user <a href="/conf/users/'):rpush(path(2)):lpush('">'):push(user(0).xid,0):lpush('</a></div>')
var credmgr = lib.render.conf.sec(co, uid)
pg:ppush(credmgr)
credmgr:free()
return pg:finalize()
else goto e404 end
elseif path.ct == 3 then
var cinp: lib.str.acc cinp:init(256)
cinp:lpush('<div class="elem-group">')
if user.ptr.rights.rank > 0 and (co.who.rights.powers.elevate() or co.who.rights.powers.demote()) then
var max = co.who.rights.rank
if not co.who.rights.powers.elevate() then max = user.ptr.rights.rank end
var min = co.srv.cfg.nranks
if not co.who.rights.powers.demote() then min = user.ptr.rights.rank end
push_num_field(cinp, 'rank', 'rank', max, min, user.ptr.rights.rank, user.ptr.id == co.who.id)
end
if co.who.rights.powers.herald() then
var sanitized: pstr
if user.ptr.epithet == nil
then sanitized = pstr {ptr='', ct=0}
else sanitized = lib.html.sanitize(cs(user.ptr.epithet),true)
end
cinp:lpush('<div class="elem"><label for="epithet">epithet</label><input type="text" id="epithet" name="epithet" value="'):ppush(sanitized):lpush('"></div>')
if user.ptr.epithet ~= nil then sanitized:free() end
end
if co.who.rights.powers.invite() or co.who.rights.powers.discipline() then
var min: uint32 = 0
if not (co.who.rights.powers.discipline() or
co.who.rights.powers.demote() and co.who.rights.powers.invite())
then min = user.ptr.rights.invites end
var max: uint32 = co.srv.cfg.maxinvites
if not co.who.rights.powers.invite() then max = user.ptr.rights.invites end
push_num_field(cinp, 'invites', 'invites', min, max, user.ptr.rights.invites, false)
end
if co.who.rights.powers.elevate() or co.who.rights.powers.demote() then
var max: uint32 = 5000
if not co.who.rights.powers.elevate() then max = user.ptr.rights.quota end
var min: uint32 = 0
if not co.who.rights.powers.demote() then min = user.ptr.rights.quota end
push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
end
cinp:lpush('</div><div class="elem"><div class="check-panel">')
if user.ptr.id ~= co.who.id and
((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
(user.ptr.rights.rank > 0 and co.who.rights.powers.demote())) then
push_checkbox(&cinp, 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
end
cinp:lpush('</div></div>')
if (co.who.rights.powers.elevate() or
co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
var map = array([lib.store.privmap])
cinp:lpush('<details><summary>powers</summary><div class="pick-list">')
for i=0, [map.type.N] do
if (co.who.rights.powers and map[i].priv):sz() > 0 then
var on = (user.ptr.rights.powers and map[i].priv):sz() > 0
var enabled = ( on and co.who.rights.powers.demote() ) or
((not on) and co.who.rights.powers.elevate())
var namea: lib.str.acc namea:compose('power-', map[i].name)
var name = namea:finalize()
push_pickbox(&cinp, name, pstr.null(), map[i].name, on, enabled, pstr.null())
name:free()
end
end
cinp:lpush('</div></details>')
end
if co.who.id ~= uid and co.who.rights.powers.purge() then
var purgeconf: lib.str.acc purgeconf:init(48)
var purgestrs = array(
'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'eta', 'nu', 'kappa',
'emerald', 'carnelian', 'sapphire', 'ruby', 'amethyst', 'glory',
'hope', 'grace', 'pearl', 'carnation', 'rose', 'peony', 'poppy'
)
for i=0,3 do
purgeconf:push(purgestrs[lib.crypt.random(intptr,0,[purgestrs.type.N])],0)
if i ~= 2 then purgeconf:lpush('-') end
end
cinp:lpush('<details><summary>purge account</summary><p>you have the authority to destroy this account and all its associated content irreversibly and irretrievably. if you really wish to apply such an extreme sanction, enter the confirmation string <strong style="user-select:none">'):push(purgeconf.buf,purgeconf.sz):lpush('</strong> below and press the “alter” button to begin the process.</p><div class="elem"><label for="purge">purge confirmation string</label><input type="text" id="purge" name="purgekey"></div><input type="hidden" name="purgestr" value="'):push(purgeconf.buf,purgeconf.sz):lpush('"></details>')
purgeconf:free()
end
-- TODO black mark system? e.g. resolution option for badthink reports
-- adds a black mark to the offending user; they can be automatically banned
-- or brought up for review after a certain number of offenses; possibly lower
-- set of default privs for marked users
var cinpp = cinp:finalize() defer cinpp:free()
var unym: lib.str.acc unym:init(64)
unym:lpush('<a href="/')
if user(0).origin ~= 0 then unym:lpush('@') end
do var sanxid = lib.html.sanitize(user(0).xid, true)
unym:ppush(sanxid)
sanxid:free() end
unym:lpush('" class="id">')
lib.render.nym(user.ptr,0,&unym,false)
unym:lpush('</a>')
var ctlbox = data.view.conf_user_ctl {
name = unym:finalize();
inputcontent = cinpp;
btns = pstr{'',0};
}
if co.who.id ~= uid and co.who.rights.powers.cred() then
ctlbox.btns = lib.str.acc{}:compose('<a class="button" href="/conf/users/',path(2),'/cred">security & credentials</a>'):finalize()
end
var pg: lib.str.acc pg:init(512)
ctlbox:append(&pg)
ctlbox.name:free()
if ctlbox.btns.ct > 0 then ctlbox.btns:free() end
return pg:finalize()
end
else
var modes = array(P'local', P'remote', P'staff', P'titled', P'peons', P'all')
var idbuf: int8[lib.math.shorthand.maxlen]
var ulst: lib.str.acc ulst:init(256)
var mode: uint8 = mode_local
var modestr = co:pgetv('show')
ulst:lpush('<div style="text-align: right"><em>showing ')
|