193 lines
5.2 KiB
Lua
193 lines
5.2 KiB
Lua
local json = require("json")
|
|
local socket = require("socket")
|
|
local unix = require("socket.unix")
|
|
local bit32 = require("bit32")
|
|
local ssb = { VERSION = '0.1' }
|
|
|
|
local function get_ssb_dir()
|
|
local home = os.getenv("HOME")
|
|
return string.format("%s/.ssb", home)
|
|
end
|
|
|
|
local function read_manifest()
|
|
local manifest = get_ssb_dir() .. "/manifest.json"
|
|
local f = io.open(manifest, "rb")
|
|
if not f then
|
|
return nil, "cannot find manifest: " .. manifest
|
|
end
|
|
local buf = f:read("*all")
|
|
f:close()
|
|
local js = json.decode(buf)
|
|
return js, nil
|
|
end
|
|
|
|
function do_read(conn, id, final)
|
|
local buf, err = conn:receive(9)
|
|
if string.len(buf) < 9 then
|
|
minetest.log("do_read: header len should be 9 but is: " .. string.len(buf))
|
|
return nil, string.len(buf)
|
|
end
|
|
|
|
local first = string.byte(buf, 1)
|
|
local len = bit32.bor(bit32.lshift(string.byte(buf, 2), 24),
|
|
bit32.lshift(string.byte(buf, 3), 16),
|
|
bit32.lshift(string.byte(buf, 4), 8),
|
|
string.byte(buf, 5))
|
|
-- remove minus sign
|
|
local id = bit32.bxor(bit32.bor(bit32.lshift(string.byte(buf, 6), 24),
|
|
bit32.lshift(string.byte(buf, 7), 16),
|
|
bit32.lshift(string.byte(buf, 8), 8),
|
|
string.byte(buf, 9)), 0xFFFFFFFF) + 1
|
|
-- minetest.log("do_read: len: " .. len)
|
|
-- minetest.log("do_read: id : " .. id)
|
|
if id ~= 1 then
|
|
minetest.log("do_read: id should be 1 but is: " .. id)
|
|
-- minetest.log("do_read: " .. string.byte(buf, 6) .. string.byte(buf, 7) .. string.byte(buf, 8) .. string.byte(buf, 9))
|
|
end
|
|
return conn:receive(len)
|
|
end
|
|
|
|
function do_write(conn, req, mtype, ptype, id, final)
|
|
-- first byte construction
|
|
local pkt = {
|
|
buffer = 0,
|
|
string = 1,
|
|
json = 2,
|
|
}
|
|
|
|
local pktype = pkt[ptype]
|
|
local stream
|
|
if mtype == "async" then stream = 0 else stream = 1 end
|
|
local fin
|
|
if final then fin = 1 else fin = 0 end
|
|
local first = string.char(bit32.bor(bit32.lshift(stream, 3), bit32.lshift(fin, 2), bit32.band(pktype, 3)))
|
|
|
|
-- big-endian header construction
|
|
local b0 = bit32.rshift(bit32.band(string.len(req), 0xFF000000), 24)
|
|
local b1 = bit32.rshift(bit32.band(string.len(req), 0x00FF0000), 16)
|
|
local b2 = bit32.rshift(bit32.band(string.len(req), 0x0000FF00), 8)
|
|
local b3 = bit32.band(string.len(req), 0x000000FF)
|
|
-- limitation to 255 ids
|
|
local head = string.char(b0, b1, b2, b3, 0, 0, 0, id)
|
|
|
|
-- concat
|
|
local buf = first .. head .. req
|
|
return conn:send(buf)
|
|
end
|
|
|
|
function do_goodbye(conn)
|
|
return do_write(conn, "", "async", "buffer", 0, true)
|
|
end
|
|
|
|
function rpc_async(conn, meth, mtype, param)
|
|
-- wrap
|
|
local req = json.encode({
|
|
name = meth,
|
|
args = {param},
|
|
})
|
|
|
|
local ret, err = do_write(conn, req, mtype, "json", 1, false)
|
|
-- minetest.log("do_write: len: " .. ret)
|
|
if err then
|
|
minetest.log("do_write: " .. err)
|
|
return ret, err
|
|
end
|
|
|
|
ret, err = do_read(conn, 1, false)
|
|
-- minetest.log("do_read: len: " .. string.len(ret))
|
|
if err then
|
|
minetest.log("do_read: " .. err)
|
|
return ret, err
|
|
end
|
|
|
|
do_goodbye(conn)
|
|
|
|
-- local ret = json.encode({
|
|
-- name = meth,
|
|
-- type = mtype,
|
|
-- arg = param,
|
|
-- })
|
|
-- local err = nil
|
|
return ret, err
|
|
end
|
|
|
|
function muxrpc(conn, meth, mtype, param)
|
|
local switch = {
|
|
async = rpc_async,
|
|
-- TODO implement all types
|
|
}
|
|
|
|
local rpc = switch[mtype]
|
|
if not rpc then
|
|
return nil, "method type unimplemented yet."
|
|
end
|
|
|
|
return rpc(conn, meth, mtype, param)
|
|
end
|
|
|
|
local function parse_msgs(arr)
|
|
local r = {}
|
|
for i, msg in ipairs(arr) do
|
|
r[i] = json.decode(msg)
|
|
end
|
|
return r
|
|
end
|
|
|
|
local mt = {
|
|
__index = function(self, k)
|
|
local manifest, err = read_manifest()
|
|
if err then
|
|
return function(s, ...)
|
|
return nil, "cannot initialize: " .. err
|
|
end
|
|
end
|
|
|
|
return function(s, ...)
|
|
local res, err = nil
|
|
|
|
if not manifest[k] then
|
|
return err, "method not found: " .. k
|
|
end
|
|
|
|
local mtype = manifest[k]
|
|
|
|
local params = {...}
|
|
local js
|
|
-- for key, val in ipairs(params) do
|
|
-- minetest.log(key .. "=" .. json.encode(val))
|
|
-- end
|
|
-- take only first param
|
|
js = params[1]
|
|
|
|
local c = unix()
|
|
local ret, err = c:connect(get_ssb_dir() .. "/socket")
|
|
if err then
|
|
return c, err
|
|
end
|
|
|
|
local ret, err = muxrpc(c, k, mtype, js)
|
|
if err then
|
|
c:close()
|
|
return ret, err
|
|
end
|
|
|
|
-- minetest.log(ret)
|
|
if not err then
|
|
if (type(ret) ~= "table") then
|
|
res = json.decode(ret)
|
|
else
|
|
res = parse_msgs(ret)
|
|
end
|
|
else
|
|
res = nil
|
|
err = ret
|
|
end
|
|
|
|
return res, err
|
|
end
|
|
end
|
|
}
|
|
|
|
setmetatable(ssb, mt)
|
|
return ssb
|