diff --git a/matrix-send b/matrix-send index f042356..ab3cc7d 100755 --- a/matrix-send +++ b/matrix-send @@ -19,11 +19,11 @@ require "luarocks.loader" getopt = require "posix.unistd".getopt -mkdir = require "posix.sys.stat".mkdir -bname = require "posix.libgen".basename -http = require "socket.http" -cjson = require "cjson" -ltn12 = require "ltn12" + mkdir = require "posix.sys.stat".mkdir + bname = require "posix.libgen".basename + http = require "socket.http" + cjson = require "cjson" + ltn12 = require "ltn12" version = "2.0" confdir = string.gsub("~/.config/matrix-send", '~', os.getenv("HOME"), 1) @@ -31,9 +31,10 @@ confpath = confdir .. "/config.lua" confvarv = os.getenv("MATRIXSEND_CONFIG") hostname = io.input("/etc/hostname"):read("l") -json = {} - uri = {} - sh = os.execute +matrix = {} + json = {} + uri = {} + sh = os.execute ----------------- --- Functions --- @@ -50,38 +51,6 @@ function fileexists(file) 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: -- matrix-send: s function msg(s) @@ -104,20 +73,87 @@ function confpanic(s) os.exit(1) 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_config = [[ -- Default configuration file for matrix-send -- In comments, here is a simple example configuration. --login = { --- server = "matrix.envs.net", +-- server = "envs.net", -- username = "john", -- password = "examplepassword --} -- You could also do it this way: ---login.server = "matrix.envs.net" +--login.server = "envs.net" --login.username = "john" --login.password = "examplepassword" @@ -242,9 +278,29 @@ else txnid = 0 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([[ { "body": "%s", @@ -259,33 +315,8 @@ if not login.token then login.token = cf:read('l') cf:close() else - -- json.login should only be assigned if it needs to be assigned - 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) - - -- 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 + -- Login + login.token = matrix.login(uri.login, json.login) -- Cache the access token if not cache.disable then @@ -297,15 +328,11 @@ if not login.token then 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) -- Send the message! -body = put(uri.message, json.message) -t = cjson.decode(body) -if t.error then - panic(login.server .. ": " .. t.error) -end +matrix.send(uri.message, json.message, uri.login, json.login) -- Increment txnid and cache if not cache.disable then