Differences From
Artifact [557555fd0b]:
1 1 -- vim: ft=terra
2 2 local util = lib.util
3 3 local secmode = lib.enum { 'public', 'private', 'lockdown', 'isolate' }
4 4 local pstring = lib.mem.ptr(int8)
5 +local mimetypes = {
6 + {'html', 'text/html'};
7 + {'json', 'application/json'};
8 + {'json', 'application/activity+json'};
9 + {'json', 'application/ld+json'};
10 + {'mkdown', 'text/markdown'};
11 + {'text', 'text/plain'};
12 + {'ansi', 'text/x-ansi'};
13 +}
14 +
5 15 local struct srv
6 16 local struct cfgcache {
7 17 secret: pstring
8 18 pol_sec: secmode.t
9 19 pol_reg: bool
10 20 pol_autoherald: bool
11 21 credmgd: bool
................................................................................
178 188
179 189 local usrdefs = {
180 190 str = {
181 191 ['acl-follow' ] = {cfgfld = 'usrdef_pol_follow', fallback = 'local'};
182 192 ['acl-follow-req'] = {cfgfld = 'usrdef_pol_follow_req', fallback = 'all'};
183 193 };
184 194 }
195 +
196 +terra convo:matchmime(mime: lib.http.mime.t): bool
197 + return self.reqtype == [lib.http.mime.none]
198 + or self.reqtype == mime
199 +end
185 200
186 201 terra convo:usercfg_str(uid: uint64, setting: pstring): pstring
187 202 var set = self.srv:actor_conf_str_get(&self.srv.pool, uid, setting)
188 203 if not set then
189 204 [(function()
190 205 local q = quote return pstring.null() end
191 206 for key, dfl in pairs(usrdefs.str) do
................................................................................
316 331 if not lockdown then lockhdr = "" end
317 332 lib.net.mg_printf(self.con, "HTTP/1.1 200 OK\r\nContent-Type: %.*s\r\nContent-Length: %llu\r\n%sX-Content-Options: nosniff\r\n\r\n", mime.ct, mime.ptr, data.ct + 2, lockhdr)
318 333 lib.net.mg_send(self.con, data.ptr, data.ct)
319 334 lib.net.mg_send(self.con, '\r\n', 2)
320 335 end
321 336
322 337 terra convo:json(data: pstring)
323 - self:bytestream_trusted(false, 'application/ld+json', data:blob())
338 + self:bytestream_trusted(false, 'application/activity+json; charset=utf-8', data:blob())
324 339 end
325 340
326 341 terra convo:bytestream(mime: pstring, data: lib.mem.ptr(uint8))
327 342 -- TODO this is not a satisfactory solution; it's a bandaid on a gaping
328 343 -- chest wound. ultimately we need to compile a whitelist of safe mime
329 344 -- types as part of mimelib, but that is no small task. for now, this
330 345 -- will keep the patient from immediately bleeding out
................................................................................
370 385 p = p + lib.session.cookie_gen(self.srv.cfg.secret, aid, lib.osclock.time(nil), p)
371 386 lib.dbg('sending cookie ',{&sesskey[0],15})
372 387 p = lib.str.ncpy(p, '; Path=/', 9)
373 388 end
374 389 self:reroute_cookie(dest, &sesskey[0])
375 390 end
376 391
392 +terra convo:stra(sz: intptr) -- convenience function
393 + var s: lib.str.acc
394 + s:pool(&self.srv.pool,sz)
395 + return s
396 +end
397 +
398 +convo.methods.qstr = macro(function(self, ...) -- convenience string builder
399 + local exp = {...}
400 + return `lib.str.acc{}:pcompose(&self.srv.pool, [exp]):finalize()
401 +end)
402 +
377 403 terra convo:complain(code: uint16, title: rawstring, msg: rawstring)
378 404 if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end
379 405
380 - var ti: lib.str.acc ti:compose('error :: ', title)
381 - var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>')
382 - var body = [convo.page] {
383 - title = ti:finalize();
384 - body = bo:finalize();
385 - class = 'error';
386 - cache = false;
387 - }
388 -
389 - self:statpage(code, body)
390 -
391 - body.title:free()
392 - body.body:free()
406 + if self:matchmime(lib.http.mime.html) then
407 + var body = [convo.page] {
408 + title = self:qstr('error :: ', title);
409 + body = self:qstr('<div class="message"><img class="icon" src="/s/warn.svg"><h1>',title,'</h1><p>',msg,'</p></div>');
410 + class = 'error';
411 + cache = false;
412 + }
413 +
414 + self:statpage(code, body)
415 + else
416 + var pg = lib.http.page { respcode = code, body = pstring.null() }
417 + var ctt = lib.http.mime.none
418 + if self:matchmime(lib.http.mime.json) then ctt = lib.http.mime.json
419 + pg.body = ([lib.tpl.mk'{"_parsav_error":@$ekind, "_parsav_error_desc":@$edesc}']
420 + {ekind = title, edesc = msg}):poolstr(&self.srv.pool)
421 + elseif self:matchmime(lib.http.mime.text) then ctt = lib.http.mime.text
422 + pg.body = self:qstr('error: ',title,'\n',msg)
423 + elseif self:matchmime(lib.http.mime.mkdown) then ctt = lib.http.mime.mkdown
424 + pg.body = self:qstr('# error :: ',title,'\n\n',msg)
425 + elseif self:matchmime(lib.http.mime.ansi) then ctt = lib.http.mime.ansi
426 + pg.body = self:qstr('\27[1;31merror :: ',title,'\27[m\n',msg)
427 + end
428 + var cthdr = lib.http.header { 'Content-Type', 'text/plain' }
429 + if ctt == lib.http.mime.none then
430 + pg.headers.ct = 0
431 + else
432 + pg.headers = lib.typeof(pg.headers) { &cthdr, 1 }
433 + switch ctt do
434 + case [ctt.type](lib.http.mime.json) then
435 + cthdr.value = 'application/json'
436 + end
437 + escape
438 + for i,v in ipairs(mimetypes) do local key,mime = v[1],v[2]
439 + if key ~= 'json' then
440 + emit quote case [ctt.type](lib.http.mime.[key]) then cthdr.value = [mime] end end
441 + end
442 + end
443 + end
444 + end
445 + end
446 + pg:send(self.con)
447 + end
448 +end
449 +
450 +terra convo:fail(code: uint16)
451 + switch code do
452 + escape
453 + local stderrors = {
454 + {400, 'bad request', "the action you have attempted on this resource is not meaningful"};
455 + {401, 'unauthorized', "this resource is not available at your clearance level"};
456 + {403, 'forbidden', "we can neither confirm nor deny the existence of this resource"};
457 + {404, 'resource not found', "that resource is not extant on or known to this server"};
458 + {405, 'method not allowed', "the method you have attempted on this resource is not meaningful"};
459 + {406, 'not acceptable', "none of the suggested content types are a viable representation of this resource"};
460 + {500, 'internal server error', "parsav did a fucksy wucksy"};
461 + }
462 +
463 + for i,v in ipairs(stderrors) do
464 + emit quote case uint16([v[1]]) then
465 + self:complain([v])
466 + end end
467 + end
468 + end
469 + else self:complain(500,'unknown error','an unrecognized error was thrown. this is a bug')
470 + end
393 471 end
394 472
395 473 terra convo:confirm(title: pstring, msg: pstring, cancel: pstring)
396 474 var conf = data.view.confirm {
397 475 title = title;
398 476 query = msg;
399 477 cancel = cancel;
................................................................................
405 483 class = 'query';
406 484 body = body; cache = false;
407 485 }
408 486 self:stdpage(cf)
409 487 --cf.title:free()
410 488 end
411 489
412 -terra convo:stra(sz: intptr) -- convenience function
413 - var s: lib.str.acc
414 - s:pool(&self.srv.pool,sz)
415 - return s
416 -end
417 -
418 -convo.methods.qstr = macro(function(self, ...) -- convenience string builder
419 - local exp = {...}
420 - return `lib.str.acc{}:pcompose(&self.srv.pool, [exp]):finalize()
421 -end)
422 -
423 490 convo.methods.assertpow = macro(function(self, pow)
424 491 return quote
425 492 var ok = true
426 493 if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then
427 494 ok = false
428 495 self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action'])
429 496 end
................................................................................
504 571 end
505 572 terra convo:pgetv(name: rawstring)
506 573 var s,l = self:getv(name)
507 574 return pstring { ptr = s, ct = l }
508 575 end
509 576
510 577 local route = {} -- these are defined in route.t, as they need access to renderers
511 -terra route.dispatch_http :: {&convo, lib.mem.ptr(int8), lib.http.method.t} -> {}
512 -
513 -local mimetypes = {
514 - {'html', 'text/html'};
515 - {'json', 'application/json'};
516 - {'json', 'application/ld+json'};
517 - {'json', 'application/activity+json'};
518 - {'mkdown', 'text/markdown'};
519 - {'text', 'text/plain'};
520 - {'ansi', 'text/x-ansi'};
521 -}
578 +terra route.dispatch_http :: {&convo, lib.mem.ptr(int8)} -> {}
522 579
523 580 local mimevar = symbol(lib.mem.ref(int8))
524 581 local mimeneg = `lib.http.mime.none
525 582
526 583 for i, t in ipairs(mimetypes) do
527 584 local name, mime = t[1], t[2]
528 585 mimeneg = quote
................................................................................
821 878 end
822 879 bsr:free()
823 880 upmap:free()
824 881 end
825 882 end
826 883 end
827 884
828 - route.dispatch_http(&co, uri, co.method)
885 + route.dispatch_http(&co, uri)
829 886
830 887 ::fail::
831 888 if co.uploads.run > 0 then
832 889 for i=0,co.uploads.sz do
833 890 co.uploads(i).filename:free()
834 891 co.uploads(i).field:free()
835 892 end