Sun, 30 Dec 2018 09:43:36 +0000
client: Ignore timeout timer if we received a stanza
local async = require "scansion.async"; local new_error = require "scansion.error".new_error; local verse = require "verse".init("client"); local parse_xml = require "scansion.xml".parse; local default_stanza_timeout = 3; local stanzacmp = require "scansion.stanzacmp"; local function filter_expression(script, s) local expr = s:match("^%$%{(.+)%}$"); if not expr then return s end local name, value_name = expr:match("^(.+)'s (.+)$"); assert(name, "Unable to parse expression: "..expr); local key = value_name:lower():gsub(" ", "_"); assert(script.objects[name], "Unknown object called "..name); local value = script.objects[name][key]; assert(value ~= nil, "Unknown attribute (of "..name.."): "..value_name); return value; end local function fill_vars(script, stanza) for k, v in pairs(stanza.attr) do stanza.attr[k] = filter_expression(script, v); end for i, child in ipairs(stanza) do if type(child) == "string" then stanza[i] = filter_expression(script, child); elseif type(child) == "table" then fill_vars(script, child); end end return stanza; end return { _validate = function (client) assert(client.jid, "No JID specified"); client.stream = verse.new(verse.new_logger(client.name)); client.stream.connect_host = client.connect_host client.stream.connect_port = client.connect_port function client.log(fmt, ...) return client.stream:info(fmt, ...); end -- This one prints all received data client.stream:hook("incoming-raw", function (s) client.log("Data in: %s", s); end, 1000); client.stream:hook("outgoing-raw", function (s) client.log("Data out: %s", s); end, 1000); -- And incoming, parsed, stanzas client.stream:hook("stanza", function (s) client.log("Stanza: %s", s) end); -- Handle unexpected disconnects client.stream:hook("disconnected", function (s) if not (client.disconnect_expected or client.script.finished) or (s.reason and s.reason ~= "stream closed" and s.reason ~= "closed") then client.log("Unexpected disconnect!"); error("Unexpected disconnect"..(s.reason and " ("..tostring(s.reason)..")" or "")); end end); end; _finish = function (client) if client.stream.connected then client.disconnect_expected = true; client.stream:close(); end end; connects = function (client) local wait, done = async.waiter(); client.stream:hook("ready", function () client.stream.conn:pause() client.log"ready" done() client.log("ready done") end); client.stream:connect_client(client.jid, client.password); wait(); client.full_jid = client.stream.jid; client.host = client.stream.host; end; sends = function (client, data) local stanza = fill_vars(client.script, assert(parse_xml((table.concat(data):gsub("\t", " "))))); local wait, done = async.waiter(); local function handle_drained() client.stream:unhook("drained", handle_drained); done(); end client.stream:hook("drained", handle_drained); client.stream:send(stanza); wait(); end; receives = function (client, data) local wait, done = async.waiter(); local expected_stanza = false; local have_received_stanza = false; data = table.concat(data):gsub("\t", " "):gsub("^%s+", ""):gsub("%s+$", ""); if data ~= "nothing" then expected_stanza = fill_vars(client.script, assert(parse_xml(data))); end local function stanza_handler(received_stanza) have_received_stanza = true; if not expected_stanza then error(new_error("unexpected-stanza", { text = "Received unexpected stanza"; stanza = tostring(received_stanza); })); elseif not expected_stanza or not stanzacmp.stanzas_match(expected_stanza, received_stanza) then if not expected_stanza then client.log("Received a stanza when none were expected: %s", received_stanza); else client.log("Expected: %s", expected_stanza); client.log("Received: %s", received_stanza); end error(new_error("unexpected-stanza", { text = "Received unexpected stanza"; stanza = tostring(received_stanza); expected = expected_stanza and tostring(expected_stanza) or nil; })); else client.last_received_id = received_stanza.attr.id; client.log("YES! %s", expected_stanza) end expected_stanza = nil; client.stream:unhook("stanza", stanza_handler); client.stream.conn:pause(); client.log("Calling done") done(); end client.stream:hook("stanza", stanza_handler, 100); verse.add_task(client.stanza_timeout or default_stanza_timeout, function () if have_received_stanza then return; end if expected_stanza then client.log("TIMEOUT waiting for %s", expected_stanza) local e = new_error("stanza-timeout", { text = "Timed out waiting for stanza" }); error(e); end if expected_stanza == false then client.log("Good - no stanzas were received (expected)"); done(); end end); client.stream.conn:resume(); wait(); end; disconnects = function (client) client.disconnect_expected = true; client.stream:close(); end; }