comparison plugins/mod_admin_shell.lua @ 14115:44a8c8d4146a

Merge 13.0->trunk
author Matthew Wild <mwild1@gmail.com>
date Tue, 31 Mar 2026 12:25:20 +0100
parents f20bb1c53652 b9688ac25255
children 7008869fcce2
comparison
equal deleted inserted replaced
14112:59064f0f8b4e 14115:44a8c8d4146a
2897 2897
2898 -- host_commands[section..":"..name][host] = handler 2898 -- host_commands[section..":"..name][host] = handler
2899 -- host_commands[section..":"..name][false] = metadata 2899 -- host_commands[section..":"..name][false] = metadata
2900 local host_commands = {}; 2900 local host_commands = {};
2901 2901
2902 -- Remove command handlers for a host (specify name to clear just one command)
2903 function clear_host_commands(host, name)
2904 for command_name, command_info in pairs(host_commands) do
2905 if not name or name == command_name then
2906 command_info[host] = nil;
2907 local has_any_host_handlers_left;
2908 for k in pairs(command_info) do
2909 if k ~= false then
2910 has_any_host_handlers_left = true;
2911 break;
2912 end
2913 end
2914 if not has_any_host_handlers_left then
2915 host_commands[command_name] = nil;
2916 end
2917 if name then
2918 break;
2919 end
2920 end
2921 end
2922 end
2923
2924 -- This returns a wrapper function which routes to the correct handler for a host
2925 local function new_host_command_handler(qualified_name, selector_name, selector_index)
2926 return function (...)
2927 local selected_host = select(2, jid_split((select(selector_index, ...))));
2928 if type(selected_host) ~= "string" then
2929 return nil, "Invalid or missing argument '"..selector_name.."'";
2930 end
2931 if not prosody.hosts[selected_host] then
2932 return nil, "Unknown host: "..selected_host;
2933 end
2934 local host_handler = host_commands[qualified_name][selected_host];
2935 if not host_handler then
2936 return nil, "This command is not available on "..selected_host;
2937 end
2938 return host_handler(...);
2939 end;
2940 end
2941
2902 local function new_item_handlers(command_host) 2942 local function new_item_handlers(command_host)
2903 local function on_command_added(event) 2943 local function on_command_added(event)
2904 local command = event.item; 2944 local command = event.item;
2905 local mod_name = event.source and ("mod_"..event.source.name) or "<unknown module>"; 2945 local mod_name = event.source and ("mod_"..event.source.name) or "<unknown module>";
2906 if not schema.validate(command_metadata_schema, command) or type(command.handler) ~= "function" then 2946 if not schema.validate(command_metadata_schema, command) or type(command.handler) ~= "function" then
2930 return; 2970 return;
2931 end 2971 end
2932 host_command_info = { 2972 host_command_info = {
2933 [false] = { 2973 [false] = {
2934 host_selector = command.host_selector; 2974 host_selector = command.host_selector;
2935 handler = function (...) 2975 handler = new_host_command_handler(qualified_name, command.host_selector, selector_index);
2936 local selected_host = select(2, jid_split((select(selector_index, ...))));
2937 if type(selected_host) ~= "string" then
2938 return nil, "Invalid or missing argument '"..command.host_selector.."'";
2939 end
2940 if not prosody.hosts[selected_host] then
2941 return nil, "Unknown host: "..selected_host;
2942 end
2943 local host_handler = host_commands[qualified_name][selected_host];
2944 if not host_handler then
2945 return nil, "This command is not available on "..selected_host;
2946 end
2947 return host_handler(...);
2948 end;
2949 }; 2976 };
2950 }; 2977 };
2951 host_commands[qualified_name] = host_command_info; 2978 host_commands[qualified_name] = host_command_info;
2952 end 2979 end
2953 if host_command_info[command_host] then 2980 if host_command_info[command_host] then
2954 module:log("warn", "Command %s() is already registered - overwriting with %s", qualified_name, mod_name); 2981 module:log("warn", "Command %s() is already registered on %s - overwriting with %s", qualified_name, command_host, mod_name);
2955 end 2982 end
2956 host_command_info[command_host] = handler; 2983 host_command_info[command_host] = handler;
2957 end 2984 end
2958 2985
2959 local section_t = def_env[command.section]; 2986 local section_t = def_env[command.section];
3010 return; 3037 return;
3011 end 3038 end
3012 3039
3013 local section_t = def_env[command.section]; 3040 local section_t = def_env[command.section];
3014 if not section_t or section_t[command.name] ~= handler then 3041 if not section_t or section_t[command.name] ~= handler then
3015 return; 3042 -- Handler doesn't match, but it could be the host-selector wrapper
3043 -- Let's try removing a per-host command
3044 local qualified_name = command.section..":"..command.name;
3045 local host_command_handlers = host_commands[qualified_name];
3046 if host_command_handlers then
3047 -- Bingo!
3048 module:log("debug", "Removing command %s from %s", qualified_name, command_host);
3049 clear_host_commands(command_host, qualified_name);
3050 elseif section_t then
3051 module:log("warn", "Not removing shell command: handler mismatch: %q ~= %q", section_t and section_t[command.name], handler);
3052 return;
3053 end
3016 end 3054 end
3017 3055
3018 section_t[command.name] = nil; 3056 section_t[command.name] = nil;
3019 if next(section_t) == nil then -- Delete section if empty 3057 if next(section_t) == nil then -- Delete section if empty
3020 def_env[command.section] = nil; 3058 def_env[command.section] = nil;
3021 end 3059 end
3022
3023 if command_host then
3024 local host_command_info = host_commands[command.section..":"..command.name];
3025 if host_command_info then
3026 -- Remove our host handler
3027 host_command_info[command_host] = nil;
3028 -- Clean up entire command entry if there are no per-host handlers left
3029 local any_hosts = false;
3030 for k in pairs(host_command_info) do
3031 if k then -- metadata is false, ignore it
3032 any_hosts = true;
3033 break;
3034 end
3035 end
3036 if not any_hosts then
3037 host_commands[command.section..":"..command.name] = nil;
3038 end
3039 end
3040 end
3041 end 3060 end
3042 return on_command_added, on_command_removed; 3061 return on_command_added, on_command_removed;
3043 end 3062 end
3044 3063
3045 module:handle_items("shell-command", new_item_handlers()); 3064 module:handle_items("shell-command", new_item_handlers());
3046 3065
3047 function module.add_host(host_module) 3066 function module.add_host(host_module)
3048 host_module:handle_items("shell-command", new_item_handlers(host_module.host)); 3067 host_module:handle_items("shell-command", new_item_handlers(host_module.host));
3068 function host_module.unload()
3069 -- Due to non-deterministic unload order, we might not see item
3070 -- removal events for all commands before we get unloaded, so
3071 -- we clear everything from the host explicitly when we're
3072 -- unloaded (also sensible if we are unloaded manually)
3073 clear_host_commands(host_module.host);
3074 end
3049 end 3075 end
3050 3076
3051 function module.unload() 3077 function module.unload()
3052 stanza_watchers.cleanup(); 3078 stanza_watchers.cleanup();
3053 end 3079 end