Index: common.lua
==================================================================
--- common.lua
+++ common.lua
@@ -22,11 +22,54 @@
local t = fd:read('*a')
return chomp(t), fd:close()
end
end
-local function dump(v,pfx,cyc)
+local function copy(a)
+ local new = {}
+ for k,v in pairs(a) do new[k] = v end
+ return new
+end
+
+local function cat(a,b)
+ a = copy(a)
+ local ofs = #a
+ for k,v in pairs(b) do
+ if type(k) == 'number' then
+ a[k+ofs] = v
+ else a[k] = v end
+ end
+ return a
+end
+
+local function search(tbl,pred,lst,path)
+ lst = lst or {} path = path or {}
+ if type(pred) ~= 'function' then
+ local val = pred
+ pred = function(a,k)
+ if type(a) == 'table' and a ~= val then return end
+ return a == val
+ end
+ end
+ for k,v in pairs(tbl) do
+ local res = pred(v,k)
+ local np = cat(path, {tbl})
+ if res == true then
+ table.insert(lst, {
+ key = k;
+ value = v;
+ parent = tbl;
+ path = np;
+ })
+ elseif res == nil then
+ search(v,pred,lst,np)
+ end
+ end
+ return lst
+end
+
+local function dump(v,pfx,cyc,ismeta)
pfx = pfx or ''
cyc = cyc or {}
local np = pfx .. ' '
if type(v) == 'table' then
@@ -39,15 +82,24 @@
local str = ''
for k,v in pairs(v) do
local tkey, tval = dump(k,np,cyc), dump(v,np,cyc)
str = str .. string.format('%s[%s] = %s\n', np, tkey,tval)
end
- return '{\n' .. str .. pfx .. '}\n'
+ local meta = ''
+ if getmetatable(v) then
+ meta = dump(getmetatable(v),pfx,cyc,true) .. '::'
+ end
+ if ismeta then
+ return string.format('%s<|\n%s%s|>',meta,str,pfx)
+ else
+ return meta..'{\n' .. str .. pfx .. '}\n'
+ end
else
return string.format('%s', v)
end
end
+
local ping = function(path)
local f = io.open(path)
if f then f:close() return true end
return false
end
@@ -70,16 +122,14 @@
end
math.randomseed(seed)
else math.randomseed(os.time()) end
return {
- exec = exec;
dump = dump;
- ping = ping;
- chomp = chomp;
- map = map;
- tobool = tobool;
+ exec = exec, ping = ping;
+ map = map, copy = copy, cat = cat, search = search;
+ chomp = chomp, tobool = tobool;
find = function(lst,pred)
for k,v in pairs(lst) do
local test = pred(v,k)
if test then return test end
end
@@ -96,14 +146,17 @@
return s
end;
append = function(a,b)
for _, v in pairs(b) do a[#a+1] = v end
end;
- has = function(haystack,needle,eq)
- eq = eq or function(a,b) return a == b end
+ has = function(haystack,pred)
+ if type(pred) ~= 'function' then
+ local val = pred
+ pred = function(a) return a == val end
+ end
for k,v in pairs(haystack) do
- if eq(needle,v) then return k end
+ if pred(v,k) then return k,v end
end
end;
keys = function(ary)
local kt = {}
for k,v in pairs(ary) do kt[#kt+1] = k end
Index: mem.t
==================================================================
--- mem.t
+++ mem.t
@@ -108,10 +108,13 @@
end
terra t.methods.null(): t return t { ptr = nil, ct = 0 } end -- maybe should be a macro?
terra t:ref() return self.ptr ~= nil end
t.metamethods.__not = macro(function(self) return `not self:ref() end)
t.metamethods.__apply = macro(function(self,idx) return `self.ptr[ [idx or 0] ] end)
+ t.metamethods.__add = terra(self: &t, sz: intptr): t
+ var n = @self n:advance(sz) return n
+ end
t.metamethods.__update = macro(function(self,idx,rhs)
return quote self.ptr[idx] = rhs end end)
t.metamethods.__cast = function(from,to,exp)
if to == t then
if from == niltype then return `t.null()
Index: route.t
==================================================================
--- route.t
+++ route.t
@@ -925,12 +925,41 @@
::e404:: do co:complain(404, 'artifact not found', 'no such artifact has been uploaded to this instance') return end
end
local json = {}
-terra json.webfinger(co: &lib.srv.convo)
-
+do wftpl = lib.tpl.mk [[{
+ "subject": @$subj,
+ "links": [
+ { "rel": "self", "type": "application/ld+json", "href": @$href }
+ ]
+ }]]
+ terra json.webfinger(co: &lib.srv.convo)
+ var res = co:pgetv('resource')
+ if (not res) or not res:startswith 'acct:' then goto err end
+
+ -- technically we should look this user up in the database to make sure
+ -- they actually exist, buuut that's costly and i doubt that's actually
+ -- necessary for webfinger to do its job. so we cheat and just do string
+ -- munging so lookups are as cheap as possible. TODO make sure this works
+ -- in practice and doesn't cause any weird security problems
+ var acct = res + 5
+ var svp = lib.str.find(acct, '@')
+ if svp:ref() then
+ acct.ct = (svp.ptr - acct.ptr)
+ svp:advance(1)
+ if not svp:cmp(co.srv.cfg.domain) then goto err end
+ end
+ var tp = wftpl {
+ subj = res;
+ href = co:qstr('https://', co.srv.cfg.domain, '/@', acct);
+ }
+ co:json(tp:poolstr(&co.srv.pool))
+
+ do return end -- error conditions
+ ::err:: do co:json('{}') return end
+ 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})
Index: srv.t
==================================================================
--- srv.t
+++ srv.t
@@ -10,10 +10,11 @@
pol_autoherald: bool
credmgd: bool
maxupsz: intptr
poolinitsz: intptr
instance: pstring
+ domain: pstring
overlord: &srv
ui_cue_staff: pstring
ui_cue_founder: pstring
ui_hue: uint16
nranks: uint16
@@ -30,13 +31,14 @@
cfg: cfgcache
id: rawstring
pool: lib.mem.pool
}
-terra cfgcache:free() -- :/
+terra cfgcache:free() -- :/ TODO replace with pool
self.secret:free()
self.instance:free()
+ self.domain:free()
self.ui_cue_staff:free()
self.ui_cue_founder:free()
self.usrdef_pol_follow:free()
self.usrdef_pol_follow_req:free()
end
@@ -509,10 +511,12 @@
terra route.dispatch_http :: {&convo, lib.mem.ptr(int8), lib.http.method.t} -> {}
local mimetypes = {
{'html', 'text/html'};
{'json', 'application/json'};
+ {'json', 'application/ld+json'};
+ {'json', 'application/activity+json'};
{'mkdown', 'text/markdown'};
{'text', 'text/plain'};
{'ansi', 'text/x-ansi'};
}
@@ -531,29 +535,35 @@
local handle = {
http = terra(con: &lib.net.mg_connection, event_kind: int, event: &opaque, userdata: &opaque)
var server = [&srv](userdata)
var mgpeer = getpeer(con)
- var peer = lib.store.inet { port = mgpeer.port; }
- if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end
- if peer.pv == 6 then
- for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end
- else -- v4
- @[&uint32](&peer.v4) = mgpeer.ip
- end
+ -- var pbuf: int8[128]
+
-- the peer property is currently broken and there is precious
-- little i can do about this -- it always reports a peer v4 IP
- -- of 0.0.0.0, altho the port seems to come through correctly.
- -- for now i'm leaving it as is, but note that netmask restrictions
- -- WILL NOT WORK until upstream gets its shit together. FIXME
+ -- of 0.0.0.0 for v6 connections, altho the port seems to come
+ -- through correctly. -- for now i'm leaving it as is, but note
+ -- that netmask restrictions WILL NOT WORK until upstream gets
+ -- its shit together. FIXME
-- needs to check for an X-Forwarded-For header from nginx and
-- use that instead of the peer iff peer is ::1/127.1 FIXME
-- maybe also haproxy support?
switch event_kind do
case lib.net.MG_EV_HTTP_MSG then
+ -- lib.net.mg_ntoa(&mgpeer,&pbuf[0],127)
+ -- lib.dbg('got connection from client ',&pbuf[0])
+ var peer = lib.store.inet { port = mgpeer.port; }
+ if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end
+ if peer.pv == 6 then
+ for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end
+ else -- v4
+ @[&uint32](&peer.v4) = mgpeer.ip
+ end
+
lib.dbg('routing HTTP request')
var msg = [&lib.net.mg_http_message](event)
var co = convo {
con = con, srv = server, msg = msg;
aid = 0, aid_issue = 0, who = nil;
@@ -1112,10 +1122,11 @@
return default
end
terra cfgcache:load()
self.instance = self.overlord:conf_get('instance-name')
+ self.domain = self.overlord:conf_get('domain')
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)
Index: str.t
==================================================================
--- str.t
+++ str.t
@@ -61,10 +61,17 @@
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:startswith(other: ty)
+ for i=0, other.ct do
+ if other(i) == 0 then return true end
+ if other(i) ~= self(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 }
@@ -561,7 +568,23 @@
if add:ref() then acc:ppush(add) end
cur = cont
end
return acc:finalize()
end
+
+terra m.qesc(pool: &lib.mem.pool, str: m.t): m.t
+ -- escape double-quotes
+ var a: m.acc a:pool(pool, str.ct + str.ct/2)
+ a:lpush '"'
+ for i=0, str.ct do
+ if str(i) == @'"' then a:lpush '\\"'
+ elseif str(i) == @'\\' then a:lpush '\\\\'
+ elseif str(i) < 0x20 then -- for json
+ var hex = lib.math.hexbyte(str(i))
+ a:lpush('\\u00'):push(&hex[0], 2)
+ else a:push(str.ptr + i,1) end
+ end
+ a:lpush '"'
+ return a:finalize()
+end
return m
Index: tpl.t
==================================================================
--- tpl.t
+++ tpl.t
@@ -36,11 +36,11 @@
str = str:gsub(' ?', file)
end)
- for start, mode, key, stop in string.gmatch(str,'()'..tplchar..'([:!]?)([-a-zA-Z0-9_]+)()') do
+ for start, mode, key, stop in string.gmatch(str,'()'..tplchar..'([:!$]?)([-a-zA-Z0-9_]+)()') do
if string.sub(str,start-1,start-1) ~= '\\' then
segs[#segs+1] = string.sub(str,last,start-1)
fields[#segs] = { key = key:gsub('-','_'), mode = (mode ~= '' and mode or nil) }
last = stop
end
@@ -75,11 +75,13 @@
field = fld.key;
type = lib.mem.ptr(int8);
}
end
kfac[fld.key] = (kfac[fld.key] or 0) + 1
- sanmode[fld.key] = fld.mode == ':' and 6 or fld.mode == '!' and 5 or 1
+ sanmode[fld.key] = fld.mode == ':' and 6
+ or fld.mode == '!' and 5
+ or fld.mode == '$' and 2 or 1
end
for key, fac in pairs(kfac) do
local sanfac = sanmode[key]
tallyup[#tallyup + 1] = quote
@@ -101,27 +103,33 @@
senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
appenders[#appenders+1] = quote [accumulator]:push([seg], [#seg]) end
if fields[idx] and fields[idx].mode then
local f = fields[idx]
local fp = `symself.[f.key]
+ local sanexp
+ if f.mode == '$' then
+ sanexp = `lib.str.qesc(pool, fp)
+ else
+ sanexp = `lib.html.sanitize(pool, fp, [f.mode == ':'])
+ end
copiers[#copiers+1] = quote
if fp.ct > 0 then
- var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
+ var san = sanexp
[cpypos] = lib.mem.cpy([cpypos], [&opaque](san.ptr), san.ct)
--san:free()
end
end
senders[#senders+1] = quote
if fp.ct > 0 then
- var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
+ var san = sanexp
lib.net.mg_send([destcon], san.ptr, san.ct)
--san:free()
end
end
appenders[#appenders+1] = quote
if fp.ct > 0 then
- var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
+ var san = sanexp
[accumulator]:ppush(san)
--san:free()
end
end
elseif fields[idx] then