aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/mod_console.lua140
-rw-r--r--plugins/mod_dialback.lua8
-rw-r--r--plugins/mod_disco.lua9
-rw-r--r--plugins/mod_register.lua29
-rw-r--r--plugins/mod_roster.lua15
-rw-r--r--plugins/mod_saslauth.lua16
-rw-r--r--plugins/mod_selftests.lua53
-rw-r--r--plugins/mod_tls.lua3
-rw-r--r--plugins/mod_vcard.lua3
9 files changed, 262 insertions, 14 deletions
diff --git a/plugins/mod_console.lua b/plugins/mod_console.lua
new file mode 100644
index 00000000..5787ad25
--- /dev/null
+++ b/plugins/mod_console.lua
@@ -0,0 +1,140 @@
+
+local connlisteners_register = require "net.connlisteners".register;
+
+local console_listener = { default_port = 5582; default_mode = "*l"; };
+
+local commands = {};
+local default_env = {};
+local default_env_mt = { __index = default_env };
+
+console = {};
+
+function console:new_session(conn)
+ local w = conn.write;
+ return { conn = conn;
+ send = function (t) w(tostring(t)); end;
+ print = function (t) w("| "..tostring(t).."\n"); end;
+ disconnect = function () conn.close(); end;
+ env = setmetatable({}, default_env_mt);
+ };
+end
+
+local sessions = {};
+
+function console_listener.listener(conn, data)
+ local session = sessions[conn];
+
+ if not session then
+ -- Handle new connection
+ session = console:new_session(conn);
+ sessions[conn] = session;
+ session.print("Welcome to the lxmppd admin console!");
+ end
+ if data then
+ -- Handle data
+
+ if data:match("[!.]$") then
+ local command = data:lower();
+ command = data:match("^%w+") or data:match("%p");
+ if commands[command] then
+ commands[command](session, data);
+ return;
+ end
+ end
+
+ session.env._ = data;
+
+ local chunk, err = loadstring("return "..data);
+ if not chunk then
+ chunk, err = loadstring(data);
+ if not chunk then
+ err = err:gsub("^%[string .-%]:%d+: ", "");
+ err = err:gsub("^:%d+: ", "");
+ err = err:gsub("'<eof>'", "the end of the line");
+ session.print("Sorry, I couldn't understand that... "..err);
+ return;
+ end
+ end
+
+ setfenv(chunk, session.env);
+ local ranok, taskok, message = pcall(chunk);
+
+ if not ranok then
+ session.print("Fatal error while running command, it did not complete");
+ session.print("Error: "..taskok);
+ return;
+ end
+
+ if not message then
+ session.print("Result: "..tostring(taskok));
+ return;
+ elseif (not taskok) and message then
+ session.print("Command completed with a problem");
+ session.print("Message: "..tostring(message));
+ return;
+ end
+
+ session.print("OK: "..tostring(message));
+ end
+end
+
+function console_listener.disconnect(conn, err)
+
+end
+
+connlisteners_register('console', console_listener);
+
+-- Console commands --
+-- These are simple commands, not valid standalone in Lua
+
+function commands.bye(session)
+ session.print("See you! :)");
+ session.disconnect();
+end
+
+commands["!"] = function (session, data)
+ if data:match("^!!") then
+ session.print("!> "..session.env._);
+ return console_listener.listener(session.conn, session.env._);
+ end
+ local old, new = data:match("^!(.-[^\\])!(.-)!$");
+ if old and new then
+ local ok, res = pcall(string.gsub, session.env._, old, new);
+ if not ok then
+ session.print(res)
+ return;
+ end
+ session.print("!> "..res);
+ return console_listener.listener(session.conn, res);
+ end
+ session.print("Sorry, not sure what you want");
+end
+
+-- Session environment --
+-- Anything in default_env will be accessible within the session as a global variable
+
+default_env.server = {};
+function default_env.server.reload()
+ dofile "main.lua"
+ return true, "Server reloaded";
+end
+
+default_env.module = {};
+function default_env.module.load(name)
+ local mm = require "modulemanager";
+ local ok, err = mm.load(name);
+ if not ok then
+ return false, err or "Unknown error loading module";
+ end
+ return true, "Module loaded";
+end
+
+default_env.config = {};
+function default_env.config.load(filename, format)
+ local cfgm_load = require "core.configmanager".load;
+ local ok, err = cfgm_load(filename, format);
+ if not ok then
+ return false, err or "Unknown error loading config";
+ end
+ return true, "Config loaded";
+end
diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua
index c17cbcaf..87ac303b 100644
--- a/plugins/mod_dialback.lua
+++ b/plugins/mod_dialback.lua
@@ -55,8 +55,12 @@ add_handler({ "s2sout_unauthed", "s2sout" }, "verify", xmlns_dialback,
log("warn", "dialback for "..(origin.dialback_verifying.from_host or "(unknown)").." failed");
valid = "invalid";
end
- origin.dialback_verifying.sends2s(format("<db:result from='%s' to='%s' id='%s' type='%s'>%s</db:result>",
- attr.from, attr.to, attr.id, valid, origin.dialback_verifying.dialback_key));
+ if not origin.dialback_verifying.sends2s then
+ log("warn", "Incoming s2s session %s was closed in the meantime, so we can't notify it of the db result", tostring(origin.dialback_verifying):match("%w+$"));
+ else
+ origin.dialback_verifying.sends2s(format("<db:result from='%s' to='%s' id='%s' type='%s'>%s</db:result>",
+ attr.to, attr.from, attr.id, valid, origin.dialback_verifying.dialback_key));
+ end
end
end);
diff --git a/plugins/mod_disco.lua b/plugins/mod_disco.lua
new file mode 100644
index 00000000..261650ce
--- /dev/null
+++ b/plugins/mod_disco.lua
@@ -0,0 +1,9 @@
+
+local discomanager_handle = require "core.discomanager".handle;
+
+add_iq_handler({"c2s", "s2sin"}, "http://jabber.org/protocol/disco#info", function (session, stanza)
+ session.send(discomanager_handle(stanza));
+end);
+add_iq_handler({"c2s", "s2sin"}, "http://jabber.org/protocol/disco#items", function (session, stanza)
+ session.send(discomanager_handle(stanza));
+end);
diff --git a/plugins/mod_register.lua b/plugins/mod_register.lua
index fb001392..c2b85bae 100644
--- a/plugins/mod_register.lua
+++ b/plugins/mod_register.lua
@@ -2,6 +2,7 @@
local st = require "util.stanza";
local usermanager_user_exists = require "core.usermanager".user_exists;
local usermanager_create_user = require "core.usermanager".create_user;
+local datamanager_store = require "util.datamanager".store;
add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
if stanza.tags[1].name == "query" then
@@ -16,7 +17,33 @@ add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
elseif stanza.attr.type == "set" then
if query.tags[1] and query.tags[1].name == "remove" then
-- TODO delete user auth data, send iq response, kick all user resources with a <not-authorized/>, delete all user data
- session.send(st.error_reply(stanza, "cancel", "not-allowed"));
+ --session.send(st.error_reply(stanza, "cancel", "not-allowed"));
+ --return;
+ usermanager_create_user(session.username, nil, session.host); -- Disable account
+ -- FIXME the disabling currently allows a different user to recreate the account
+ -- we should add an in-memory account block mode when we have threading
+ session.send(st.reply(stanza));
+ local roster = session.roster;
+ for _, session in pairs(hosts[session.host].sessions[session.username].sessions) do -- disconnect all resources
+ session:disconnect({condition = "not-authorized", text = "Account deleted"});
+ end
+ -- TODO datamanager should be able to delete all user data itself
+ datamanager.store(session.username, session.host, "roster", nil);
+ datamanager.store(session.username, session.host, "vCard", nil);
+ datamanager.store(session.username, session.host, "private", nil);
+ datamanager.store(session.username, session.host, "offline", nil);
+ local bare = session.username.."@"..session.host;
+ for jid, item in pairs(roster) do
+ if jid ~= "pending" then
+ if item.subscription == "both" or item.subscription == "to" then
+ -- TODO unsubscribe
+ end
+ if item.subscription == "both" or item.subscription == "from" then
+ -- TODO unsubscribe
+ end
+ end
+ end
+ datamanager.store(session.username, session.host, "accounts", nil); -- delete accounts datastore at the end
else
local username = query:child_with_name("username");
local password = query:child_with_name("password");
diff --git a/plugins/mod_roster.lua b/plugins/mod_roster.lua
index 23a19828..24d858e7 100644
--- a/plugins/mod_roster.lua
+++ b/plugins/mod_roster.lua
@@ -4,6 +4,7 @@ local st = require "util.stanza"
local jid_split = require "util.jid".split;
local t_concat = table.concat;
+local handle_outbound_presence_subscriptions_and_probes = require "core.presencemanager".handle_outbound_presence_subscriptions_and_probes;
local rm_remove_from_roster = require "core.rostermanager".remove_from_roster;
local rm_add_to_roster = require "core.rostermanager".add_to_roster;
local rm_roster_push = require "core.rostermanager".roster_push;
@@ -38,15 +39,25 @@ add_iq_handler("c2s", "jabber:iq:roster",
and query.tags[1].attr.jid ~= "pending" then
local item = query.tags[1];
local from_node, from_host = jid_split(stanza.attr.from);
+ local from_bare = from_node and (from_node.."@"..from_host) or from_host; -- bare JID
local node, host, resource = jid_split(item.attr.jid);
- if not resource then
+ local to_bare = node and (node.."@"..host) or host; -- bare JID
+ if not resource and host then
if item.attr.jid ~= from_node.."@"..from_host then
if item.attr.subscription == "remove" then
- if session.roster[item.attr.jid] then
+ local r_item = session.roster[item.attr.jid];
+ if r_item then
local success, err_type, err_cond, err_msg = rm_remove_from_roster(session, item.attr.jid);
if success then
session.send(st.reply(stanza));
rm_roster_push(from_node, from_host, item.attr.jid);
+ if r_item.subscription == "both" or r_item.subscription == "from" then
+ handle_outbound_presence_subscriptions_and_probes(session,
+ st.presence({type="unsubscribed"}), from_bare, to_bare);
+ elseif r_item.subscription == "both" or r_item.subscription == "to" then
+ handle_outbound_presence_subscriptions_and_probes(session,
+ st.presence({type="unsubscribe"}), from_bare, to_bare);
+ end
else
session.send(st.error_reply(stanza, err_type, err_cond, err_msg));
end
diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua
index 6ceb0be3..7ca4308b 100644
--- a/plugins/mod_saslauth.lua
+++ b/plugins/mod_saslauth.lua
@@ -83,19 +83,21 @@ add_handler("c2s_unauthed", "auth", xmlns_sasl, sasl_handler);
add_handler("c2s_unauthed", "abort", xmlns_sasl, sasl_handler);
add_handler("c2s_unauthed", "response", xmlns_sasl, sasl_handler);
+local mechanisms_attr = { xmlns='urn:ietf:params:xml:ns:xmpp-sasl' };
+local bind_attr = { xmlns='urn:ietf:params:xml:ns:xmpp-bind' };
+local xmpp_session_attr = { xmlns='urn:ietf:params:xml:ns:xmpp-session' };
add_event_hook("stream-features",
function (session, features)
if not session.username then
- t_insert(features, "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>");
+ features:tag("mechanisms", mechanisms_attr);
-- TODO: Provide PLAIN only if TLS is active, this is a SHOULD from the introduction of RFC 4616. This behavior could be overridden via configuration but will issuing a warning or so.
- t_insert(features, "<mechanism>PLAIN</mechanism>");
- t_insert(features, "<mechanism>DIGEST-MD5</mechanism>");
- t_insert(features, "</mechanisms>");
+ features:tag("mechanism"):text("PLAIN"):up();
+ features:tag("mechanism"):text("DIGEST-MD5"):up();
+ features:up();
else
- t_insert(features, "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><required/></bind>");
- t_insert(features, "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>");
+ features:tag("bind", bind_attr):tag("required"):up():up();
+ features:tag("session", xmpp_session_attr):up();
end
- --send [[<register xmlns="http://jabber.org/features/iq-register"/> ]]
end);
add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-bind",
diff --git a/plugins/mod_selftests.lua b/plugins/mod_selftests.lua
new file mode 100644
index 00000000..4f128504
--- /dev/null
+++ b/plugins/mod_selftests.lua
@@ -0,0 +1,53 @@
+
+local st = require "util.stanza";
+local register_component = require "core.componentmanager".register_component;
+local core_route_stanza = core_route_stanza;
+local socket = require "socket";
+local config = require "core.configmanager";
+local ping_hosts = config.get("*", "mod_selftests", "ping_hosts") or { "jabber.org" };
+
+local open_pings = {};
+
+local t_insert = table.insert;
+
+local log = require "util.logger".init("mod_selftests");
+
+local tests_jid = "self_tests@getjabber.ath.cx";
+local host = "getjabber.ath.cx";
+
+if not (tests_jid and host) then
+ for currhost in pairs(host) do
+ if currhost ~= "localhost" then
+ tests_jid, host = "self_tests@"..currhost, currhost;
+ end
+ end
+end
+
+if tests_jid and host then
+ local bot = register_component(tests_jid, function(origin, stanza, ourhost)
+ local time = open_pings[stanza.attr.id];
+
+ if time then
+ log("info", "Ping reply from %s in %fs", tostring(stanza.attr.from), socket.gettime() - time);
+ else
+ log("info", "Unexpected reply: %s", stanza:pretty_print());
+ end
+ end);
+
+
+ local our_origin = hosts[host];
+ add_event_hook("server-started",
+ function ()
+ local id = st.new_id();
+ local ping_attr = { xmlns = 'urn:xmpp:ping' };
+ local function send_ping(to)
+ log("info", "Sending ping to %s", to);
+ core_route_stanza(our_origin, st.iq{ to = to, from = tests_jid, id = id, type = "get" }:tag("ping", ping_attr));
+ open_pings[id] = socket.gettime();
+ end
+
+ for _, host in ipairs(ping_hosts) do
+ send_ping(host);
+ end
+ end);
+end
diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua
index b5ca5015..cc46d556 100644
--- a/plugins/mod_tls.lua
+++ b/plugins/mod_tls.lua
@@ -24,9 +24,10 @@ add_handler("c2s_unauthed", "starttls", xmlns_starttls,
end
end);
+local starttls_attr = { xmlns = xmlns_starttls };
add_event_hook("stream-features",
function (session, features)
if session.conn.starttls then
- t_insert(features, "<starttls xmlns='"..xmlns_starttls.."'/>");
+ features:tag("starttls", starttls_attr):up();
end
end);
diff --git a/plugins/mod_vcard.lua b/plugins/mod_vcard.lua
index fb7382c2..d2f2c7ba 100644
--- a/plugins/mod_vcard.lua
+++ b/plugins/mod_vcard.lua
@@ -43,9 +43,10 @@ add_iq_handler({"c2s", "s2sin"}, "vcard-temp",
end
end);
+local feature_vcard_attr = { var='vcard-temp' };
add_event_hook("stream-features",
function (session, features)
if session.type == "c2s" then
- t_insert(features, "<feature var='vcard-temp'/>");
+ features:tag("feature", feature_vcard_attr):up();
end
end);