clix/adhoc.lua

1
local dataforms = require "prosody.util.dataforms";
2
 
3
local xmlns_validate = 'http://jabber.org/protocol/xdata-validate';
4
local function dataform_from_stanza(stanza)
5
	local layout = {
6
		title = stanza:get_child_text("title");
7
		instructions = stanza:get_child_text("instructions");
8
	};
9
	for tag in stanza:childtags("field") do
10
		local field = {
11
			name = tag.attr.var;
12
			label = tag.attr.label;
13
			type = tag.attr.type;
14
			required = tag:get_child("required") and true or nil;
15
			value = tag:get_child_text("value");
16
		};
17
		layout[#layout+1] = field;
18
		if field.type then
19
			local value = {};
20
			if field.type:match"list%-" then
21
				for tag in tag:childtags("option") do
22
					value[#value+1] = { label = tag.attr.label, value = tag:get_child_text("value") };
23
				end
24
				for tag in tag:childtags("value") do
25
					value[#value+1] = { label = tag.attr.label, value = tag:get_text(), default = true };
26
				end
27
			elseif field.type:match"%-multi" then
28
				for tag in tag:childtags("value") do
29
					value[#value+1] = tag.attr.label and { label = tag.attr.label, value = tag:get_text() } or tag:get_text();
30
				end
31
				if field.type == "text-multi" then
32
					field.value = table.concat(value, "\n");
33
				else
34
					field.value = value;
35
				end
36
			end
37
		end
38
		local datatype_tag = tag:get_child("validate", xmlns_validate);
39
		if datatype_tag then
40
			field.datatype = datatype_tag.attr.datatype;
41
			local range_tag = datatype_tag:get_child("range");
42
			if range_tag then
43
				field.range_min = tonumber(range_tag.attr.min);
44
				field.range_max = tonumber(range_tag.attr.max);
45
			end
46
		end
47
 
48
	end
49
	return dataforms.new(layout);
50
end
51
 
52
-- TODO Cleanup, commit
53
return function (opts, arg)
54
	if opts.short_help then
55
		print("Execute an Ad-Hoc Command");
56
		return;
57
	end
58
	local function on_connect(conn)
59
		if opts.node then
60
			conn:execute_command(opts.to or conn.host, opts.node, function(cmd)
61
				conn:info("status: %s", cmd.status);
62
				local note = cmd.note;
63
				if note then
64
					conn[note.attr.type or "info"](conn, note:get_text());
65
				end
66
				if cmd.status == "executing" then
67
					local data = {};
68
					for i=1,#arg do
69
						local k,v = arg[i]:match"^([^=]+)=(.*)";
70
						if k and v then
71
							data[k] = v; --FIXME multiple
72
						end
73
					end
74
					local command_form_layout = dataform_from_stanza(cmd.form)
75
					if opts.interactive then
76
						for i=1,#command_form_layout do
77
							local item = command_form_layout[i];
78
							if item.type ~= "hidden" and not data[item.name] then
79
								-- FIXME Current value isn't shown
80
								io.stderr:write(item.label..": ");
81
								if item.type:match"%-multi" then
82
									local t = { };
83
										repeat
84
										local line = io.read("*l");
85
										if line and line ~= "" then
86
											if item.type:match"list%-" then
87
												t[#t+1] = { label = line, value = line, default = true };
88
											else
89
												t[#t+1] = line;
90
											end
91
										end
92
									until not line or line == "";
93
									if item.type == "text-multi" then
94
										t = table.concat(t, "\n");
95
									end
96
									data[item.name] = t;
97
								--elseif item.type == "list-single" then
98
									--data[item.name] = { (io.read("*l")) };
99
								else
100
									data[item.name] = io.read("*l");
101
								end
102
							end
103
						end
104
					end
105
					cmd:next(command_form_layout:form(data, "submit"));
106
				elseif cmd.status == "completed" then
107
					if cmd.form then
108
						local command_form_layout = dataform_from_stanza(cmd.form)
109
						local data = command_form_layout:data(cmd.form);
110
						if data.title then
111
							print("= " .. data.title .. " =");
112
							print()
113
						end
114
						if data.instructions then
115
							print(data.instructions);
116
							print()
117
						end
118
						for i, item in ipairs(command_form_layout) do
119
							if item.type ~= "hidden" then
120
								print("== " .. (item.label or item.name) .. " ==")
121
								print(data[item.name]);
122
							end
123
						end
124
 
125
					end
126
					conn:close();
127
				else
128
					conn:warn("unhandled command status: %s", tostring(cmd.status));
129
				end
130
			end);
131
		else
132
			conn:disco_items(opts.to or conn.host, "http://jabber.org/protocol/commands", function(items)
133
				-- TODO It would be neat to be able to choose from this list
134
				if items then
135
					for i=1,#items do
136
						print(items[i].name, items[i].node);
137
					end
138
				end
139
				conn:close();
140
			end);
141
		end
142
	end
143
	clix_connect(opts, on_connect, {"adhoc"});
144
end