clix/raw.lua

1
local verse = require "verse";
2
local envload = require "prosody.util.envload".envload;
3
local xml = require "prosody.util.xml";
4
short_opts.i = "interactive";
5
short_opts.e = "echo";
6
 
7
return function (opts, args)
8
	if opts.short_help then
9
		print("Send/receive raw XML to/from the server");
10
		return;
11
	end
12
	
13
	local send_xml;
14
	if opts.stdin then
15
		send_xml = io.read("*a");
16
	end
17
	
18
	local function on_connect(conn)
19
		_G.conn = conn
20
		local print = print;
21
		local function stprint(stanza)
22
			if stanza.attr.to == conn.jid then
23
				stanza.attr.to = nil;
24
			end
25
			if opts.pretty and stanza.indent then
26
				return print(stanza:indent(1, "  "):pretty_print());
27
			end
28
			return print(stanza);
29
		end
30
		conn:hook("stanza", stprint, 1)
31
		if opts.interactive then
32
			local stdin = {
33
				getfd = function () return 0; end;
34
				dirty = function (self) return false; end;
35
				settimeout = function () end;
36
				send = function (_, d) return #d, 0; end;
37
				close = function () end;
38
				receive = function (_, patt)
39
					local data = io.stdin:read(patt);
40
				if data == nil then
41
					conn:close();
42
				end
43
					if opts.echo then
44
						io.write(data, patt == "*l" and "\n" or "");
45
					end
46
					return data;
47
				end
48
			};
49
			local stwrap;
50
			do
51
				local f_mt, r_mt = {}, {};
52
				function f_mt:__call(...) if ... and type(...) == "string" then return self{ to=... } end return self._f(...) end
53
				function f_mt:__index(k) return setmetatable({_st = self._f{ type = k }}, r_mt); end
54
				function r_mt:__call(to) self._st.attr.to = to; return self._st end
55
				function stwrap(f) return setmetatable({_f=f},f_mt) end
56
			end
57
			local env = setmetatable({}, { __index = {
58
				s = verse.stanza,
59
				m = stwrap(verse.message),
60
				p = stwrap(verse.presence),
61
				iq = stwrap(verse.iq),
62
				r = function ()
63
					return verse.stanza("r", { xmlns = "urn:xmpp:sm:3" });
64
				end,
65
				ping = function(host)
66
					return verse.iq{ type="get", to=host}:tag("ping", {xmlns="urn:xmpp:ping"});
67
				end,
68
				version = function(host)
69
					return verse.iq{ type="get", to=host}:query"jabber:iq:version";
70
				end,
71
				vcard = function(who)
72
					return verse.iq{ type="get", to=who}:tag("vCard",{xmlns="vcard-temp"});
73
				end,
74
				pubsub = function(...)
75
					return conn.pubsub(...);
76
				end,
77
				disco = function (to, node)
78
					return verse.iq{ type="get", to=to }:tag("query", { xmlns="http://jabber.org/protocol/disco#info", node = node });
79
				end,
80
				items = function (to, node)
81
					return verse.iq{ type="get", to=to }:tag("query", { xmlns="http://jabber.org/protocol/disco#items", node = node });
82
				end,
83
				join = function (roomnick)
84
					return verse.presence({ to=roomnick }):tag("x", { xmlns="http://jabber.org/protocol/muc" });
85
				end,
86
			}});
87
			local function on_incoming(stdin, data)
88
				if not data then
89
					conn:close();
90
					return
91
				end
92
				if data:sub(1,1) ~= "<" then
93
					local sandboxed = true;
94
					if data:sub(1,1) == ">" then
95
						sandboxed = false;
96
						data = data:sub(2);
97
					else
98
						data = "return "..data;
99
					end
100
					local chunk, err = envload(data, "@stdin", sandboxed and env or _G);
101
					if not chunk then
102
						conn:error(err);
103
						return;
104
					end
105
					data = "";
106
					local ok, ret = pcall(chunk);
107
					if ok then
108
						data = ret
109
					else
110
						conn:error(ret);
111
						return;
112
					end
113
				end
114
				if data then
115
					conn:send(data);
116
				end
117
				if not opts.interactive then
118
					conn:close();
119
				end
120
			end
121
			stdin = verse.server.wrapclient(stdin, "stdin", 0, {
122
				onincoming = on_incoming, ondisconnect = function () conn:close() end, onreadtimeout = function () return true; end
123
				}, "*l");
124
		else
125
			if not send_xml then
126
				send_xml = table.concat(args, " ");
127
			end
128
			send_xml = assert(xml.parse(send_xml));
129
			if send_xml.name == "iq" then
130
				conn:send_iq(send_xml, function ()
131
					conn:close();
132
				end);
133
			else
134
				conn:send(send_xml);
135
				conn:close();
136
			end
137
		end
138
 
139
	end
140
	local plugins = {};
141
	for i=#args,1,-1 do
142
		if args[i]:sub(1,1) == "+" then
143
			table.insert(plugins, table.remove(args, i):sub(2))
144
		end
145
	end
146
	return clix_connect(opts, on_connect, plugins);
147
end