aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/configmanager.lua13
-rw-r--r--core/hostmanager.lua7
-rw-r--r--core/modulemanager.lua9
-rw-r--r--core/s2smanager.lua3
-rw-r--r--core/sessionmanager.lua8
-rw-r--r--core/stanza_router.lua14
-rw-r--r--man/prosodyctl.man2
-rw-r--r--net/dns.lua9
-rw-r--r--net/httpserver.lua28
-rw-r--r--net/xmppclient_listener.lua1
-rw-r--r--net/xmppcomponent_listener.lua1
-rw-r--r--net/xmppserver_listener.lua1
-rw-r--r--plugins/mod_bosh.lua4
-rw-r--r--plugins/mod_component.lua2
-rw-r--r--plugins/mod_compression.lua8
-rw-r--r--plugins/mod_console.lua2
-rw-r--r--plugins/mod_debug.lua191
-rw-r--r--plugins/mod_offline.lua56
-rw-r--r--plugins/mod_pep.lua93
-rw-r--r--plugins/mod_presence.lua41
-rw-r--r--plugins/mod_register.lua2
-rw-r--r--plugins/mod_saslauth.lua6
-rw-r--r--plugins/mod_selftests.lua61
-rw-r--r--plugins/mod_tls.lua14
-rw-r--r--plugins/muc/muc.lib.lua43
-rwxr-xr-xprosody38
-rw-r--r--prosody.cfg.lua.dist2
-rw-r--r--tests/test_util_jid.lua11
-rwxr-xr-xtools/ejabberd2prosody.lua2
-rw-r--r--tools/ejabberdsql2prosody.lua2
-rw-r--r--util-src/encodings.c2
-rw-r--r--util-src/pposix.c3
-rw-r--r--util/dataforms.lua4
-rw-r--r--util/dependencies.lua8
-rw-r--r--util/jid.lua13
-rw-r--r--util/sasl.lua2
-rw-r--r--util/stanza.lua28
37 files changed, 319 insertions, 415 deletions
diff --git a/core/configmanager.lua b/core/configmanager.lua
index 1fbe83b8..a7c7c4be 100644
--- a/core/configmanager.lua
+++ b/core/configmanager.lua
@@ -9,8 +9,9 @@
local _G = _G;
-local setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type =
- setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type;
+local setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type, pairs, table, format =
+ setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type, pairs, table, string.format;
+
local eventmanager = require "core.eventmanager";
@@ -115,6 +116,10 @@ do
rawset(env, "__currenthost", "*") -- Default is global
function env.Host(name)
+ if rawget(config, name) and rawget(config[name].core, "component_module") then
+ error(format("Host %q clashes with previously defined %s Component %q, for services use a sub-domain like conference.%s",
+ name, config[name].core.component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0);
+ end
rawset(env, "__currenthost", name);
-- Needs at least one setting to logically exist :)
set(name or "*", "core", "defined", true);
@@ -122,6 +127,10 @@ do
env.host = env.Host;
function env.Component(name)
+ if rawget(config, name) and rawget(config[name].core, "defined") and not rawget(config[name].core, "component_module") then
+ error(format("Component %q clashes with previously defined Host %q, for services use a sub-domain like conference.%s",
+ name, name, name), 0);
+ end
set(name, "core", "component_module", "component");
-- Don't load the global modules by default
set(name, "core", "load_global_modules", false);
diff --git a/core/hostmanager.lua b/core/hostmanager.lua
index 713788dd..125e71ba 100644
--- a/core/hostmanager.lua
+++ b/core/hostmanager.lua
@@ -33,12 +33,19 @@ local hosts_loaded_once;
local function load_enabled_hosts(config)
local defined_hosts = config or configmanager.getconfig();
+ local activated_any_host;
for host, host_config in pairs(defined_hosts) do
if host ~= "*" and (host_config.core.enabled == nil or host_config.core.enabled) and not host_config.core.component_module then
+ activated_any_host = true;
activate(host, host_config);
end
end
+
+ if not activated_any_host then
+ log("error", "No hosts defined in the config file. This may cause unexpected behaviour as no modules will be loaded.");
+ end
+
eventmanager.fire_event("hosts-activated", defined_hosts);
hosts_loaded_once = true;
end
diff --git a/core/modulemanager.lua b/core/modulemanager.lua
index 9cd56187..ecb1bc6c 100644
--- a/core/modulemanager.lua
+++ b/core/modulemanager.lua
@@ -197,6 +197,15 @@ function unload(host, name, ...)
end
end
hooks:remove(host, name);
+ if mod.module.items then -- remove items
+ for key,t in pairs(mod.module.items) do
+ for i = #t,1,-1 do
+ local value = t[i];
+ t[i] = nil;
+ hosts[host].events.fire_event("item-removed/"..key, {source = self, item = value});
+ end
+ end
+ end
modulemap[host][name] = nil;
return true;
end
diff --git a/core/s2smanager.lua b/core/s2smanager.lua
index aabfddd6..92f07354 100644
--- a/core/s2smanager.lua
+++ b/core/s2smanager.lua
@@ -504,6 +504,8 @@ function mark_connected(session)
end
end
+local function null_data_handler(conn, data) log("debug", "Discarding data from destroyed s2s session: %s", data); end
+
function destroy_session(session, reason)
(session.log or log)("info", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host));
@@ -519,6 +521,7 @@ function destroy_session(session, reason)
session[k] = nil;
end
end
+ session.data = null_data_handler;
end
return _M;
diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua
index 5e7fe06d..2dd0070b 100644
--- a/core/sessionmanager.lua
+++ b/core/sessionmanager.lua
@@ -10,7 +10,6 @@
local tonumber, tostring = tonumber, tostring;
local ipairs, pairs, print, next= ipairs, pairs, print, next;
-local collectgarbage = collectgarbage;
local format = import("string", "format");
local hosts = hosts;
@@ -25,6 +24,7 @@ local uuid_generate = require "util.uuid".generate;
local rm_load_roster = require "core.rostermanager".load_roster;
local config_get = require "core.configmanager".get;
local nameprep = require "util.encodings".stringprep.nameprep;
+local resourceprep = require "util.encodings".stringprep.resourceprep;
local fire_event = require "core.eventmanager".fire_event;
local add_task = require "util.timer".add_task;
@@ -66,6 +66,8 @@ function new_session(conn)
return session;
end
+local function null_data_handler(conn, data) log("debug", "Discarding data from destroyed c2s session: %s", data); end
+
function destroy_session(session, err)
(session.log or log)("info", "Destroying session for %s (%s@%s)", session.full_jid or "(unknown)", session.username or "(unknown)", session.host or "(unknown)");
@@ -88,6 +90,7 @@ function destroy_session(session, err)
session[k] = nil;
end
end
+ session.data = null_data_handler;
end
function make_authenticated(session, username)
@@ -106,7 +109,8 @@ function bind_resource(session, resource)
if session.resource then return nil, "cancel", "already-bound", "Cannot bind multiple resources on a single connection"; end
-- We don't support binding multiple resources
- resource = resource or uuid_generate();
+ resource = resourceprep(resource);
+ resource = resource ~= "" and resource or uuid_generate();
--FIXME: Randomly-generated resources must be unique per-user, and never conflict with existing
if not hosts[session.host].sessions[session.username] then
diff --git a/core/stanza_router.lua b/core/stanza_router.lua
index 00c37ed7..b5b1b45f 100644
--- a/core/stanza_router.lua
+++ b/core/stanza_router.lua
@@ -36,12 +36,14 @@ function core_process_stanza(origin, stanza)
end
end
- if origin.type == "c2s" then
+ if origin.type == "c2s" and stanza.attr.xmlns == "jabber:client" then
if not origin.full_jid
and not(stanza.name == "iq" and stanza.attr.type == "set" and stanza.tags[1] and stanza.tags[1].name == "bind"
and stanza.tags[1].attr.xmlns == "urn:ietf:params:xml:ns:xmpp-bind") then
-- authenticated client isn't bound and current stanza is not a bind request
- origin.send(st.error_reply(stanza, "auth", "not-authorized")); -- FIXME maybe allow stanzas to account or server
+ if stanza.attr.type ~= "result" and stanza.attr.type ~= "error" then
+ origin.send(st.error_reply(stanza, "auth", "not-authorized")); -- FIXME maybe allow stanzas to account or server
+ end
return;
end
@@ -98,7 +100,7 @@ function core_process_stanza(origin, stanza)
return; -- FIXME what should we do here? does this work with subdomains?
end
end
- core_post_stanza(origin, stanza);
+ core_post_stanza(origin, stanza, origin.full_jid);
else
local h = hosts[stanza.attr.to or origin.host or origin.to_host];
if h then
@@ -119,7 +121,7 @@ function core_process_stanza(origin, stanza)
end
end
-function core_post_stanza(origin, stanza)
+function core_post_stanza(origin, stanza, preevents)
local to = stanza.attr.to;
local node, host, resource = jid_split(to);
local to_bare = node and (node.."@"..host) or host; -- bare JID
@@ -143,7 +145,7 @@ function core_post_stanza(origin, stanza)
end
local event_data = {origin=origin, stanza=stanza};
- if origin.full_jid == stanza.attr.from then -- c2s connection
+ if preevents then -- c2s connection
if hosts[origin.host].events.fire_event('pre-'..stanza.name..to_type, event_data) then return; end -- do preprocessing
end
local h = hosts[to_bare] or hosts[host or origin.host];
@@ -191,6 +193,6 @@ function core_route_stanza(origin, stanza)
log("debug", "Routing outgoing stanza for %s to %s", from_host, host);
send_s2s(from_host, host, stanza);
else
- log("warn", "received stanza from unhandled connection type: %s", origin.type);
+ log("warn", "received %s stanza from unhandled connection type: %s", tostring(stanza.name), tostring(origin.type));
end
end
diff --git a/man/prosodyctl.man b/man/prosodyctl.man
index e677443f..44a84145 100644
--- a/man/prosodyctl.man
+++ b/man/prosodyctl.man
@@ -76,4 +76,4 @@ determine if a host has been configured.
More information may be found online at: \fIhttp://prosody.im/\fP
.SH AUTHORS
-Dwayne Bent <dbb.0@liqd.org>
+Dwayne Bent <dbb.1@liqd.org>
diff --git a/net/dns.lua b/net/dns.lua
index ca0ec622..1b5321af 100644
--- a/net/dns.lua
+++ b/net/dns.lua
@@ -594,17 +594,18 @@ end
function resolver:remember(rr, type) -- - - - - - - - - - - - - - remember
--print ('remember', type, rr.class, rr.type, rr.name)
+ local qname, qtype, qclass = standardize(rr.name, rr.type, rr.class);
if type ~= '*' then
- type = rr.type;
- local all = get(self.cache, rr.class, '*', rr.name);
+ type = qtype;
+ local all = get(self.cache, qclass, '*', qname);
--print('remember all', all);
if all then append(all, rr); end
end
self.cache = self.cache or setmetatable({}, cache_metatable);
- local rrs = get(self.cache, rr.class, type, rr.name) or
- set(self.cache, rr.class, type, rr.name, setmetatable({}, rrs_metatable));
+ local rrs = get(self.cache, qclass, type, qname) or
+ set(self.cache, qclass, type, qname, setmetatable({}, rrs_metatable));
append(rrs, rr);
if type == 'MX' then self.unsorted[rrs] = true; end
diff --git a/net/httpserver.lua b/net/httpserver.lua
index dae49d2a..6bd641f9 100644
--- a/net/httpserver.lua
+++ b/net/httpserver.lua
@@ -36,8 +36,8 @@ end
local function send_response(request, response)
-- Write status line
local resp;
- if response.body then
- local body = tostring(response.body);
+ if response.body or response.headers then
+ local body = response.body and tostring(response.body);
log("debug", "Sending response to %s", request.id);
resp = { "HTTP/1.0 ", response.status or "200 OK", "\r\n"};
local h = response.headers;
@@ -49,14 +49,14 @@ local function send_response(request, response)
t_insert(resp, "\r\n");
end
end
- if not (h and h["Content-Length"]) then
+ if body and not (h and h["Content-Length"]) then
t_insert(resp, "Content-Length: ");
t_insert(resp, #body);
t_insert(resp, "\r\n");
end
t_insert(resp, "\r\n");
- if request.method ~= "HEAD" then
+ if body and request.method ~= "HEAD" then
t_insert(resp, body);
end
else
@@ -147,22 +147,29 @@ local function request_reader(request, data, startpos)
elseif request.state == "headers" then
log("debug", "Reading headers...")
local pos = startpos;
- local headers = request.headers or {};
+ local headers, headers_complete = request.headers;
+ if not headers then
+ headers = {};
+ request.headers = headers;
+ end
+
for line in data:gmatch("(.-)\r\n") do
startpos = (startpos or 1) + #line + 2;
local k, v = line:match("(%S+): (.+)");
if k and v then
headers[k:lower()] = v;
--- log("debug", "Header: "..k:lower().." = "..v);
+ --log("debug", "Header: '"..k:lower().."' = '"..v.."'");
elseif #line == 0 then
- request.headers = headers;
+ headers_complete = true;
break;
else
log("debug", "Unhandled header line: "..line);
end
end
- if not expectbody(request) then
+ if not headers_complete then return; end
+
+ if not expectbody(request) then
call_callback(request);
return;
end
@@ -176,7 +183,10 @@ local function request_reader(request, data, startpos)
log("debug", "Reading request line...")
local method, path, http, linelen = data:match("^(%S+) (%S+) HTTP/(%S+)\r\n()", startpos);
if not method then
- return call_callback(request, "invalid-status-line");
+ log("warn", "Invalid HTTP status line, telling callback then closing");
+ local ret = call_callback(request, "invalid-status-line");
+ request:destroy();
+ return ret;
end
request.method, request.path, request.httpversion = method, path, http;
diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua
index 01d73a36..f455e7be 100644
--- a/net/xmppclient_listener.lua
+++ b/net/xmppclient_listener.lua
@@ -140,7 +140,6 @@ function xmppclient.disconnect(conn, err)
sm_destroy_session(session, err);
sessions[conn] = nil;
session = nil;
- collectgarbage("collect");
end
end
diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua
index c16f41a0..2045d28f 100644
--- a/net/xmppcomponent_listener.lua
+++ b/net/xmppcomponent_listener.lua
@@ -169,7 +169,6 @@ function component_listener.disconnect(conn, err)
sessions[conn] = nil;
for k in pairs(session) do session[k] = nil; end
session = nil;
- collectgarbage("collect");
end
end
diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua
index 4394eac5..86a3d735 100644
--- a/net/xmppserver_listener.lua
+++ b/net/xmppserver_listener.lua
@@ -162,7 +162,6 @@ function xmppserver.disconnect(conn, err)
s2s_destroy_session(session, err);
sessions[conn] = nil;
session = nil;
- collectgarbage("collect");
end
end
diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua
index 3e41ef7b..5de79eff 100644
--- a/plugins/mod_bosh.lua
+++ b/plugins/mod_bosh.lua
@@ -152,7 +152,7 @@ function stream_callbacks.streamopened(request, attr)
local r, send_buffer = session.requests, session.send_buffer;
local response = { headers = default_headers }
function session.send(s)
- log("debug", "Sending BOSH data: %s", tostring(s));
+ --log("debug", "Sending BOSH data: %s", tostring(s));
local oldest_request = r[1];
while oldest_request and oldest_request.destroyed do
t_remove(r, 1);
@@ -160,7 +160,7 @@ function stream_callbacks.streamopened(request, attr)
oldest_request = r[1];
end
if oldest_request then
- log("debug", "We have an open request, so using that to send with");
+ log("debug", "We have an open request, so sending on that");
response.body = t_concat{"<body xmlns='http://jabber.org/protocol/httpbind' sid='", sid, "' xmlns:stream = 'http://etherx.jabber.org/streams'>", tostring(s), "</body>" };
oldest_request:send(response);
--log("debug", "Sent");
diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua
index 69a42eaf..4201bb19 100644
--- a/plugins/mod_component.lua
+++ b/plugins/mod_component.lua
@@ -44,7 +44,7 @@ function handle_component_auth(session, stanza)
local secret = config.get(session.user, "core", "component_secret");
if not secret then
- (session.log or log)("warn", "Component attempted to identify as %s, but component_password is not set", session.user);
+ (session.log or log)("warn", "Component attempted to identify as %s, but component_secret is not set", session.user);
session:close("not-authorized");
return;
end
diff --git a/plugins/mod_compression.lua b/plugins/mod_compression.lua
index f1cae737..ec9e24ec 100644
--- a/plugins/mod_compression.lua
+++ b/plugins/mod_compression.lua
@@ -14,10 +14,10 @@ local xmlns_compression_protocol = "http://jabber.org/protocol/compress"
local compression_stream_feature = st.stanza("compression", {xmlns=xmlns_compression_feature}):tag("method"):text("zlib"):up();
local compression_level = module:get_option("compression_level");
-
-- if not defined assume admin wants best compression
if compression_level == nil then compression_level = 9 end;
+
compression_level = tonumber(compression_level);
if not compression_level or compression_level < 1 or compression_level > 9 then
module:log("warn", "Invalid compression level in config: %s", tostring(compression_level));
@@ -41,7 +41,7 @@ module:add_handler({"c2s_unauthed", "c2s"}, "compress", xmlns_compression_protoc
if session.compressed then
local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method");
session.send(error_st);
- session:log("warn", "Tried to establish another compression layer.");
+ session.log("warn", "Tried to establish another compression layer.");
end
-- checking if the compression method is supported
@@ -56,7 +56,7 @@ module:add_handler({"c2s_unauthed", "c2s"}, "compress", xmlns_compression_protoc
if status == false then
local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
session.send(error_st);
- session:log("error", "Failed to create zlib.deflate filter.");
+ session.log("error", "Failed to create zlib.deflate filter.");
module:log("error", deflate_stream);
return
end
@@ -65,7 +65,7 @@ module:add_handler({"c2s_unauthed", "c2s"}, "compress", xmlns_compression_protoc
if status == false then
local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
session.send(error_st);
- session:log("error", "Failed to create zlib.deflate filter.");
+ session.log("error", "Failed to create zlib.deflate filter.");
module:log("error", inflate_stream);
return
end
diff --git a/plugins/mod_console.lua b/plugins/mod_console.lua
index 82045232..d8362a07 100644
--- a/plugins/mod_console.lua
+++ b/plugins/mod_console.lua
@@ -192,7 +192,7 @@ function commands.help(session, data)
elseif section == "server" then
print [[server:version() - Show the server's version number]]
print [[server:uptime() - Show how long the server has been running]]
- --print [[server:shutdown(reason) - Shut down the server, with an optional reason to be broadcast to all connections]]
+ print [[server:shutdown(reason) - Shut down the server, with an optional reason to be broadcast to all connections]]
elseif section == "config" then
print [[config:reload() - Reload the server configuration. Modules may need to be reloaded for changes to take effect.]]
elseif section == "console" then
diff --git a/plugins/mod_debug.lua b/plugins/mod_debug.lua
deleted file mode 100644
index 9f80202f..00000000
--- a/plugins/mod_debug.lua
+++ /dev/null
@@ -1,191 +0,0 @@
--- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-module.host = "*";
-
-local connlisteners_register = require "net.connlisteners".register;
-
-local console_listener = { default_port = 5583; default_mode = "*l"; default_interface = "127.0.0.1" };
-
-local sha256, missingglobal = require "util.hashes".sha256;
-
-local commands = {};
-local debug_env = {};
-local debug_env_mt = { __index = function (t, k) return rawget(_G, k) or missingglobal(k); end, __newindex = function (t, k, v) rawset(_G, k, v); end };
-
-local t_insert, t_concat = table.insert, table.concat;
-local t_concatall = function (t, sep) local tt = {}; for k, s in pairs(t) do tt[k] = tostring(s); end return t_concat(tt, sep); end
-
-
-setmetatable(debug_env, debug_env_mt);
-
-console = {};
-
-function console:new_session(conn)
- local w = function(s) conn.write(s:gsub("\n", "\r\n")); end;
- local session = { conn = conn;
- send = function (t) w(tostring(t)); end;
- print = function (t) w("| "..tostring(t).."\n"); end;
- disconnect = function () conn.close(); end;
- };
-
- return session;
-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;
- printbanner(session);
- end
- if data then
- -- Handle data
- (function(session, 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
-
- 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
-
- debug_env.print = session.print;
-
- setfenv(chunk, debug_env);
-
- local ret = { pcall(chunk) };
-
- if not ret[1] then
- session.print("Fatal error while running command, it did not complete");
- session.print("Error: "..ret[2]);
- return;
- end
-
- table.remove(ret, 1);
-
- local retstr = t_concatall(ret, ", ");
- if retstr ~= "" then
- session.print("Result: "..retstr);
- else
- session.print("No result, or nil");
- return;
- end
- end)(session, data);
- end
- session.send(string.char(0));
-end
-
-function console_listener.disconnect(conn, err)
-
-end
-
-connlisteners_register('debug', console_listener);
-require "net.connlisteners".start("debug");
-
--- 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
-
-function printbanner(session)
-session.print [[
- ____ \ / _
- | _ \ _ __ ___ ___ _-_ __| |_ _
- | |_) | '__/ _ \/ __|/ _ \ / _` | | | |
- | __/| | | (_) \__ \ |_| | (_| | |_| |
- |_| |_| \___/|___/\___/ \__,_|\__, |
- A study in simplicity |___/
-
-]]
-session.print("Welcome to the Prosody debug console. For a list of commands, type: help");
-session.print("You may find more help on using this console in our online documentation at ");
-session.print("http://prosody.im/doc/debugconsole\n");
-end
-
-local byte, char = string.byte, string.char;
-local gmatch, gsub = string.gmatch, string.gsub;
-
-local function vdecode(text, key)
- local keyarr = {};
- for l in gmatch(key, ".") do t_insert(keyarr, byte(l) - 32) end
- local pos, keylen = 0, #keyarr;
- return (gsub(text, ".", function (letter)
- if byte(letter) < 32 then return ""; end
- pos = (pos%keylen)+1;
- return char(((byte(letter) - 32 - keyarr[pos]) % 94) + 32);
- end));
-end
-
-local subst = {
- ["f880c08056ba7dbecb1ccfe5d7728bd6dcd654e94f7a9b21788c43397bae0bc5"] =
- [=[nRYeKR$l'5Ix%u*1Mc-K}*bwv*\ $1KLMBd$KH R38`$[6}VQ@,6Qn]=];
- ["92f718858322157202ec740698c1390e47bc819e52b6a099c54c378a9f7529d6"] =
- [=[V\Z5`WZ5,T$<)7LM'w3Z}M(7V'{pa) &'>0+{v)O(0M*V5K$$LL$|2wT}6
- 1as*")e!>]=];
- ["467b65edcc7c7cd70abf2136cc56abd037216a6cd9e17291a2219645be2e2216"] =
- [=[i#'Z,E1-"YaHW(j/0xs]I4x&%(Jx1h&18'(exNWT D3b+K{*8}w(%D {]=];
- ["f73729d7f2fbe686243a25ac088c7e6aead3d535e081329f2817438a5c78bee5"] =
- [=[,3+(Q{3+W\ftQ%wvv/C0z-l%f>ABc(vkp<bb8]=];
- ["6afa189489b096742890d0c5bd17d5bb8af8ac460c7026984b64e8f14a40404e"] =
- [=[9N{)5j34gd*}&]H&dy"I&7(",a F1v6jY+IY7&S+86)1z(Vo]=];
- ["cc5e5293ef8a1acbd9dd2bcda092c5c77ef46d3ec5aea65024fca7ed4b3c94a9"] =
- [=[_]Rc}IF'Kfa&))Ry+6|x!K2|T*Vze)%4Hwz'L3uI|OwIa)|q#uq2+Qu u7
- [V3(z(*TYY|T\1_W'2] Dwr{-{@df#W.H5^x(ydtr{c){UuV@]=];
- ["b3df231fd7ddf73f72f39cb2510b1fe39318f4724728ed58948a180663184d3e"] =
- [=[iH!"9NLS'%geYw3^R*fvWM1)MwxLS!d[zP(p0sQ|8tX{dWO{9w!+W)b"MU
- W)V8&(2Wx"'dTL9*PP%1"JV(I|Jr1^f'-Hc3U\2H3Z='K#,)dPm]=];
- }
-
-function missingglobal(name)
- if sha256 then
- local hash = sha256(name.."|"..name:reverse(), true);
-
- if subst[hash] then
- return vdecode(subst[hash], sha256(name:reverse(), true));
- end
- end
-end
diff --git a/plugins/mod_offline.lua b/plugins/mod_offline.lua
deleted file mode 100644
index c74d011e..00000000
--- a/plugins/mod_offline.lua
+++ /dev/null
@@ -1,56 +0,0 @@
--- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-
-local datamanager = require "util.datamanager";
-local st = require "util.stanza";
-local datetime = require "util.datetime";
-local ipairs = ipairs;
-local jid_split = require "util.jid".split;
-
-module:add_feature("msgoffline");
-
-module:hook("message/offline/store", function(event)
- local origin, stanza = event.origin, event.stanza;
- local to = stanza.attr.to;
- local node, host;
- if to then
- node, host = jid_split(to)
- else
- node, host = origin.username, origin.host;
- end
-
- stanza.attr.stamp, stanza.attr.stamp_legacy = datetime.datetime(), datetime.legacy();
- local result = datamanager.list_append(node, host, "offline", st.preserialize(stanza));
- stanza.attr.stamp, stanza.attr.stamp_legacy = nil, nil;
-
- return true;
-end);
-
-module:hook("message/offline/broadcast", function(event)
- local origin = event.origin;
- local node, host = origin.username, origin.host;
-
- local data = datamanager.list_load(node, host, "offline");
- if not data then return true; end
- for _, stanza in ipairs(data) do
- stanza = st.deserialize(stanza);
- stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = host, stamp = stanza.attr.stamp}):up(); -- XEP-0203
- stanza:tag("x", {xmlns = "jabber:x:delay", from = host, stamp = stanza.attr.stamp_legacy}):up(); -- XEP-0091 (deprecated)
- stanza.attr.stamp, stanza.attr.stamp_legacy = nil, nil;
- origin.send(stanza);
- end
- return true;
-end);
-
-module:hook("message/offline/delete", function(event)
- local origin = event.origin;
- local node, host = origin.username, origin.host;
-
- return datamanager.list_store(node, host, "offline", nil);
-end);
diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua
index bfe22867..ef03ab0f 100644
--- a/plugins/mod_pep.lua
+++ b/plugins/mod_pep.lua
@@ -37,9 +37,16 @@ end
module:add_identity("pubsub", "pep", "Prosody");
module:add_feature("http://jabber.org/protocol/pubsub#publish");
-local function publish(session, node, item)
+local function subscription_presence(user_bare, recipient)
+ local recipient_bare = jid_bare(recipient);
+ if (recipient_bare == user_bare) then return true end
+ local item = load_roster(jid_split(user_bare))[recipient_bare];
+ return item and (item.subscription == 'from' or item.subscription == 'both');
+end
+
+local function publish(session, node, id, item)
item.attr.xmlns = nil;
- local disable = #item.tags ~= 1 or #item.tags[1].tags == 0;
+ local disable = #item.tags ~= 1 or #item.tags[1] == 0;
if #item.tags == 0 then item.name = "retract"; end
local bare = session.username..'@'..session.host;
local stanza = st.message({from=bare, type='headline'})
@@ -58,9 +65,9 @@ local function publish(session, node, item)
end
else
if not user_data then user_data = {}; data[bare] = user_data; end
- user_data[node] = stanza;
+ user_data[node] = {id or "1", item};
end
-
+
-- broadcast
for recipient, notify in pairs(recipients[bare] or NULL) do
if notify[node] then
@@ -74,10 +81,14 @@ local function publish_all(user, recipient, session)
local notify = recipients[user] and recipients[user][recipient];
if d and notify then
for node in pairs(notify) do
- local message = d[node];
- if message then
- message.attr.to = recipient;
- session.send(message);
+ if d[node] then
+ local id, item = unpack(d[node]);
+ session.send(st.message({from=user, to=recipient, type='headline'})
+ :tag('event', {xmlns='http://jabber.org/protocol/pubsub#event'})
+ :tag('items', {node=node})
+ :add_child(item)
+ :up()
+ :up());
end
end
end
@@ -106,11 +117,9 @@ end
module:hook("presence/bare", function(event)
-- inbound presence to bare JID recieved
local origin, stanza = event.origin, event.stanza;
-
local user = stanza.attr.to or (origin.username..'@'..origin.host);
- local bare = jid_bare(stanza.attr.from);
- local item = load_roster(jid_split(user))[bare];
- if not stanza.attr.to or (item and (item.subscription == 'from' or item.subscription == 'both')) then
+
+ if not stanza.attr.to or subscription_presence(user, stanza.attr.from) then
local recipient = stanza.attr.from;
local current = recipients[user] and recipients[user][recipient];
local hash = get_caps_hash_from_presence(stanza, current);
@@ -135,19 +144,63 @@ end, 10);
module:hook("iq/bare/http://jabber.org/protocol/pubsub:pubsub", function(event)
local session, stanza = event.origin, event.stanza;
+ local payload = stanza.tags[1];
+
if stanza.attr.type == 'set' and (not stanza.attr.to or jid_bare(stanza.attr.from) == stanza.attr.to) then
- local payload = stanza.tags[1];
- if payload.name == 'pubsub' then -- <pubsub xmlns='http://jabber.org/protocol/pubsub'>
+ payload = payload.tags[1];
+ if payload and (payload.name == 'publish' or payload.name == 'retract') and payload.attr.node then -- <publish node='http://jabber.org/protocol/tune'>
+ local node = payload.attr.node;
payload = payload.tags[1];
- if payload and (payload.name == 'publish' or payload.name == 'retract') and payload.attr.node then -- <publish node='http://jabber.org/protocol/tune'>
- local node = payload.attr.node;
- payload = payload.tags[1];
- if payload and payload.name == "item" then -- <item>
- session.send(st.reply(stanza));
- publish(session, node, st.clone(payload));
+ if payload and payload.name == "item" then -- <item>
+ local id = payload.attr.id;
+ session.send(st.reply(stanza));
+ publish(session, node, id, st.clone(payload));
+ return true;
+ end
+ end
+ elseif stanza.attr.type == 'get' then
+ local user = stanza.attr.to and jid_bare(stanza.attr.to) or session.username..'@'..session.host;
+ if subscription_presence(user, stanza.attr.from) then
+ local user_data = data[user];
+ local node, requested_id;
+ payload = payload.tags[1];
+ if payload and payload.name == 'items' then
+ node = payload.attr.node;
+ local item = payload.tags[1];
+ if item and item.name == "item" then
+ requested_id = item.attr.id;
+ end
+ end
+ if node and user_data and user_data[node] then -- Send the last item
+ local id, item = unpack(user_data[node]);
+ if not requested_id or id == requested_id then
+ local stanza = st.reply(stanza)
+ :tag('pubsub', {xmlns='http://jabber.org/protocol/pubsub'})
+ :tag('items', {node=node})
+ :add_child(item)
+ :up()
+ :up();
+ session.send(stanza);
+ return true;
+ else -- requested item doesn't exist
+ local stanza = st.reply(stanza)
+ :tag('pubsub', {xmlns='http://jabber.org/protocol/pubsub'})
+ :tag('items', {node=node})
+ :up();
+ session.send(stanza);
return true;
end
+ elseif node then -- node doesn't exist
+ session.send(st.error_reply(stanza, 'cancel', 'item-not-found'));
+ return true;
+ else --invalid request
+ session.send(st.error_reply(stanza, 'modify', 'bad-request'));
+ return true;
end
+ else --no presence subscription
+ session.send(st.error_reply(stanza, 'auth', 'not-authorized')
+ :tag('presence-subscription-required', {xmlns='http://jabber.org/protocol/pubsub#errors'}));
+ return true;
end
end
end);
diff --git a/plugins/mod_presence.lua b/plugins/mod_presence.lua
index abbc3a3d..a39d9c19 100644
--- a/plugins/mod_presence.lua
+++ b/plugins/mod_presence.lua
@@ -18,6 +18,7 @@ local st = require "util.stanza";
local jid_split = require "util.jid".split;
local jid_bare = require "util.jid".bare;
local hosts = hosts;
+local NULL = {};
local rostermanager = require "core.rostermanager";
local sessionmanager = require "core.sessionmanager";
@@ -54,16 +55,21 @@ local function select_top_resources(user)
end
return recipients;
end
-local function recalc_resource_map(origin)
- local user = hosts[origin.host].sessions[origin.username];
- user.top_resources = select_top_resources(user);
- if #user.top_resources == 0 then user.top_resources = nil; end
+local function recalc_resource_map(user)
+ if user then
+ user.top_resources = select_top_resources(user);
+ if #user.top_resources == 0 then user.top_resources = nil; end
+ end
end
function handle_normal_presence(origin, stanza, core_route_stanza)
+ if full_sessions[origin.full_jid] then -- if user is still connected
+ origin.send(stanza); -- reflect their presence back to them
+ end
local roster = origin.roster;
local node, host = origin.username, origin.host;
- for _, res in pairs(hosts[host].sessions[node].sessions) do -- broadcast to all resources
+ local user = bare_sessions[node.."@"..host];
+ for _, res in pairs(user and user.sessions or NULL) do -- broadcast to all resources
if res ~= origin and res.presence then -- to resource
stanza.attr.to = res.full_jid;
core_route_stanza(origin, stanza);
@@ -76,6 +82,7 @@ function handle_normal_presence(origin, stanza, core_route_stanza)
end
end
if stanza.attr.type == nil and not origin.presence then -- initial presence
+ origin.presence = stanza; -- FIXME repeated later
local probe = st.presence({from = origin.full_jid, type = "probe"});
for jid, item in pairs(roster) do -- probe all contacts we are subscribed to
if item.subscription == "both" or item.subscription == "to" then
@@ -83,7 +90,7 @@ function handle_normal_presence(origin, stanza, core_route_stanza)
core_route_stanza(origin, probe);
end
end
- for _, res in pairs(hosts[host].sessions[node].sessions) do -- broadcast from all available resources
+ for _, res in pairs(user and user.sessions or NULL) do -- broadcast from all available resources
if res ~= origin and res.presence then
res.presence.attr.to = origin.full_jid;
core_route_stanza(res, res.presence);
@@ -114,7 +121,7 @@ function handle_normal_presence(origin, stanza, core_route_stanza)
origin.presence = nil;
if origin.priority then
origin.priority = nil;
- recalc_resource_map(origin);
+ recalc_resource_map(user);
end
if origin.directed then
for jid in pairs(origin.directed) do
@@ -136,7 +143,7 @@ function handle_normal_presence(origin, stanza, core_route_stanza)
else priority = 0; end
if origin.priority ~= priority then
origin.priority = priority;
- recalc_resource_map(origin);
+ recalc_resource_map(user);
end
end
stanza.attr.to = nil; -- reset it
@@ -217,7 +224,7 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b
if stanza.attr.type == "probe" then
if rostermanager.is_contact_subscribed(node, host, from_bare) then
if 0 == send_presence_of_available_resources(node, host, st_from, origin, core_route_stanza) then
- -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too)
+ core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- TODO send last activity
end
else
core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unsubscribed"}));
@@ -227,7 +234,7 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b
core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="subscribed"})); -- already subscribed
-- Sending presence is not clearly stated in the RFC, but it seems appropriate
if 0 == send_presence_of_available_resources(node, host, from_bare, origin, core_route_stanza) then
- -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too)
+ core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- TODO send last activity
end
else
core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- acknowledging receipt
@@ -326,6 +333,20 @@ module:hook("presence/full", function(data)
end -- resource not online, discard
return true;
end);
+module:hook("presence/host", function(data)
+ -- inbound presence to the host
+ local origin, stanza = data.origin, data.stanza;
+
+ local from_bare = jid_bare(stanza.attr.from);
+ local t = stanza.attr.type;
+ if t == "probe" then
+ core_route_stanza(hosts[module.host], st.presence({ from = module.host, to = from_bare, id = stanza.attr.id }));
+ elseif t == "subscribe" then
+ core_route_stanza(hosts[module.host], st.presence({ from = module.host, to = from_bare, id = stanza.attr.id, type = "subscribed" }));
+ core_route_stanza(hosts[module.host], st.presence({ from = module.host, to = from_bare, id = stanza.attr.id }));
+ end
+ return true;
+end);
module:hook("resource-unbind", function(event)
local session, err = event.session, event.error;
diff --git a/plugins/mod_register.lua b/plugins/mod_register.lua
index bda40124..be1be0ae 100644
--- a/plugins/mod_register.lua
+++ b/plugins/mod_register.lua
@@ -141,7 +141,7 @@ module:add_iq_handler("c2s_unauthed", "jabber:iq:register", function (session, s
username = nodeprep(table.concat(username));
password = table.concat(password);
local host = module.host;
- if not username then
+ if not username or username == "" then
session.send(st.error_reply(stanza, "modify", "not-acceptable", "The requested username is invalid."));
elseif usermanager_user_exists(username, host) then
session.send(st.error_reply(stanza, "cancel", "conflict", "The requested username already exists."));
diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua
index e248479b..2aee2be0 100644
--- a/plugins/mod_saslauth.lua
+++ b/plugins/mod_saslauth.lua
@@ -38,13 +38,13 @@ local new_sasl = require "util.sasl".new;
local function build_reply(status, ret, err_msg)
local reply = st.stanza(status, {xmlns = xmlns_sasl});
if status == "challenge" then
- log("debug", "%s", ret or "");
+ --log("debug", "CHALLENGE: %s", ret or "");
reply:text(base64.encode(ret or ""));
elseif status == "failure" then
reply:tag(ret):up();
if err_msg then reply:tag("text"):text(err_msg); end
elseif status == "success" then
- log("debug", "%s", ret or "");
+ --log("debug", "SUCCESS: %s", ret or "");
reply:text(base64.encode(ret or ""));
else
module:log("error", "Unknown sasl status: %s", status);
@@ -124,7 +124,7 @@ local function sasl_handler(session, stanza)
local text = stanza[1];
if text then
text = base64.decode(text);
- log("debug", "%s", text);
+ --log("debug", "AUTH: %s", text:gsub("[%z\001-\008\011\012\014-\031]", " "));
if not text then
session.sasl_handler = nil;
session.send(build_reply("failure", "incorrect-encoding"));
diff --git a/plugins/mod_selftests.lua b/plugins/mod_selftests.lua
deleted file mode 100644
index 1f413634..00000000
--- a/plugins/mod_selftests.lua
+++ /dev/null
@@ -1,61 +0,0 @@
--- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-module.host = "*" -- Global module
-
-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 ping_hosts = module:get_option("ping_hosts") or { "coversant.interop.xmpp.org", "djabberd.interop.xmpp.org", "djabberd-trunk.interop.xmpp.org", "ejabberd.interop.xmpp.org", "openfire.interop.xmpp.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];
- module: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 cceef308..67555b15 100644
--- a/plugins/mod_tls.lua
+++ b/plugins/mod_tls.lua
@@ -14,9 +14,11 @@ local xmlns_starttls = 'urn:ietf:params:xml:ns:xmpp-tls';
local secure_auth_only = module:get_option("c2s_require_encryption") or module:get_option("require_encryption");
local secure_s2s_only = module:get_option("s2s_require_encryption");
+local host = hosts[module.host];
+
module:add_handler("c2s_unauthed", "starttls", xmlns_starttls,
function (session, stanza)
- if session.conn.starttls then
+ if session.conn.starttls and host.ssl_ctx_in then
session.send(st.stanza("proceed", { xmlns = xmlns_starttls }));
session:reset_stream();
if session.host and hosts[session.host].ssl_ctx_in then
@@ -26,14 +28,15 @@ module:add_handler("c2s_unauthed", "starttls", xmlns_starttls,
session.log("info", "TLS negotiation started...");
session.secure = false;
else
- -- FIXME: What reply?
session.log("warn", "Attempt to start TLS, but TLS is not available on this connection");
+ (session.sends2s or session.send)(st.stanza("failure", { xmlns = xmlns_starttls }));
+ session:close();
end
end);
module:add_handler("s2sin_unauthed", "starttls", xmlns_starttls,
function (session, stanza)
- if session.conn.starttls then
+ if session.conn.starttls and host.ssl_ctx_in then
session.sends2s(st.stanza("proceed", { xmlns = xmlns_starttls }));
session:reset_stream();
if session.to_host and hosts[session.to_host].ssl_ctx_in then
@@ -43,8 +46,9 @@ module:add_handler("s2sin_unauthed", "starttls", xmlns_starttls,
session.log("info", "TLS negotiation started for incoming s2s...");
session.secure = false;
else
- -- FIXME: What reply?
session.log("warn", "Attempt to start TLS, but TLS is not available on this s2s connection");
+ (session.sends2s or session.send)(st.stanza("failure", { xmlns = xmlns_starttls }));
+ session:close();
end
end);
@@ -66,7 +70,7 @@ module:hook("s2s-stream-features",
function (data)
local session, features = data.session, data.features;
if session.to_host and session.conn.starttls then
- features:tag("starttls", starttls_attr):up();
+ features:tag("starttls", starttls_attr);
if secure_s2s_only then
features:tag("required"):up():up();
else
diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua
index 3a185e17..60687886 100644
--- a/plugins/muc/muc.lib.lua
+++ b/plugins/muc/muc.lib.lua
@@ -128,19 +128,21 @@ function room_mt:broadcast_presence(stanza, sid, code, nick)
end
end
function room_mt:broadcast_message(stanza, historic)
+ local to = stanza.attr.to;
for occupant, o_data in pairs(self._occupants) do
for jid in pairs(o_data.sessions) do
stanza.attr.to = jid;
self:_route_stanza(stanza);
end
end
+ stanza.attr.to = to;
if historic then -- add to history
local history = self._data['history'];
if not history then history = {}; self._data['history'] = history; end
- -- stanza = st.clone(stanza);
+ stanza = st.clone(stanza);
stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
- t_insert(history, st.clone(st.preserialize(stanza)));
+ t_insert(history, st.preserialize(stanza));
while #history > history_length do t_remove(history, 1) end
end
end
@@ -461,6 +463,9 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha
if not item.attr.jid and item.attr.nick then -- COMPAT Workaround for Miranda sending 'nick' instead of 'jid' when changing affiliation
local occupant = self._occupants[self.jid.."/"..item.attr.nick];
if occupant then item.attr.jid = occupant.jid; end
+ elseif not item.attr.nick and item.attr.jid then
+ local nick = self._jid_nick[item.attr.jid];
+ if nick then item.attr.nick = select(3, jid_split(nick)); end
end
local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1];
if item.attr.affiliation and item.attr.jid and not item.attr.role then
@@ -492,9 +497,14 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha
-- TODO allow admins and owners not in room? Provide read-only access to everyone who can see the participants anyway?
if _rol == "none" then _rol = nil; end
local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin");
- for nick, occupant in pairs(self._occupants) do
+ for occupant_jid, occupant in pairs(self._occupants) do
if occupant.role == _rol then
- reply:tag("item", {nick = nick, role = _rol or "none", affiliation = occupant.affiliation or "none", jid = occupant.jid}):up();
+ reply:tag("item", {
+ nick = select(3, jid_split(occupant_jid)),
+ role = _rol or "none",
+ affiliation = occupant.affiliation or "none",
+ jid = occupant.jid
+ }):up();
end
end
origin.send(reply);
@@ -517,17 +527,26 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha
local from, to = stanza.attr.from, stanza.attr.to;
local room = jid_bare(to);
local current_nick = self._jid_nick[from];
- if not current_nick then -- not in room
+ local occupant = self._occupants[current_nick];
+ if not occupant then -- not in room
origin.send(st.error_reply(stanza, "cancel", "not-acceptable"));
+ elseif occupant.role == "visitor" then
+ origin.send(st.error_reply(stanza, "cancel", "forbidden"));
else
local from = stanza.attr.from;
stanza.attr.from = current_nick;
local subject = getText(stanza, {"subject"});
if subject then
- self:set_subject(current_nick, subject); -- TODO use broadcast_message_stanza
+ if occupant.role == "moderator" then
+ self:set_subject(current_nick, subject); -- TODO use broadcast_message_stanza
+ else
+ stanza.attr.from = from;
+ origin.send(st.error_reply(stanza, "cancel", "forbidden"));
+ end
else
self:broadcast_message(stanza, true);
end
+ stanza.attr.from = from;
end
elseif stanza.name == "message" and type == "error" and is_kickable_error(stanza) then
local current_nick = self._jid_nick[stanza.attr.from];
@@ -651,21 +670,21 @@ function room_mt:get_role(nick)
local session = self._occupants[nick];
return session and session.role or nil;
end
-function room_mt:set_role(actor, nick, role, callback, reason)
+function room_mt:set_role(actor, occupant_jid, role, callback, reason)
if role == "none" then role = nil; end
if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end
if self:get_affiliation(actor) ~= "owner" then return nil, "cancel", "not-allowed"; end
- local occupant = self._occupants[nick];
+ local occupant = self._occupants[occupant_jid];
if not occupant then return nil, "modify", "not-acceptable"; end
if occupant.affiliation == "owner" or occupant.affiliation == "admin" then return nil, "cancel", "not-allowed"; end
- local p = st.presence({from = nick})
+ local p = st.presence({from = occupant_jid})
:tag("x", {xmlns = "http://jabber.org/protocol/muc#user"})
- :tag("item", {affiliation=occupant.affiliation or "none", nick=nick, role=role or "none"})
+ :tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"})
:tag("reason"):text(reason or ""):up()
:up();
if not role then -- kick
p.attr.type = "unavailable";
- self._occupants[nick] = nil;
+ self._occupants[occupant_jid] = nil;
for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick
self._jid_nick[jid] = nil;
end
@@ -678,7 +697,7 @@ function room_mt:set_role(actor, nick, role, callback, reason)
self:_route_stanza(p);
end
if callback then callback(); end
- self:broadcast_except_nick(p, nick);
+ self:broadcast_except_nick(p, occupant_jid);
return true;
end
diff --git a/prosody b/prosody
index 40aeac5e..7f4a2cec 100755
--- a/prosody
+++ b/prosody
@@ -198,7 +198,7 @@ function init_global_state()
if type(port) ~= "number" then
log("error", "Non-numeric "..option.."_ports: "..tostring(port));
else
- cl.start(listener, {
+ local ok, err = cl.start(listener, {
ssl = conntype ~= "tcp" and global_ssl_ctx,
port = port,
interface = (option and config.get("*", "core", option.."_interface"))
@@ -206,6 +206,33 @@ function init_global_state()
or config.get("*", "core", "interface"),
type = conntype
});
+ if not ok then
+ local friendly_message = err;
+ if err:match(" in use") then
+ if port == 5222 or port == 5223 or port == 5269 then
+ friendly_message = "check that Prosody or another XMPP server is "
+ .."not already running and using this port";
+ elseif port == 80 or port == 81 then
+ friendly_message = "check that a HTTP server is not already using "
+ .."this port";
+ elseif port == 5280 then
+ friendly_message = "check that Prosody or a BOSH connection manager "
+ .."is not already running";
+ else
+ friendly_message = "this port is in use by another application";
+ end
+ elseif err:match("permission") then
+ friendly_message = "Prosody does not have sufficient privileges to use this port";
+ elseif err == "no ssl context" then
+ if not config.get("*", "core", "ssl") then
+ friendly_message = "there is no 'ssl' config under Host \"*\" which is "
+ .."require for legacy SSL ports";
+ else
+ friendly_message = "initializing SSL support failed, see previous log entries";
+ end
+ end
+ log("error", "Failed to open server port %d, %s", port, friendly_message);
+ end
end
end
end
@@ -280,15 +307,18 @@ function prepare_to_start()
-- start listening on sockets
prosody.net_activate_ports("c2s", "xmppclient", {5222});
prosody.net_activate_ports("s2s", "xmppserver", {5269});
- prosody.net_activate_ports("component", "xmppcomponent", {}, "tcp");
+ prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
prosody.start_time = os.time();
end
function init_global_protection()
- -- Catch global accesses --
- local locked_globals_mt = { __index = function (t, k) error("Attempt to read a non-existent global '"..k.."'", 2); end, __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end }
+ -- Catch global accesses
+ local locked_globals_mt = {
+ __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
+ __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
+ };
function prosody.unlock_globals()
setmetatable(_G, nil);
diff --git a/prosody.cfg.lua.dist b/prosody.cfg.lua.dist
index d660a9bd..04a1ce5e 100644
--- a/prosody.cfg.lua.dist
+++ b/prosody.cfg.lua.dist
@@ -63,7 +63,7 @@ Host "*"
-- Other specific functionality
--"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
- --"console"; -- telnet to port 5582 (needs console_enabled = true)
+ --"console"; -- Opens admin telnet interface on localhost port 5582
--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
--"httpserver"; -- Serve static files from a directory over HTTP
};
diff --git a/tests/test_util_jid.lua b/tests/test_util_jid.lua
index fe6ec74e..f579e597 100644
--- a/tests/test_util_jid.lua
+++ b/tests/test_util_jid.lua
@@ -6,6 +6,16 @@
-- COPYING file in the source package for more information.
--
+function join(join)
+ assert_equal(join("a", "b", "c"), "a@b/c", "builds full JID");
+ assert_equal(join("a", "b", nil), "a@b", "builds bare JID");
+ assert_equal(join(nil, "b", "c"), "b/c", "builds full host JID");
+ assert_equal(join(nil, "b", nil), "b", "builds bare host JID");
+ assert_equal(join(nil, nil, nil), nil, "invalid JID is nil");
+ assert_equal(join("a", nil, nil), nil, "invalid JID is nil");
+ assert_equal(join(nil, nil, "c"), nil, "invalid JID is nil");
+ assert_equal(join("a", nil, "c"), nil, "invalid JID is nil");
+end
function split(split)
@@ -43,3 +53,4 @@ function bare(bare)
assert_equal(bare("user@@host/resource"), nil, "invalid JID is nil");
assert_equal(bare("user@host/"), nil, "invalid JID is nil");
end
+
diff --git a/tools/ejabberd2prosody.lua b/tools/ejabberd2prosody.lua
index 7b19260d..d0c22df7 100755
--- a/tools/ejabberd2prosody.lua
+++ b/tools/ejabberd2prosody.lua
@@ -49,7 +49,7 @@ function vcard(node, host, stanza)
end
function password(node, host, password)
local ret, err = dm.store(node, host, "accounts", {password = password});
- print("["..(err or "success").."] accounts: "..node.."@"..host.." = "..password);
+ print("["..(err or "success").."] accounts: "..node.."@"..host);
end
function roster(node, host, jid, item)
local roster = dm.load(node, host, "roster") or {};
diff --git a/tools/ejabberdsql2prosody.lua b/tools/ejabberdsql2prosody.lua
index f652af5b..0e5dd0c4 100644
--- a/tools/ejabberdsql2prosody.lua
+++ b/tools/ejabberdsql2prosody.lua
@@ -254,7 +254,7 @@ end
for i, row in ipairs(t["users"] or NULL) do
local node, password = row.username, row.password;
local ret, err = dm.store(node, host, "accounts", {password = password});
- print("["..(err or "success").."] accounts: "..node.."@"..host.." = "..password);
+ print("["..(err or "success").."] accounts: "..node.."@"..host);
end
function roster(node, host, jid, item)
diff --git a/util-src/encodings.c b/util-src/encodings.c
index 5147512f..c573a330 100644
--- a/util-src/encodings.c
+++ b/util-src/encodings.c
@@ -174,7 +174,7 @@ static int Lidna_to_ascii(lua_State *L) /** idna.to_ascii(s) */
size_t len;
const char *s = luaL_checklstring(L, 1, &len);
char* output = NULL;
- int ret = idna_to_ascii_8z(s, &output, 0);
+ int ret = idna_to_ascii_8z(s, &output, IDNA_USE_STD3_ASCII_RULES);
if (ret == IDNA_SUCCESS) {
lua_pushstring(L, output);
idn_free(output);
diff --git a/util-src/pposix.c b/util-src/pposix.c
index 94086ed6..cef75287 100644
--- a/util-src/pposix.c
+++ b/util-src/pposix.c
@@ -463,9 +463,10 @@ int lc_getrlimit(lua_State *L) {
return 3;
}
-void lc_abort(lua_State* L)
+int lc_abort(lua_State* L)
{
abort();
+ return 0;
}
/* Register functions */
diff --git a/util/dataforms.lua b/util/dataforms.lua
index a3bde8ca..56671347 100644
--- a/util/dataforms.lua
+++ b/util/dataforms.lua
@@ -23,8 +23,8 @@ function new(layout)
return setmetatable(layout, form_mt);
end
-function form_t.form(layout, data)
- local form = st.stanza("x", { xmlns = xmlns_forms, type = "form" });
+function form_t.form(layout, data, formtype)
+ local form = st.stanza("x", { xmlns = xmlns_forms, type = formtype or "form" });
if layout.title then
form:tag("title"):text(layout.title):up();
end
diff --git a/util/dependencies.lua b/util/dependencies.lua
index a0535e5c..cb022644 100644
--- a/util/dependencies.lua
+++ b/util/dependencies.lua
@@ -17,8 +17,12 @@ local function missingdep(name, sources, msg)
print("Prosody was unable to find "..tostring(name));
print("This package can be obtained in the following ways:");
print("");
- for k,v in pairs(sources) do
- print("", k, v);
+ local longest_platform = 0;
+ for platform in pairs(sources) do
+ longest_platform = math.max(longest_platform, #platform);
+ end
+ for platform, source in pairs(sources) do
+ print("", platform..":"..(" "):rep(4+longest_platform-#platform)..source);
end
print("");
print(msg or (name.." is required for Prosody to run, so we will now exit."));
diff --git a/util/jid.lua b/util/jid.lua
index ccc8309c..b43247cc 100644
--- a/util/jid.lua
+++ b/util/jid.lua
@@ -65,4 +65,17 @@ function prep(jid)
return host;
end
+function join(node, host, resource)
+ if node and host and resource then
+ return node.."@"..host.."/"..resource;
+ elseif node and host then
+ return node.."@"..host;
+ elseif host and resource then
+ return host.."/"..resource;
+ elseif host then
+ return host;
+ end
+ return nil; -- Invalid JID
+end
+
return _M;
diff --git a/util/sasl.lua b/util/sasl.lua
index 402f05b4..5bc6db75 100644
--- a/util/sasl.lua
+++ b/util/sasl.lua
@@ -38,7 +38,7 @@ local function new_plain(realm, credentials_handler)
function object.feed(self, message)
if message == "" or message == nil then return "failure", "malformed-request" end
local response = message
- local authorization = s_match(response, "([^%z]+)")
+ local authorization = s_match(response, "([^%z]*)")
local authentication = s_match(response, "%z([^%z]+)%z")
local password = s_match(response, "%z[^%z]+%z([^%z]+)")
diff --git a/util/stanza.lua b/util/stanza.lua
index d295d5cc..069daa53 100644
--- a/util/stanza.lua
+++ b/util/stanza.lua
@@ -93,6 +93,17 @@ function stanza_mt:add_child(child)
return self;
end
+function stanza_mt:get_child(name, xmlns)
+ for _, child in ipairs(self.tags) do
+ if (not name or child.name == name)
+ and ((not xmlns and self.attr.xmlns == child.attr.xmlns)
+ or child.attr.xmlns == xmlns) then
+
+ return child;
+ end
+ end
+end
+
function stanza_mt:child_with_name(name)
for _, child in ipairs(self.tags) do
if child.name == name then return child; end
@@ -280,13 +291,16 @@ function reply(orig)
return stanza(orig.name, orig.attr and { to = orig.attr.from, from = orig.attr.to, id = orig.attr.id, type = ((orig.name == "iq" and "result") or orig.attr.type) });
end
-function error_reply(orig, type, condition, message)
- local t = reply(orig);
- t.attr.type = "error";
- t:tag("error", {type = type})
- :tag(condition, {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
- if (message) then t:tag("text"):text(message):up(); end
- return t; -- stanza ready for adding app-specific errors
+do
+ local xmpp_stanzas_attr = { xmlns = xmlns_stanzas };
+ function error_reply(orig, type, condition, message)
+ local t = reply(orig);
+ t.attr.type = "error";
+ t:tag("error", {type = type}) --COMPAT: Some day xmlns:stanzas goes here
+ :tag(condition, xmpp_stanzas_attr):up();
+ if (message) then t:tag("text", xmpp_stanzas_attr):text(message):up(); end
+ return t; -- stanza ready for adding app-specific errors
+ end
end
function presence(attr)