Implement server discovery (.well-known/matrix/client), and re-authenticate if access token expired or was invalidated somehow
This commit is contained in:
parent
83dcd81e95
commit
62a21ca3c8
1 changed files with 104 additions and 77 deletions
177
matrix-send
177
matrix-send
|
@ -19,11 +19,11 @@
|
||||||
|
|
||||||
require "luarocks.loader"
|
require "luarocks.loader"
|
||||||
getopt = require "posix.unistd".getopt
|
getopt = require "posix.unistd".getopt
|
||||||
mkdir = require "posix.sys.stat".mkdir
|
mkdir = require "posix.sys.stat".mkdir
|
||||||
bname = require "posix.libgen".basename
|
bname = require "posix.libgen".basename
|
||||||
http = require "socket.http"
|
http = require "socket.http"
|
||||||
cjson = require "cjson"
|
cjson = require "cjson"
|
||||||
ltn12 = require "ltn12"
|
ltn12 = require "ltn12"
|
||||||
|
|
||||||
version = "2.0"
|
version = "2.0"
|
||||||
confdir = string.gsub("~/.config/matrix-send", '~', os.getenv("HOME"), 1)
|
confdir = string.gsub("~/.config/matrix-send", '~', os.getenv("HOME"), 1)
|
||||||
|
@ -31,7 +31,8 @@ confpath = confdir .. "/config.lua"
|
||||||
confvarv = os.getenv("MATRIXSEND_CONFIG")
|
confvarv = os.getenv("MATRIXSEND_CONFIG")
|
||||||
hostname = io.input("/etc/hostname"):read("l")
|
hostname = io.input("/etc/hostname"):read("l")
|
||||||
|
|
||||||
json = {}
|
matrix = {}
|
||||||
|
json = {}
|
||||||
uri = {}
|
uri = {}
|
||||||
sh = os.execute
|
sh = os.execute
|
||||||
|
|
||||||
|
@ -50,38 +51,6 @@ function fileexists(file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Makes an HTTP POST request to uri and sends body. Returns the output of the HTTP server.
|
|
||||||
function post(uri, body)
|
|
||||||
local respbody = {} -- for the response body
|
|
||||||
local result, respcode, respheaders, respstatus = http.request {
|
|
||||||
method = "POST",
|
|
||||||
url = uri,
|
|
||||||
source = ltn12.source.string(body),
|
|
||||||
headers = {
|
|
||||||
["content-type"] = "application/json",
|
|
||||||
["content-length"] = tostring(#body)
|
|
||||||
},
|
|
||||||
sink = ltn12.sink.table(respbody)
|
|
||||||
}
|
|
||||||
return table.concat(respbody)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Makes an HTTP PUT request to uri and sends body. Returns the output of the HTTP server.
|
|
||||||
function put(uri, body)
|
|
||||||
local respbody = {} -- for the response body
|
|
||||||
local result, respcode, respheaders, respstatus = http.request {
|
|
||||||
method = "PUT",
|
|
||||||
url = uri,
|
|
||||||
source = ltn12.source.string(body),
|
|
||||||
headers = {
|
|
||||||
["content-type"] = "application/json",
|
|
||||||
["content-length"] = tostring(#body)
|
|
||||||
},
|
|
||||||
sink = ltn12.sink.table(respbody)
|
|
||||||
}
|
|
||||||
return table.concat(respbody)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Prints a message to stdout in the following format:
|
-- Prints a message to stdout in the following format:
|
||||||
-- matrix-send: s
|
-- matrix-send: s
|
||||||
function msg(s)
|
function msg(s)
|
||||||
|
@ -104,20 +73,87 @@ function confpanic(s)
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
--- Matrix functions ---
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
function matrix.login(uri, json)
|
||||||
|
local respbody = {}
|
||||||
|
local result, respcode, respheaders, respstatus = http.request {
|
||||||
|
method = "POST",
|
||||||
|
url = uri,
|
||||||
|
source = ltn12.source.string(json),
|
||||||
|
headers = {
|
||||||
|
["content-type"] = "application/json",
|
||||||
|
["content-length"] = tostring(#json)
|
||||||
|
},
|
||||||
|
sink = ltn12.sink.table(respbody)
|
||||||
|
}
|
||||||
|
local body = table.concat(respbody)
|
||||||
|
local t = cjson.decode(body)
|
||||||
|
if t.error then
|
||||||
|
panic("server: " .. t.error)
|
||||||
|
else
|
||||||
|
return t.access_token
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function matrix.send(uri, json, login_uri, login_json)
|
||||||
|
local respbody = {}
|
||||||
|
local result, respcode, respheaders, respstatus = http.request {
|
||||||
|
method = "PUT",
|
||||||
|
url = uri,
|
||||||
|
source = ltn12.source.string(json),
|
||||||
|
headers = {
|
||||||
|
["content-type"] = "application/json",
|
||||||
|
["content-length"] = tostring(#json)
|
||||||
|
},
|
||||||
|
sink = ltn12.sink.table(respbody)
|
||||||
|
}
|
||||||
|
local body = table.concat(respbody)
|
||||||
|
local t = cjson.decode(body)
|
||||||
|
if t.error then
|
||||||
|
if t.errcode == "M_UNKNOWN_TOKEN" then
|
||||||
|
msg("token expired/invalidated; re-authenticating")
|
||||||
|
-- I mean, it works
|
||||||
|
t = setmetatable({}, {
|
||||||
|
__concat = function(left, right)
|
||||||
|
if type(left) == "string" then
|
||||||
|
return left .. tostring(right)
|
||||||
|
end
|
||||||
|
return tostring(left) .. tostring(right)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
t.token = matrix.login(login_uri, login_json)
|
||||||
|
if not cache.disable then
|
||||||
|
mkdir(cache.location)
|
||||||
|
cf = io.open(cache.location .. "/token", 'w+')
|
||||||
|
cf:write(t.token)
|
||||||
|
cf:close()
|
||||||
|
end
|
||||||
|
matrix.send(string.gsub(uri, "access_token=.+", "access_token=" .. t.token), json, login_uri, login_json)
|
||||||
|
else
|
||||||
|
panic("server: " .. t.error)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Default configuration file
|
-- Default configuration file
|
||||||
default_config = [[
|
default_config = [[
|
||||||
-- Default configuration file for matrix-send
|
-- Default configuration file for matrix-send
|
||||||
-- In comments, here is a simple example configuration.
|
-- In comments, here is a simple example configuration.
|
||||||
|
|
||||||
--login = {
|
--login = {
|
||||||
-- server = "matrix.envs.net",
|
-- server = "envs.net",
|
||||||
-- username = "john",
|
-- username = "john",
|
||||||
-- password = "examplepassword
|
-- password = "examplepassword
|
||||||
--}
|
--}
|
||||||
|
|
||||||
-- You could also do it this way:
|
-- You could also do it this way:
|
||||||
|
|
||||||
--login.server = "matrix.envs.net"
|
--login.server = "envs.net"
|
||||||
--login.username = "john"
|
--login.username = "john"
|
||||||
--login.password = "examplepassword"
|
--login.password = "examplepassword"
|
||||||
|
|
||||||
|
@ -242,9 +278,29 @@ else
|
||||||
txnid = 0
|
txnid = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
uri.login = string.format("https://%s/_matrix/client/v3/login", login.server)
|
-- Get .well-known
|
||||||
|
uri.well_known = string.format("https://%s/.well-known/matrix/client", login.server)
|
||||||
|
wellknown_body, errno = http.request(uri.well_known)
|
||||||
|
if errno ~= 200 then
|
||||||
|
panic("server: Error " .. errno .. " while getting /.well-known/matrix/client")
|
||||||
|
end
|
||||||
|
wellknown_t = cjson.decode(wellknown_body)
|
||||||
|
login.server = wellknown_t["m.homeserver"].base_url
|
||||||
|
|
||||||
|
-- Assign URIs and JSON bodies
|
||||||
|
uri.login = string.format("%s/_matrix/client/v3/login", login.server)
|
||||||
|
json.login = string.format([[
|
||||||
|
{
|
||||||
|
"identifier": {
|
||||||
|
"type": "m.id.user",
|
||||||
|
"user": "%s"
|
||||||
|
},
|
||||||
|
"initial_device_display_name": "matrix-send@%s",
|
||||||
|
"password": "%s",
|
||||||
|
"type": "m.login.password"
|
||||||
|
}
|
||||||
|
]], login.username, hostname, login.password)
|
||||||
|
|
||||||
-- Assemble various JSON messages
|
|
||||||
json.message = string.format([[
|
json.message = string.format([[
|
||||||
{
|
{
|
||||||
"body": "%s",
|
"body": "%s",
|
||||||
|
@ -259,33 +315,8 @@ if not login.token then
|
||||||
login.token = cf:read('l')
|
login.token = cf:read('l')
|
||||||
cf:close()
|
cf:close()
|
||||||
else
|
else
|
||||||
-- json.login should only be assigned if it needs to be assigned
|
-- Login
|
||||||
json.login = string.format([[
|
login.token = matrix.login(uri.login, json.login)
|
||||||
{
|
|
||||||
"identifier": {
|
|
||||||
"type": "m.id.user",
|
|
||||||
"user": "%s"
|
|
||||||
},
|
|
||||||
"initial_device_display_name": "matrix-send@%s",
|
|
||||||
"password": "%s",
|
|
||||||
"type": "m.login.password"
|
|
||||||
}
|
|
||||||
|
|
||||||
]], login.username, hostname, login.password)
|
|
||||||
|
|
||||||
-- Send the request!
|
|
||||||
local body = post(uri.login, json.login)
|
|
||||||
|
|
||||||
-- What else do I call it?
|
|
||||||
local t = cjson.decode(body)
|
|
||||||
|
|
||||||
-- If there was a server side error, print the error and exit.
|
|
||||||
if t.error then
|
|
||||||
panic(login.server .. ": " .. t.error)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Log the access token
|
|
||||||
login.token = t.access_token
|
|
||||||
|
|
||||||
-- Cache the access token
|
-- Cache the access token
|
||||||
if not cache.disable then
|
if not cache.disable then
|
||||||
|
@ -297,15 +328,11 @@ if not login.token then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
uri.message = string.format("https://%s/_matrix/client/v3/rooms/%s/send/m.room.message/%d?access_token=%s",
|
uri.message = string.format("%s/_matrix/client/v3/rooms/%s/send/m.room.message/%d?access_token=%s",
|
||||||
login.server, room_id, txnid, login.token)
|
login.server, room_id, txnid, login.token)
|
||||||
|
|
||||||
-- Send the message!
|
-- Send the message!
|
||||||
body = put(uri.message, json.message)
|
matrix.send(uri.message, json.message, uri.login, json.login)
|
||||||
t = cjson.decode(body)
|
|
||||||
if t.error then
|
|
||||||
panic(login.server .. ": " .. t.error)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Increment txnid and cache
|
-- Increment txnid and cache
|
||||||
if not cache.disable then
|
if not cache.disable then
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue