clix/adhoc.lua

Sat, 24 Jun 2023 09:59:07 +0200

author
Kim Alvefur <zash@zash.se>
date
Sat, 24 Jun 2023 09:59:07 +0200
changeset 170
0d561f921c13
parent 168
75e8ca131178
permissions
-rw-r--r--

clix.adhoc: Move stanza to dataform converter here

Removes the need for verse to have a custom util.dataforms fork only for
this

local dataforms = require "prosody.util.dataforms";

local xmlns_validate = 'http://jabber.org/protocol/xdata-validate';
local function dataform_from_stanza(stanza)
	local layout = {
		title = stanza:get_child_text("title");
		instructions = stanza:get_child_text("instructions");
	};
	for tag in stanza:childtags("field") do
		local field = {
			name = tag.attr.var;
			label = tag.attr.label;
			type = tag.attr.type;
			required = tag:get_child("required") and true or nil;
			value = tag:get_child_text("value");
		};
		layout[#layout+1] = field;
		if field.type then
			local value = {};
			if field.type:match"list%-" then
				for tag in tag:childtags("option") do
					value[#value+1] = { label = tag.attr.label, value = tag:get_child_text("value") };
				end
				for tag in tag:childtags("value") do
					value[#value+1] = { label = tag.attr.label, value = tag:get_text(), default = true };
				end
			elseif field.type:match"%-multi" then
				for tag in tag:childtags("value") do
					value[#value+1] = tag.attr.label and { label = tag.attr.label, value = tag:get_text() } or tag:get_text();
				end
				if field.type == "text-multi" then
					field.value = table.concat(value, "\n");
				else
					field.value = value;
				end
			end
		end
		local datatype_tag = tag:get_child("validate", xmlns_validate);
		if datatype_tag then
			field.datatype = datatype_tag.attr.datatype;
			local range_tag = datatype_tag:get_child("range");
			if range_tag then
				field.range_min = tonumber(range_tag.attr.min);
				field.range_max = tonumber(range_tag.attr.max);
			end
		end

	end
	return dataforms.new(layout);
end

-- TODO Cleanup, commit
return function (opts, arg)
	if opts.short_help then
		print("Execute an Ad-Hoc Command");
		return;
	end
	local function on_connect(conn)
		if opts.node then
			conn:execute_command(opts.to or conn.host, opts.node, function(cmd)
				conn:info("status: %s", cmd.status);
				local note = cmd.note;
				if note then
					conn[note.attr.type or "info"](conn, note:get_text());
				end
				if cmd.status == "executing" then
					local data = {};
					for i=1,#arg do
						local k,v = arg[i]:match"^([^=]+)=(.*)";
						if k and v then
							data[k] = v; --FIXME multiple
						end
					end
					local command_form_layout = dataform_from_stanza(cmd.form)
					if opts.interactive then
						for i=1,#command_form_layout do
							local item = command_form_layout[i];
							if item.type ~= "hidden" and not data[item.name] then
								-- FIXME Current value isn't shown
								io.stderr:write(item.label..": ");
								if item.type:match"%-multi" then
									local t = { };
										repeat
										local line = io.read("*l");
										if line and line ~= "" then
											if item.type:match"list%-" then
												t[#t+1] = { label = line, value = line, default = true };
											else
												t[#t+1] = line;
											end
										end
									until not line or line == "";
									if item.type == "text-multi" then
										t = table.concat(t, "\n");
									end
									data[item.name] = t;
								--elseif item.type == "list-single" then
									--data[item.name] = { (io.read("*l")) };
								else
									data[item.name] = io.read("*l");
								end
							end
						end
					end
					cmd:next(command_form_layout:form(data, "submit"));
				elseif cmd.status == "completed" then
					if cmd.form then
						local command_form_layout = dataform_from_stanza(cmd.form)
						local data = command_form_layout:data(cmd.form);
						if data.title then
							print("= " .. data.title .. " =");
							print()
						end
						if data.instructions then
							print(data.instructions);
							print()
						end
						for i, item in ipairs(command_form_layout) do
							if item.type ~= "hidden" then
								print("== " .. (item.label or item.name) .. " ==")
								print(data[item.name]);
							end
						end

					end
					conn:close();
				else
					conn:warn("unhandled command status: %s", tostring(cmd.status));
				end
			end);
		else
			conn:disco_items(opts.to or conn.host, "http://jabber.org/protocol/commands", function(items)
				-- TODO It would be neat to be able to choose from this list
				if items then
					for i=1,#items do
						print(items[i].name, items[i].node);
					end
				end
				conn:close();
			end);
		end
	end
	clix_connect(opts, on_connect, {"adhoc"});
end

mercurial