File

clix/raw.lua @ 150:af9b9acb10d6

clix.raw: Track and print response to iq stanza given as cli argument While fire-and-forget is okay for messages and presence, it's kinda useless for a lot of IQs uses, so this is pretty nice!
author Kim Alvefur <zash@zash.se>
date Sat, 10 Apr 2021 00:13:23 +0200
parent 149:014e73c329c5
child 151:5398623682d7
line wrap: on
line source

local verse = require "verse";
local xml = require "util.xml";
short_opts.i = "interactive";
short_opts.e = "echo";

return function (opts, args)
	if opts.short_help then
		print("Send/receive raw XML to/from the server");
		return;
	end
	
	local send_xml;
	if opts.stdin then
		send_xml = io.read("*a");
	end
	
	local function on_connect(conn)
		local print = print;
		local function stprint(stanza)
			if stanza.attr.to == conn.jid then
				stanza.attr.to = nil;
			end
			if opts.pretty and stanza.indent then
				return print(stanza:indent(1, "  "):pretty_print());
			end
			return print(stanza);
		end
		conn:hook("stanza", stprint)
		if opts.interactive then
			local stdin = {
				getfd = function () return 0; end;
				dirty = function (self) return false; end;
				settimeout = function () end;
				send = function (_, d) return #d, 0; end;
				close = function () end;
				receive = function (_, patt)
					local data = io.stdin:read(patt);
				if data == nil then
					conn:close();
				end
					if opts.echo then
						io.write(data, patt == "*l" and "\n" or "");
					end
					return data;
				end
			};
			local stwrap;
			do
				local f_mt, r_mt = {}, {};
				function f_mt:__call(...) if ... and type(...) == "string" then return self{ to=... } end return self._f(...) end
				function f_mt:__index(k) return setmetatable({_st = self._f{ type = k }}, r_mt); end
				function r_mt:__call(to) self._st.attr.to = to; return self._st end
				function stwrap(f) return setmetatable({_f=f},f_mt) end
			end
			local env = setmetatable({}, { __index = {
				s = verse.stanza,
				m = stwrap(verse.message),
				p = stwrap(verse.presence),
				iq = stwrap(verse.iq),
				ping = function(host)
					return verse.iq{ type="get", to=host}:tag("ping", {xmlns="urn:xmpp:ping"});
				end,
				version = function(host)
					return verse.iq{ type="get", to=host}:query"jabber:iq:version";
				end,
				vcard = function(who)
					return verse.iq{ type="get", to=who}:tag("vCard",{xmlns="vcard-temp"});
				end,
				pubsub = function(...)
					return conn.pubsub(...);
				end,
				disco = function (to, node)
					return verse.iq{ type="get", to=to }:tag("query", { xmlns="http://jabber.org/protocol/disco#info", node = node });
				end,
				items = function (to, node)
					return verse.iq{ type="get", to=to }:tag("query", { xmlns="http://jabber.org/protocol/disco#items", node = node });
				end,
				join = function (roomnick)
					return verse.presence({ to=roomnick }):tag("x", { xmlns="http://jabber.org/protocol/muc" });
				end,
			}});
			local function on_incoming(stdin, data)
				if not data then
					conn:close();
					return
				end
				if data:sub(1,1) ~= "<" then
					local chunk, err = loadstring("return "..data, "@stdin");
					if not chunk then
						conn:error(err);
						return;
					end
					data = "";
					setfenv(chunk, env);
					local ok, ret = pcall(chunk);
					if ok then
						data = ret
					else
						conn:error(ret);
						return;
					end
				end
				if data then
					conn:send(data);
				end
				if not opts.interactive then
					conn:close();
				end
			end
			stdin = require "net.server".wrapclient(stdin, "stdin", 0, {
				onincoming = on_incoming, ondisconnect = function () end
				}, "*l");
		else
			if not send_xml then
				send_xml = table.concat(args, " ");
			end
			send_xml = assert(xml.parse(send_xml));
			if send_xml.name == "iq" then
				conn:send_iq(send_xml, function ()
					conn:close();
				end);
			else
				conn:send(send_xml);
				conn:close();
			end
		end

	end
	local plugins = {};
	for i=#args,1,-1 do
		if args[i]:sub(1,1) == "+" then
			table.insert(plugins, table.remove(args, i):sub(2))
		end
	end
	return clix_connect(opts, on_connect, plugins);
end