diff options
43 files changed, 526 insertions, 519 deletions
diff --git a/core/portmanager.lua b/core/portmanager.lua index 9eb40abf..55868c34 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -9,7 +9,7 @@ local set = require "util.set"; local table = table; local setmetatable, rawset, rawget = setmetatable, rawset, rawget; -local type, tonumber, tostring, ipairs = type, tonumber, tostring, ipairs; +local type, tonumber, ipairs = type, tonumber, ipairs; local pairs = pairs; local prosody = prosody; @@ -103,7 +103,7 @@ local function activate(service_name) for port in bind_ports do local port_number = tonumber(port); if not port_number then - log("error", "Invalid port number specified for service '%s': %s", service_info.name, tostring(port)); + log("error", "Invalid port number specified for service '%s': %s", service_info.name, port); elseif #active_services:search(nil, interface, port_number) > 0 then log("error", "Multiple services configured to listen on the same port ([%s]:%d): %s, %s", interface, port, active_services:search(nil, interface, port)[1][1].service.name or "<unnamed>", service_name or "<unnamed>"); diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 684bb94e..ccdf4932 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -9,8 +9,7 @@ local hosts = prosody.hosts; -local tostring, pairs, setmetatable - = tostring, pairs, setmetatable; +local pairs, setmetatable = pairs, setmetatable; local logger_init = require "util.logger".init; local sessionlib = require "util.session"; @@ -75,8 +74,8 @@ local function retire_session(session, reason) session.destruction_reason = reason; - function session.send(data) log("debug", "Discarding data sent to resting session: %s", tostring(data)); end - function session.data(data) log("debug", "Discarding data received from resting session: %s", tostring(data)); end + function session.send(data) log("debug", "Discarding data sent to resting session: %s", data); end + function session.data(data) log("debug", "Discarding data received from resting session: %s", data); end session.thread = { run = function (_, data) return session.data(data) end }; session.sends2s = session.send; return setmetatable(session, resting_session); @@ -84,9 +83,8 @@ end local function destroy_session(session, reason) if session.destroyed then return; end - (session.log or log)("debug", "Destroying "..tostring(session.direction) - .." session "..tostring(session.from_host).."->"..tostring(session.to_host) - ..(reason and (": "..reason) or "")); + local log = session.log or log; + log("debug", "Destroying %s session %s->%s%s%s", session.direction, session.from_host, session.to_host, reason and ": " or "", reason or ""); if session.direction == "outgoing" then hosts[session.from_host].s2sout[session.to_host] = nil; diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 55f096b9..29779c3c 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -44,7 +44,7 @@ local function new_session(conn) if t then local ret, err = w(conn, t); if not ret then - session.log("debug", "Error writing to connection: %s", tostring(err)); + session.log("debug", "Error writing to connection: %s", err); return false, err; end end @@ -85,8 +85,8 @@ local function retire_session(session) end end - function session.send(data) log("debug", "Discarding data sent to resting session: %s", tostring(data)); return false; end - function session.data(data) log("debug", "Discarding data received from resting session: %s", tostring(data)); end + function session.send(data) log("debug", "Discarding data sent to resting session: %s", data); return false; end + function session.data(data) log("debug", "Discarding data received from resting session: %s", data); end session.thread = { run = function (_, data) return session.data(data) end }; return setmetatable(session, resting_session); end diff --git a/core/stanza_router.lua b/core/stanza_router.lua index f5a34f59..d3caeb5d 100644 --- a/core/stanza_router.lua +++ b/core/stanza_router.lua @@ -199,7 +199,7 @@ function core_route_stanza(origin, stanza) else local host_session = hosts[from_host]; if not host_session then - log("error", "No hosts[from_host] (please report): %s", tostring(stanza)); + log("error", "No hosts[from_host] (please report): %s", stanza); else local xmlns = stanza.attr.xmlns; stanza.attr.xmlns = nil; diff --git a/net/adns.lua b/net/adns.lua index 4fa01f8a..d7266958 100644 --- a/net/adns.lua +++ b/net/adns.lua @@ -11,7 +11,7 @@ local new_resolver = require "net.dns".resolver; local log = require "util.logger".init("adns"); -local coroutine, tostring, pcall = coroutine, tostring, pcall; +local coroutine, pcall = coroutine, pcall; local setmetatable = setmetatable; local function dummy_send(sock, data, i, j) return (j-i)+1; end -- luacheck: ignore 212 @@ -73,11 +73,11 @@ function async_resolver_methods:lookup(handler, qname, qtype, qclass) handler(peek); return; end - log("debug", "Records for %s not in cache, sending query (%s)...", qname, tostring(coroutine.running())); + log("debug", "Records for %s not in cache, sending query (%s)...", qname, coroutine.running()); local ok, err = resolver:query(qname, qtype, qclass); if ok then coroutine.yield(setmetatable({ resolver, qclass or "IN", qtype or "A", qname, coroutine.running()}, query_mt)); -- Wait for reply - log("debug", "Reply for %s (%s)", qname, tostring(coroutine.running())); + log("debug", "Reply for %s (%s)", qname, coroutine.running()); end if ok then ok, err = pcall(handler, resolver:peek(qname, qtype, qclass)); @@ -86,13 +86,13 @@ function async_resolver_methods:lookup(handler, qname, qtype, qclass) ok, err = pcall(handler, nil, err); end if not ok then - log("error", "Error in DNS response handler: %s", tostring(err)); + log("error", "Error in DNS response handler: %s", err); end end)(resolver:peek(qname, qtype, qclass)); end function query_methods:cancel(call_handler, reason) -- luacheck: ignore 212/reason - log("warn", "Cancelling DNS lookup for %s", tostring(self[4])); + log("warn", "Cancelling DNS lookup for %s", self[4]); self[1].cancel(self[2], self[3], self[4], self[5], call_handler); end diff --git a/net/connect.lua b/net/connect.lua index b812ffcd..d4de6fb4 100644 --- a/net/connect.lua +++ b/net/connect.lua @@ -38,7 +38,7 @@ local function attempt_connection(p) p:log("debug", "Next target to try is %s:%d", ip, port); local conn, err = server.addclient(ip, port, pending_connection_listeners, p.options.pattern or "*a", p.options.sslctx, conn_type, extra); if not conn then - log("debug", "Connection attempt failed immediately: %s", tostring(err)); + log("debug", "Connection attempt failed immediately: %s", err); p.last_error = err or "unknown reason"; return attempt_connection(p); end diff --git a/net/http.lua b/net/http.lua index fe5250ac..0e03fb3a 100644 --- a/net/http.lua +++ b/net/http.lua @@ -40,7 +40,7 @@ local listener = { default_port = 80, default_mode = "*a" }; local function handleerr(err) log("error", "Traceback[http]: %s", traceback(tostring(err), 2)); return err; end local function log_if_failed(req, ret, ...) if not ret then - log("error", "Request '%s': error in callback: %s", req.id, tostring((...))); + log("error", "Request '%s': error in callback: %s", req.id, (...)); if not req.suppress_errors then error(...); end @@ -150,7 +150,7 @@ function listener.onincoming(conn, data) local request = requests[conn]; if not request then - log("warn", "Received response from connection %s with no request attached!", tostring(conn)); + log("warn", "Received response from connection %s with no request attached!", conn); return; end diff --git a/net/http/files.lua b/net/http/files.lua index 090b15c8..7ff81fc8 100644 --- a/net/http/files.lua +++ b/net/http/files.lua @@ -94,7 +94,7 @@ local function serve(opts) if data and data.etag == etag then response_headers.content_type = data.content_type; data = data.data; - cache:get(orig_path, data); + cache:set(orig_path, data); elseif attr.mode == "directory" and path then if full_path:sub(-1) ~= "/" then local dir_path = { is_absolute = true, is_directory = true }; diff --git a/net/resolvers/service.lua b/net/resolvers/service.lua index d1b8556c..62d269e5 100644 --- a/net/resolvers/service.lua +++ b/net/resolvers/service.lua @@ -33,7 +33,11 @@ function methods:next(cb) -- Resolve DNS to target list local dns_resolver = adns.resolver(); - dns_resolver:lookup(function (answer) + dns_resolver:lookup(function (answer, err) + if not answer and not err then + -- net.adns returns nil if there are zero records or nxdomain + answer = {}; + end if answer then if #answer == 0 then if self.extra and self.extra.default_port then diff --git a/net/server_epoll.lua b/net/server_epoll.lua index 5f62d931..49ad48ea 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -9,12 +9,12 @@ local t_insert = table.insert; local t_concat = table.concat; local setmetatable = setmetatable; -local tostring = tostring; local pcall = pcall; local type = type; local next = next; local pairs = pairs; -local log = require "util.logger".init("server_epoll"); +local logger = require "util.logger"; +local log = logger.init("server_epoll"); local socket = require "socket"; local luasec = require "ssl"; local gettime = require "util.time".now; @@ -23,6 +23,7 @@ local createtable = require "util.table".create; local inet = require "util.net"; local inet_pton = inet.pton; local _SOCKETINVALID = socket._SOCKETINVALID or -1; +local new_id = require "util.id".medium; local poller = require "util.poll" local EEXIST = poller.EEXIST; @@ -61,6 +62,10 @@ local default_config = { __index = { -- Maximum and minimum amount of time to sleep waiting for events (adjusted for pending timers) max_wait = 86400; min_wait = 1e-06; + + -- EXPERIMENTAL + -- Whether to kill connections in case of callback errors. + fatal_errors = false; }}; local cfg = default_config.__index; @@ -141,6 +146,15 @@ function interface_mt:__tostring() return ("FD %d"):format(self:getfd()); end +interface.log = log; +function interface:debug(msg, ...) --luacheck: ignore 212/self + self.log("debug", msg, ...); +end + +function interface:error(msg, ...) --luacheck: ignore 212/self + self.log("error", msg, ...); +end + -- Replace the listener and tell the old one function interface:setlistener(listeners, data) self:on("detach"); @@ -151,18 +165,23 @@ end -- Call a listener callback function interface:on(what, ...) if not self.listeners then - log("error", "%s has no listeners", self); + self:debug("Interface is missing listener callbacks"); return; end local listener = self.listeners["on"..what]; if not listener then - -- log("debug", "Missing listener 'on%s'", what); -- uncomment for development and debugging + -- self:debug("Missing listener 'on%s'", what); -- uncomment for development and debugging return; end local ok, err = pcall(listener, self, ...); if not ok then - log("error", "Error calling on%s: %s", what, err); - return; + if cfg.fatal_errors then + self:debug("Closing due to error calling on%s: %s", what, err); + self:destroy(); + else + self:debug("Error calling on%s: %s", what, err); + end + return nil, err; end return err; end @@ -273,15 +292,15 @@ function interface:add(r, w) local ok, err, errno = poll:add(fd, r, w); if not ok then if errno == EEXIST then - log("debug", "%s already registered!", self); + self:debug("FD already registered in poller! (EEXIST)"); return self:set(r, w); -- So try to change its flags end - log("error", "Could not register %s: %s(%d)", self, err, errno); + self:debug("Could not register in poller: %s(%d)", err, errno); return ok, err; end self._wantread, self._wantwrite = r, w; fds[fd] = self; - log("debug", "Watching %s", self); + self:debug("Registered in poller"); return true; end @@ -294,7 +313,7 @@ function interface:set(r, w) if w == nil then w = self._wantwrite; end local ok, err, errno = poll:set(fd, r, w); if not ok then - log("error", "Could not update poller state %s: %s(%d)", self, err, errno); + self:debug("Could not update poller state: %s(%d)", err, errno); return ok, err; end self._wantread, self._wantwrite = r, w; @@ -311,12 +330,12 @@ function interface:del() end local ok, err, errno = poll:del(fd); if not ok and errno ~= ENOENT then - log("error", "Could not unregister %s: %s(%d)", self, err, errno); + self:debug("Could not unregister: %s(%d)", err, errno); return ok, err; end self._wantread, self._wantwrite = nil, nil; fds[fd] = nil; - log("debug", "Unwatched %s", self); + self:debug("Unregistered from poller"); return true; end @@ -358,6 +377,14 @@ function interface:onreadable() end end if not self.conn then return; end + if self._limit and (data or partial) then + local cost = self._limit * #(data or partial); + if cost > cfg.min_wait then + self:setreadtimeout(false); + self:pausefor(cost); + return; + end + end if self._wantread and self.conn:dirty() then self:setreadtimeout(false); self:pausefor(cfg.read_retry_delay); @@ -424,10 +451,10 @@ function interface:close() if self.writebuffer and self.writebuffer[1] then self:set(false, true); -- Flush final buffer contents self.write, self.send = noop, noop; -- No more writing - log("debug", "Close %s after writing", self); + self:debug("Close after writing"); self.ondrain = interface.close; else - log("debug", "Close %s now", self); + self:debug("Closing now"); self.write, self.send = noop, noop; self.close = noop; self:on("disconnect"); @@ -456,7 +483,7 @@ function interface:starttls(tls_ctx) if tls_ctx then self.tls_ctx = tls_ctx; end self.starttls = false; if self.writebuffer and self.writebuffer[1] then - log("debug", "Start TLS on %s after write", self); + self:debug("Start TLS after write"); self.ondrain = interface.starttls; self:set(nil, true); -- make sure wantwrite is set else @@ -466,7 +493,7 @@ function interface:starttls(tls_ctx) self.onwritable = interface.tlshandskake; self.onreadable = interface.tlshandskake; self:set(true, true); - log("debug", "Prepare to start TLS on %s", self); + self:debug("Prepared to start TLS"); end end @@ -475,12 +502,12 @@ function interface:tlshandskake() self:setreadtimeout(false); if not self._tls then self._tls = true; - log("debug", "Start TLS on %s now", self); + self:debug("Starting TLS now"); self:del(); local ok, conn, err = pcall(luasec.wrap, self.conn, self.tls_ctx); if not ok then conn, err = ok, conn; - log("error", "Failed to initialize TLS: %s", err); + self:debug("Failed to initialize TLS: %s", err); end if not conn then self:on("disconnect", err); @@ -504,22 +531,22 @@ function interface:tlshandskake() end local ok, err = self.conn:dohandshake(); if ok then - log("debug", "TLS handshake on %s complete", self); + self:debug("TLS handshake complete"); self.onwritable = nil; self.onreadable = nil; self:on("status", "ssl-handshake-complete"); self:setwritetimeout(); self:set(true, true); elseif err == "wantread" then - log("debug", "TLS handshake on %s to wait until readable", self); + self:debug("TLS handshake to wait until readable"); self:set(true, false); self:setreadtimeout(cfg.ssl_handshake_timeout); elseif err == "wantwrite" then - log("debug", "TLS handshake on %s to wait until writable", self); + self:debug("TLS handshake to wait until writable"); self:set(false, true); self:setwritetimeout(cfg.ssl_handshake_timeout); else - log("debug", "TLS handshake error on %s: %s", self, err); + self:debug("TLS handshake error: %s", err); self:on("disconnect", err); self:destroy(); end @@ -536,6 +563,7 @@ local function wrapsocket(client, server, read_size, listeners, tls_ctx) -- luas writebuffer = {}; tls_ctx = tls_ctx or (server and server.tls_ctx); tls_direct = server and server.tls_direct; + log = logger.init(("conn%s"):format(new_id())); }, interface_mt); conn:updatenames(); @@ -559,12 +587,12 @@ end function interface:onacceptable() local conn, err = self.conn:accept(); if not conn then - log("debug", "Error accepting new client: %s, server will be paused for %ds", err, cfg.accept_retry_interval); + self:debug("Error accepting new client: %s, server will be paused for %ds", err, cfg.accept_retry_interval); self:pausefor(cfg.accept_retry_interval); return; end local client = wrapsocket(conn, self, nil, self.listeners); - log("debug", "New connection %s", tostring(client)); + client:debug("New connection %s on server %s", client, self); client:init(); if self.tls_direct then client:starttls(self.tls_ctx); @@ -589,6 +617,7 @@ end -- Pause connection for some time function interface:pausefor(t) + self:debug("Pause for %fs", t); if self._pausefor then self._pausefor:close(); end @@ -603,6 +632,14 @@ function interface:pausefor(t) end); end +function interface:setlimit(Bps) + if Bps > 0 then + self._limit = 1/Bps; + else + self._limit = nil; + end +end + function interface:pause_writes() self._write_lock = true; self:setwritetimeout(false); @@ -639,7 +676,9 @@ local function listen(addr, port, listeners, config) hosts = config and config.sni_hosts; sockname = addr; sockport = port; + log = logger.init(("serv%s"):format(new_id())); }, interface_mt); + server:debug("Server %s created", server); server:add(true, false); return server; end @@ -686,6 +725,7 @@ local function addclient(addr, port, listeners, read_size, tls_ctx, typ) return nil, "invalid socket type"; end local conn, err = create(); + if not conn then return conn, err; end local ok, err = conn:settimeout(0); if not ok then return ok, err; end local ok, err = conn:setpeername(addr, port); @@ -696,6 +736,7 @@ local function addclient(addr, port, listeners, read_size, tls_ctx, typ) if tls_ctx then client:starttls(tls_ctx); end + client:debug("Client %s created", client); return client, conn; end @@ -714,6 +755,7 @@ local function watchfd(fd, onreadable, onwritable) end; -- Otherwise it'll need to be something LuaSocket-compatible end + conn.log = logger.init(("fdwatch%s"):format(new_id())); conn:add(onreadable, onwritable); return conn; end; @@ -804,6 +846,7 @@ return { -- libevent emulation event = { EV_READ = "r", EV_WRITE = "w", EV_READWRITE = "rw", EV_LEAVE = -1 }; addevent = function (fd, mode, callback) + log("warn", "Using deprecated libevent emulation, please update code to use watchfd API instead"); local function onevent(self) local ret = self:callback(); if ret == -1 then @@ -823,6 +866,7 @@ return { fds[fd] = nil; end; }, interface_mt); + conn.log = logger.init(("fdwatch%d"):format(conn:getfd())); local ok, err = conn:add(mode == "r" or mode == "rw", mode == "w" or mode == "rw"); if not ok then return ok, err; end return conn; diff --git a/net/websocket.lua b/net/websocket.lua index 469c6a58..fb16b8bb 100644 --- a/net/websocket.lua +++ b/net/websocket.lua @@ -113,7 +113,7 @@ function websocket_listeners.onincoming(conn, buffer, err) -- luacheck: ignore 2 frame.MASK = true; -- RFC 6455 6.1.5: If the data is being sent by the client, the frame(s) MUST be masked conn:write(frames.build(frame)); elseif frame.opcode == 0xA then -- Pong frame - log("debug", "Received unexpected pong frame: " .. tostring(frame.data)); + log("debug", "Received unexpected pong frame: %s", frame.data); else return fail(s, 1002, "Reserved opcode"); end @@ -131,7 +131,7 @@ end function websocket_methods:close(code, reason) if self.readyState < 2 then code = code or 1000; - log("debug", "closing WebSocket with code %i: %s" , code , tostring(reason)); + log("debug", "closing WebSocket with code %i: %s" , code , reason); self.readyState = 2; local conn = self.conn; conn:write(frames.build_close(code, reason, true)); @@ -245,7 +245,7 @@ local function connect(url, ex, listeners) or (protocol and not protocol[r.headers["sec-websocket-protocol"]]) then s.readyState = 3; - log("warn", "WebSocket connection to %s failed: %s", url, tostring(b)); + log("warn", "WebSocket connection to %s failed: %s", url, b); if s.onerror then s:onerror("connecting-failed"); end return; end diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index fa03840b..b6cdfe82 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -62,6 +62,9 @@ end function runner_callbacks:error(err) module:log("error", "Traceback[telnet]: %s", err); + + self.data.print("Fatal error while running command, it did not complete"); + self.data.print("Error: "..tostring(err)); end @@ -114,6 +117,11 @@ function console:process_line(session, line) session.env._ = line; + if not useglobalenv and commands[line:lower()] then + commands[line:lower()](session, line); + return; + end + local chunkname = "=console"; local env = (useglobalenv and redirect_output(_G, session)) or session.env or nil local chunk, err = envload("return "..line, chunkname, env); @@ -128,18 +136,7 @@ function console:process_line(session, line) end end - local ranok, taskok, message = pcall(chunk); - - if not (ranok or message or useglobalenv) and commands[line:lower()] then - commands[line:lower()](session, line); - return; - end - - if not ranok then - session.print("Fatal error while running command, it did not complete"); - session.print("Error: "..taskok); - return; - end + local taskok, message = chunk(); if not message then session.print("Result: "..tostring(taskok)); @@ -242,6 +239,7 @@ function commands.help(session, data) print [[server - Uptime, version, shutting down, etc.]] print [[port - Commands to manage ports the server is listening on]] print [[dns - Commands to manage and inspect the internal DNS resolver]] + print [[xmpp - Commands for sending XMPP stanzas]] print [[config - Reloading the configuration, etc.]] print [[console - Help regarding the console itself]] elseif section == "c2s" then @@ -249,6 +247,7 @@ function commands.help(session, data) print [[c2s:show_insecure() - Show all unencrypted client connections]] print [[c2s:show_secure() - Show all encrypted client connections]] print [[c2s:show_tls() - Show TLS cipher info for encrypted sessions]] + print [[c2s:count() - Count sessions without listing them]] print [[c2s:close(jid) - Close all sessions for the specified JID]] print [[c2s:closeall() - Close all active c2s connections ]] elseif section == "s2s" then @@ -284,6 +283,8 @@ function commands.help(session, data) print [[dns:setnameserver(nameserver) - Replace the list of name servers with the supplied one]] print [[dns:purge() - Clear the DNS cache]] print [[dns:cache() - Show cached records]] + elseif section == "xmpp" then + print [[xmpp:ping(localhost, remotehost) -- Sends a ping to a remote XMPP server and reports the response]] 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 @@ -595,10 +596,16 @@ local function get_jid(session) return jid_join("["..ip.."]:"..clientport, session.host or "["..serverip.."]:"..serverport); end -local function show_c2s(callback) - local c2s = array.collect(values(module:shared"/*/c2s/sessions")); +local function get_c2s() + local c2s = array.collect(values(prosody.full_sessions)); + c2s:append(array.collect(values(module:shared"/*/c2s/sessions"))); c2s:append(array.collect(values(module:shared"/*/bosh/sessions"))); - c2s:sort(function(a, b) + c2s:unique(); + return c2s; +end + +local function show_c2s(callback) + get_c2s():sort(function(a, b) if a.host == b.host then if a.username == b.username then return (a.resource or "") > (b.resource or ""); @@ -612,9 +619,8 @@ local function show_c2s(callback) end function def_env.c2s:count() - local c2s_count = iterators.count(values(module:shared"/*/c2s/sessions")) - local bosh_count = iterators.count(values(module:shared"/*/bosh/sessions")) - return true, "Total: ".. c2s_count + bosh_count .." clients"; + local c2s = get_c2s(); + return true, "Total: ".. #c2s .." clients"; end function def_env.c2s:show(match_jid, annotate) @@ -660,23 +666,32 @@ function def_env.c2s:show_tls(match_jid) return self:show(match_jid, tls_info); end -function def_env.c2s:close(match_jid) +local function build_reason(text, condition) + if text or condition then + return { + text = text, + condition = condition or "undefined-condition", + }; + end +end + +function def_env.c2s:close(match_jid, text, condition) local count = 0; show_c2s(function (jid, session) if jid == match_jid or jid_bare(jid) == match_jid then count = count + 1; - session:close(); + session:close(build_reason(text, condition)); end end); return true, "Total: "..count.." sessions closed"; end -function def_env.c2s:closeall() +function def_env.c2s:closeall(text, condition) local count = 0; --luacheck: ignore 212/jid show_c2s(function (jid, session) count = count + 1; - session:close(); + session:close(build_reason(text, condition)); end); return true, "Total: "..count.." sessions closed"; end @@ -881,7 +896,7 @@ function def_env.s2s:showcert(domain) .." presented by "..domain.."."); end -function def_env.s2s:close(from, to) +function def_env.s2s:close(from, to, text, condition) local print, count = self.session.print, 0; local s2s_sessions = module:shared"/*/s2s/sessions"; @@ -895,23 +910,23 @@ function def_env.s2s:close(from, to) end for _, session in pairs(s2s_sessions) do - local id = session.type..tostring(session):match("[a-f0-9]+$"); + local id = session.id or (session.type..tostring(session):match("[a-f0-9]+$")); if (match_id and match_id == id) or (session.from_host == from and session.to_host == to) then print(("Closing connection from %s to %s [%s]"):format(session.from_host, session.to_host, id)); - (session.close or s2smanager.destroy_session)(session); + (session.close or s2smanager.destroy_session)(session, build_reason(text, condition)); count = count + 1 ; end end return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end -function def_env.s2s:closeall(host) +function def_env.s2s:closeall(host, text, condition) local count = 0; local s2s_sessions = module:shared"/*/s2s/sessions"; for _,session in pairs(s2s_sessions) do if not host or session.from_host == host or session.to_host == host then - session:close(); + session:close(build_reason(text, condition)); count = count + 1; end end diff --git a/plugins/mod_blocklist.lua b/plugins/mod_blocklist.lua index cf8aad80..dad06b62 100644 --- a/plugins/mod_blocklist.lua +++ b/plugins/mod_blocklist.lua @@ -67,7 +67,7 @@ local function migrate_privacy_list(username) if item.type == "jid" and item.action == "deny" then local jid = jid_prep(item.value); if not jid then - module:log("warn", "Invalid JID in privacy store for user '%s' not migrated: %s", username, tostring(item.value)); + module:log("warn", "Invalid JID in privacy store for user '%s' not migrated: %s", username, item.value); else migrated_data[jid] = true; end diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index d4e980f2..f4d7eba2 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -78,7 +78,7 @@ end); -- Used to respond to idle sessions (those with waiting requests) function on_destroy_request(request) - log("debug", "Request destroyed: %s", tostring(request)); + log("debug", "Request destroyed: %s", request); local session = sessions[request.context.sid]; if session then local requests = session.requests; @@ -115,7 +115,7 @@ function session_timeout(now, session, context, reason) -- luacheck: ignore 212/ end function handle_POST(event) - log("debug", "Handling new request %s: %s\n----------", tostring(event.request), tostring(event.request.body)); + log("debug", "Handling new request %s: %s\n----------", event.request, event.request.body); local request, response = event.request, event.response; response.on_destroy = on_destroy_request; @@ -224,7 +224,7 @@ local function bosh_reset_stream(session) session.notopen = true; end local stream_xmlns_attr = { xmlns = "urn:ietf:params:xml:ns:xmpp-streams" }; local function bosh_close_stream(session, reason) - (session.log or log)("info", "BOSH client disconnected: %s", tostring((reason and reason.condition or reason) or "session close")); + (session.log or log)("info", "BOSH client disconnected: %s", (reason and reason.condition or reason) or "session close"); local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", ["xmlns:stream"] = xmlns_streams }); @@ -249,7 +249,7 @@ local function bosh_close_stream(session, reason) close_reply = reason; end end - log("info", "Disconnecting client, <stream:error> is: %s", tostring(close_reply)); + log("info", "Disconnecting client, <stream:error> is: %s", close_reply); end local response_body = tostring(close_reply); @@ -275,7 +275,7 @@ function stream_callbacks.streamopened(context, attr) local to_host = nameprep(attr.to); local wait = tonumber(attr.wait); if not to_host then - log("debug", "BOSH client tried to connect to invalid host: %s", tostring(attr.to)); + log("debug", "BOSH client tried to connect to invalid host: %s", attr.to); report_bad_host(); local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", ["xmlns:stream"] = xmlns_streams, condition = "improper-addressing" }); @@ -283,7 +283,7 @@ function stream_callbacks.streamopened(context, attr) return; end if not rid or (not attr.wait or not wait or wait < 0 or wait % 1 ~= 0) then - log("debug", "BOSH client sent invalid rid or wait attributes: rid=%s, wait=%s", tostring(attr.rid), tostring(attr.wait)); + log("debug", "BOSH client sent invalid rid or wait attributes: rid=%s, wait=%s", attr.rid, attr.wait); local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", ["xmlns:stream"] = xmlns_streams, condition = "bad-request" }); response:send(tostring(close_reply)); @@ -329,7 +329,7 @@ function stream_callbacks.streamopened(context, attr) s.attr.xmlns = "jabber:client"; end s = filter("stanzas/out", s); - --log("debug", "Sending BOSH data: %s", tostring(s)); + --log("debug", "Sending BOSH data: %s", s); if not s then return true end t_insert(session.send_buffer, tostring(s)); @@ -432,7 +432,7 @@ function stream_callbacks.streamopened(context, attr) end end -local function handleerr(err) log("error", "Traceback[bosh]: %s", traceback(tostring(err), 2)); end +local function handleerr(err) log("error", "Traceback[bosh]: %s", traceback(err, 2)); end function runner_callbacks:error(err) -- luacheck: ignore 212/self return handleerr(err); diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index bfec1055..53af34f0 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -127,7 +127,7 @@ function stream_callbacks.error(session, error, data) session.log("debug", "Invalid opening stream header (%s)", (data:gsub("^([^\1]+)\1", "{%1}"))); session:close("invalid-namespace"); elseif error == "parse-error" then - (session.log or log)("debug", "Client XML parse error: %s", tostring(data)); + (session.log or log)("debug", "Client XML parse error: %s", data); session:close("not-well-formed"); elseif error == "stream-error" then local condition, text = "undefined-condition"; @@ -289,7 +289,7 @@ function listener.onconnect(conn) if data then local ok, err = stream:feed(data); if not ok then - log("debug", "Received invalid XML (%s) %d bytes: %q", tostring(err), #data, data:sub(1, 300)); + log("debug", "Received invalid XML (%s) %d bytes: %q", err, #data, data:sub(1, 300)); session:close("not-well-formed"); end end diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua index b1ffc81d..afcfc68c 100644 --- a/plugins/mod_component.lua +++ b/plugins/mod_component.lua @@ -167,11 +167,11 @@ local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; function stream_callbacks.error(session, error, data) if session.destroyed then return; end - module:log("warn", "Error processing component stream: %s", tostring(error)); + module:log("warn", "Error processing component stream: %s", error); if error == "no-stream" then session:close("invalid-namespace"); elseif error == "parse-error" then - session.log("warn", "External component %s XML parse error: %s", tostring(session.host), tostring(data)); + session.log("warn", "External component %s XML parse error: %s", session.host, data); session:close("not-well-formed"); elseif error == "stream-error" then local condition, text = "undefined-condition"; @@ -208,7 +208,7 @@ function stream_callbacks.streamclosed(session) session:close(); end -local function handleerr(err) log("error", "Traceback[component]: %s", traceback(tostring(err), 2)); end +local function handleerr(err) log("error", "Traceback[component]: %s", traceback(err, 2)); end function stream_callbacks.handlestanza(session, stanza) -- Namespaces are icky. if not stanza.attr.xmlns and stanza.name == "handshake" then @@ -268,10 +268,10 @@ local function session_close(session, reason) if reason.extra then stanza:add_child(reason.extra); end - module:log("info", "Disconnecting component, <stream:error> is: %s", tostring(stanza)); + module:log("info", "Disconnecting component, <stream:error> is: %s", stanza); session.send(stanza); elseif reason.name then -- a stanza - module:log("info", "Disconnecting component, <stream:error> is: %s", tostring(reason)); + module:log("info", "Disconnecting component, <stream:error> is: %s", reason); session.send(reason); end end @@ -312,7 +312,7 @@ function listener.onconnect(conn) function session.data(_, data) local ok, err = stream:feed(data); if ok then return; end - log("debug", "Received invalid XML (%s) %d bytes: %q", tostring(err), #data, data:sub(1, 300)); + log("debug", "Received invalid XML (%s) %d bytes: %q", err, #data, data:sub(1, 300)); session:close("not-well-formed"); end @@ -327,7 +327,7 @@ end function listener.ondisconnect(conn, err) local session = sessions[conn]; if session then - (session.log or log)("info", "component disconnected: %s (%s)", tostring(session.host), tostring(err)); + (session.log or log)("info", "component disconnected: %s (%s)", session.host, err); if session.host then module:context(session.host):fire_event("component-disconnected", { session = session, reason = err }); end diff --git a/plugins/mod_groups.lua b/plugins/mod_groups.lua index 646b7408..0c44f481 100644 --- a/plugins/mod_groups.lua +++ b/plugins/mod_groups.lua @@ -25,7 +25,7 @@ function inject_roster_contacts(event) local function import_jids_to_roster(group_name) for jid in pairs(groups[group_name]) do -- Add them to roster - --module:log("debug", "processing jid %s in group %s", tostring(jid), tostring(group_name)); + --module:log("debug", "processing jid %s in group %s", jid, group_name); if jid ~= bare_jid then if not roster[jid] then roster[jid] = {}; end roster[jid].subscription = "both"; @@ -99,7 +99,7 @@ function module.load() end members[false][#members[false]+1] = curr_group; -- Is a public group end - module:log("debug", "New group: %s", tostring(curr_group)); + module:log("debug", "New group: %s", curr_group); groups[curr_group] = groups[curr_group] or {}; else -- Add JID @@ -108,7 +108,7 @@ function module.load() local jid; jid = jid_prep(entryjid:match("%S+")); if jid then - module:log("debug", "New member of %s: %s", tostring(curr_group), tostring(jid)); + module:log("debug", "New member of %s: %s", curr_group, jid); groups[curr_group][jid] = name or false; members[jid] = members[jid] or {}; members[jid][#members[jid]+1] = curr_group; diff --git a/plugins/mod_limits.lua b/plugins/mod_limits.lua index 7ae8bb34..a1a3b2c0 100644 --- a/plugins/mod_limits.lua +++ b/plugins/mod_limits.lua @@ -32,7 +32,7 @@ local function parse_burst(burst, sess_type) end local n_burst = tonumber(burst); if not n_burst then - module:log("error", "Unable to parse burst for %s: %q, using default burst interval (%ds)", sess_type, tostring(burst), default_burst); + module:log("error", "Unable to parse burst for %s: %q, using default burst interval (%ds)", sess_type, burst, default_burst); end return n_burst or default_burst; end @@ -84,8 +84,13 @@ local function filter_hook(session) local session_type = session.type:match("^[^_]+"); local filter_set, opts = type_filters[session_type], limits[session_type]; if opts then - session.throttle = throttle.create(opts.bytes_per_second * opts.burst_seconds, opts.burst_seconds); - filters.add_filter(session, "bytes/in", filter_set.bytes_in, 1000); + if session.conn and session.conn.setlimit then + session.conn:setlimit(opts.bytes_per_second); + -- Currently no burst support + else + session.throttle = throttle.create(opts.bytes_per_second * opts.burst_seconds, opts.burst_seconds); + filters.add_filter(session, "bytes/in", filter_set.bytes_in, 1000); + end end end @@ -106,9 +111,14 @@ function module.add_host(module) local session_type = session.type:match("^[^_]+"); local jid = session.username .. "@" .. session.host; if unlimited_jids:contains(jid) then - local filter_set = type_filters[session_type]; - filters.remove_filter(session, "bytes/in", filter_set.bytes_in); - session.throttle = nil; + if session.conn and session.conn.setlimit then + session.conn:setlimit(0); + -- Currently no burst support + else + local filter_set = type_filters[session_type]; + filters.remove_filter(session, "bytes/in", filter_set.bytes_in); + session.throttle = nil; + end end end); end diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 4e0cf531..cfa92ff5 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -118,10 +118,12 @@ module:hook("iq-set/self/"..xmlns_mam..":query", function(event) qstart, qend = vstart, vend; end - module:log("debug", "Archive query, id %s with %s from %s until %s", - tostring(qid), qwith or "anyone", - qstart and timestamp(qstart) or "the dawn of time", - qend and timestamp(qend) or "now"); + module:log("debug", "Archive query by %s id=%s with=%s when=%s...%s", + origin.username, + qid or stanza.attr.id, + qwith or "*", + qstart and timestamp(qstart) or "", + qend and timestamp(qend) or ""); -- RSM stuff local qset = rsm.get(query); @@ -129,6 +131,9 @@ module:hook("iq-set/self/"..xmlns_mam..":query", function(event) local reverse = qset and qset.before or false; local before, after = qset and qset.before, qset and qset.after; if type(before) ~= "string" then before = nil; end + if qset then + module:log("debug", "Archive query id=%s rsm=%q", qid or stanza.attr.id, qset); + end -- Load all the data! local data, err = archive:find(origin.username, { @@ -141,6 +146,7 @@ module:hook("iq-set/self/"..xmlns_mam..":query", function(event) }); if not data then + module:log("debug", "Archive query id=%s failed: %s", qid or stanza.attr.id, err); if err == "item-not-found" then origin.send(st.error_reply(stanza, "modify", "item-not-found")); else @@ -194,13 +200,13 @@ module:hook("iq-set/self/"..xmlns_mam..":query", function(event) first, last = last, first; end - -- That's all folks! - module:log("debug", "Archive query %s completed", tostring(qid)); - origin.send(st.reply(stanza) :tag("fin", { xmlns = xmlns_mam, queryid = qid, complete = complete }) :add_child(rsm.generate { first = first, last = last, count = total })); + + -- That's all folks! + module:log("debug", "Archive query id=%s completed, %d items returned", qid or stanza.attr.id, complete and count or count - 1); return true; end); @@ -218,13 +224,13 @@ local function shall_store(user, who) end local prefs = get_prefs(user); local rule = prefs[who]; - module:log("debug", "%s's rule for %s is %s", user, who, tostring(rule)); + module:log("debug", "%s's rule for %s is %s", user, who, rule); if rule ~= nil then return rule; end -- Below could be done by a metatable local default = prefs[false]; - module:log("debug", "%s's default rule is %s", user, tostring(default)); + module:log("debug", "%s's default rule is %s", user, default); if default == "roster" then return has_in_roster(user, who); end diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index eb93a386..7fc9fabf 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -251,7 +251,7 @@ module:hook("iq-set/bare/"..xmlns_mam..":query", function(event) end -- That's all folks! - module:log("debug", "Archive query %s completed", tostring(qid)); + module:log("debug", "Archive query %s completed", qid); origin.send(st.reply(stanza) :tag("fin", { xmlns = xmlns_mam, queryid = qid, complete = complete }) @@ -291,7 +291,7 @@ module:hook("muc-get-history", function (event) local data, err = archive:find(jid_split(room_jid), query); if not data then - module:log("error", "Could not fetch history: %s", tostring(err)); + module:log("error", "Could not fetch history: %s", err); return end @@ -317,7 +317,7 @@ module:hook("muc-get-history", function (event) maxchars = maxchars - chars; end history[i], i = item, i+1; - -- module:log("debug", tostring(item)); + -- module:log("debug", item); end function event.next_stanza() i = i - 1; @@ -428,7 +428,9 @@ end module:add_feature(xmlns_mam); module:hook("muc-disco#info", function(event) - event.reply:tag("feature", {var=xmlns_mam}):up(); + if archiving_enabled(event.room) then + event.reply:tag("feature", {var=xmlns_mam}):up(); + end end); -- Cleanup diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 54f0451d..c0a85a9d 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -183,12 +183,12 @@ local function on_node_creation(event) end function get_pep_service(username) - module:log("debug", "get_pep_service(%q)", username); local user_bare = jid_join(username, host); local service = services[username]; if service then return service; end + module:log("debug", "Creating pubsub service for user %q", username); service = pubsub.new({ pep_username = username; node_defaults = { diff --git a/plugins/mod_pep_simple.lua b/plugins/mod_pep_simple.lua index f91e5448..11268ab7 100644 --- a/plugins/mod_pep_simple.lua +++ b/plugins/mod_pep_simple.lua @@ -230,13 +230,13 @@ module:hook("iq/bare/http://jabber.org/protocol/pubsub:pubsub", function(event) return true; else --invalid request session.send(st.error_reply(stanza, 'modify', 'bad-request')); - module:log("debug", "Invalid request: %s", tostring(payload)); + module:log("debug", "Invalid request: %s", payload); 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'})); - module:log("debug", "Unauthorized request: %s", tostring(payload)); + module:log("debug", "Unauthorized request: %s", payload); return true; end end diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua index 00833772..29c821e2 100644 --- a/plugins/mod_proxy65.lua +++ b/plugins/mod_proxy65.lua @@ -117,7 +117,7 @@ function module.add_host(module) if jid_compare(jid, acl) then allow = true; break; end end if allow then break; end - module:log("warn", "Denying use of proxy for %s", tostring(stanza.attr.from)); + module:log("warn", "Denying use of proxy for %s", stanza.attr.from); origin.send(st.error_reply(stanza, "auth", "forbidden")); return true; end diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua index 05f80365..faf08cb2 100644 --- a/plugins/mod_pubsub/mod_pubsub.lua +++ b/plugins/mod_pubsub/mod_pubsub.lua @@ -82,7 +82,6 @@ function simple_broadcast(kind, node, jids, item, actor, node_obj) end local summary; - -- Compose a sensible textual representation of at least Atom payloads if item and item.tags[1] then local payload = item.tags[1]; summary = module:fire_event("pubsub-summary/"..payload.attr.xmlns, { @@ -116,6 +115,7 @@ function is_item_stanza(item) return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item"; end +-- Compose a textual representation of Atom payloads module:hook("pubsub-summary/http://www.w3.org/2005/Atom", function (event) local payload = event.payload; local title = payload:get_child_text("title"); diff --git a/plugins/mod_pubsub/pubsub.lib.lua b/plugins/mod_pubsub/pubsub.lib.lua index 50ef7ddf..d59e3d85 100644 --- a/plugins/mod_pubsub/pubsub.lib.lua +++ b/plugins/mod_pubsub/pubsub.lib.lua @@ -185,6 +185,14 @@ local node_metadata_form = dataform { type = "text-single"; name = "pubsub#type"; }; + { + type = "text-single"; + name = "pubsub#access_model"; + }; + { + type = "text-single"; + name = "pubsub#publish_model"; + }; }; local service_method_feature_map = { @@ -258,6 +266,8 @@ function _M.handle_disco_info_node(event, service) ["pubsub#title"] = node_obj.config.title; ["pubsub#description"] = node_obj.config.description; ["pubsub#type"] = node_obj.config.payload_type; + ["pubsub#access_model"] = node_obj.config.access_model; + ["pubsub#publish_model"] = node_obj.config.publish_model; }, "result")); end end @@ -318,14 +328,9 @@ function handlers.get_items(origin, stanza, items, service) for _, id in ipairs(results) do data:add_child(results[id]); end - local reply; - if data then - reply = st.reply(stanza) - :tag("pubsub", { xmlns = xmlns_pubsub }) - :add_child(data); - else - reply = pubsub_error_reply(stanza, "item-not-found"); - end + local reply = st.reply(stanza) + :tag("pubsub", { xmlns = xmlns_pubsub }) + :add_child(data); origin.send(reply); return true; end diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index f0fdc5fb..ea19f7ad 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -27,8 +27,8 @@ local s2s_destroy_session = require "core.s2smanager".destroy_session; local uuid_gen = require "util.uuid".generate; local fire_global_event = prosody.events.fire_event; local runner = require "util.async".runner; - -local s2sout = module:require("s2sout"); +local connect = require "net.connect".connect; +local service = require "net.resolvers.service"; local connect_timeout = module:get_option_number("s2s_timeout", 90); local stream_close_timeout = module:get_option_number("s2s_close_timeout", 5); @@ -45,6 +45,8 @@ local sessions = module:shared("sessions"); local runner_callbacks = {}; +local listener = {}; + local log = module._log; module:hook("stats-update", function () @@ -77,12 +79,19 @@ local function bounce_sendq(session, reason) (session.log or log)("error", "Attempting to close the dummy origin of s2s error replies, please report this! Traceback: %s", traceback()); end; }; + -- FIXME Allow for more specific error conditions + -- TODO use util.error ? + local error_type = "cancel"; + local condition = "remote-server-not-found"; + if session.had_stream then -- set when a stream is opened by the remote + error_type, condition = "wait", "remote-server-timeout"; + end for i, data in ipairs(sendq) do local reply = data[2]; if reply and not(reply.attr.xmlns) and bouncy_stanzas[reply.name] then reply.attr.type = "error"; - reply:tag("error", {type = "cancel", by = session.from_host}) - :tag("remote-server-not-found", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up(); + reply:tag("error", {type = error_type, by = session.from_host}) + :tag(condition, {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up(); if reason then reply:tag("text", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}) :text("Server-to-server connection failed: "..reason):up(); @@ -127,7 +136,7 @@ function route_to_existing_session(event) elseif host.type == "local" or host.type == "component" then log("error", "Trying to send a stanza to ourselves??") log("error", "Traceback: %s", traceback()); - log("error", "Stanza: %s", tostring(stanza)); + log("error", "Stanza: %s", stanza); return false; else -- FIXME @@ -147,17 +156,13 @@ function route_to_new_session(event) local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza; log("debug", "opening a new outgoing connection for this stanza"); local host_session = s2s_new_outgoing(from_host, to_host); + host_session.version = 1; -- Store in buffer host_session.bounce_sendq = bounce_sendq; host_session.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} }; - log("debug", "stanza [%s] queued until connection complete", tostring(stanza.name)); - s2sout.initiate_connection(host_session); - if (not host_session.connecting) and (not host_session.conn) then - log("warn", "Connection to %s failed already, destroying session...", to_host); - s2s_destroy_session(host_session, "Connection failed"); - return false; - end + log("debug", "stanza [%s] queued until connection complete", stanza.name); + connect(service.new(to_host, "xmpp-server", "tcp", { default_port = 5269 }), listener, nil, { session = host_session }); return true; end @@ -301,6 +306,7 @@ end function stream_callbacks._streamopened(session, attr) session.version = tonumber(attr.version) or 0; + session.had_stream = true; -- Had a stream opened at least once -- TODO: Rename session.secure to session.encrypted if session.secure == false then @@ -471,8 +477,6 @@ function stream_callbacks.error(session, error, data) end end -local listener = {}; - --- Session methods local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; local function session_close(session, reason, remote_reason) @@ -595,7 +599,7 @@ local function initialize_session(session) if data then local ok, err = stream:feed(data); if ok then return; end - log("debug", "Received invalid XML (%s) %d bytes: %q", tostring(err), #data, data:sub(1, 300)); + log("debug", "Received invalid XML (%s) %d bytes: %q", err, #data, data:sub(1, 300)); session:close("not-well-formed"); end end @@ -671,11 +675,16 @@ function listener.ondisconnect(conn, err) local session = sessions[conn]; if session then sessions[conn] = nil; + (session.log or log)("debug", "s2s disconnected: %s->%s (%s)", session.from_host, session.to_host, err or "connection closed"); + s2s_destroy_session(session, err); + end +end + +function listener.onfail(data, err) + local session = data and data.session; + if session then if err and session.direction == "outgoing" and session.notopen then (session.log or log)("debug", "s2s connection attempt failed: %s", err); - if s2sout.attempt_connection(session, err) then - return; -- Session lives for now - end end (session.log or log)("debug", "s2s disconnected: %s->%s (%s)", session.from_host, session.to_host, err or "connection closed"); s2s_destroy_session(session, err); @@ -699,6 +708,15 @@ function listener.ondetach(conn) sessions[conn] = nil; end +function listener.onattach(conn, data) + local session = data and data.session; + if session then + session.conn = conn; + sessions[conn] = session; + initialize_session(session); + end +end + function check_auth_policy(event) local host, session = event.host, event.session; local must_secure = secure_auth; @@ -722,8 +740,6 @@ end module:hook("s2s-check-certificate", check_auth_policy, -1); -s2sout.set_listener(listener); - module:hook("server-stopping", function(event) local reason = event.reason; for _, session in pairs(sessions) do diff --git a/plugins/mod_s2s/s2sout.lib.lua b/plugins/mod_s2s/s2sout.lib.lua deleted file mode 100644 index 34e322d2..00000000 --- a/plugins/mod_s2s/s2sout.lib.lua +++ /dev/null @@ -1,349 +0,0 @@ --- Prosody IM --- Copyright (C) 2008-2010 Matthew Wild --- Copyright (C) 2008-2010 Waqas Hussain --- --- This project is MIT/X11 licensed. Please see the --- COPYING file in the source package for more information. --- - ---- Module containing all the logic for connecting to a remote server - --- luacheck: ignore 432/err - -local portmanager = require "core.portmanager"; -local wrapclient = require "net.server".wrapclient; -local initialize_filters = require "util.filters".initialize; -local idna_to_ascii = require "util.encodings".idna.to_ascii; -local new_ip = require "util.ip".new_ip; -local rfc6724_dest = require "util.rfc6724".destination; -local socket = require "socket"; -local adns = require "net.adns"; -local t_insert, t_sort, ipairs = table.insert, table.sort, ipairs; -local local_addresses = require "util.net".local_addresses; - -local s2s_destroy_session = require "core.s2smanager".destroy_session; - -local default_mode = module:get_option("network_default_read_size", 4096); - -local log = module._log; - -local sources = {}; -local has_ipv4, has_ipv6; - -local dns_timeout = module:get_option_number("dns_timeout", 15); -local resolvers = module:get_option_set("s2s_dns_resolvers") - -local s2sout = {}; - -local s2s_listener; - - -function s2sout.set_listener(listener) - s2s_listener = listener; -end - -local function compare_srv_priorities(a,b) - return a.priority < b.priority or (a.priority == b.priority and a.weight > b.weight); -end - -function s2sout.initiate_connection(host_session) - local log = host_session.log or log; - - initialize_filters(host_session); - host_session.version = 1; - - host_session.resolver = adns.resolver(); - host_session.resolver._resolver:settimeout(dns_timeout); - if resolvers then - for resolver in resolvers do - host_session.resolver._resolver:addnameserver(resolver); - end - end - - -- Kick the connection attempting machine into life - if not s2sout.attempt_connection(host_session) then - -- Intentionally not returning here, the - -- session is needed, connected or not - s2s_destroy_session(host_session); - end - - if not host_session.sends2s then - -- A sends2s which buffers data (until the stream is opened) - -- note that data in this buffer will be sent before the stream is authed - -- and will not be ack'd in any way, successful or otherwise - local buffer; - function host_session.sends2s(data) - if not buffer then - buffer = {}; - host_session.send_buffer = buffer; - end - log("debug", "Buffering data on unconnected s2sout to %s", host_session.to_host); - buffer[#buffer+1] = data; - log("debug", "Buffered item %d: %s", #buffer, data); - end - end -end - -function s2sout.attempt_connection(host_session, err) - local to_host = host_session.to_host; - local connect_host, connect_port = to_host and idna_to_ascii(to_host), 5269; - local log = host_session.log or log; - - if not connect_host then - return false; - end - - if not err then -- This is our first attempt - log("debug", "First attempt to connect to %s, starting with SRV lookup...", to_host); - host_session.connecting = true; - host_session.resolver:lookup(function (answer) - local srv_hosts = { answer = answer }; - host_session.srv_hosts = srv_hosts; - host_session.srv_choice = 0; - host_session.connecting = nil; - if answer and #answer > 0 then - log("debug", "%s has SRV records, handling...", to_host); - for _, record in ipairs(answer) do - t_insert(srv_hosts, record.srv); - end - if #srv_hosts == 1 and srv_hosts[1].target == "." then - log("debug", "%s does not provide a XMPP service", to_host); - s2s_destroy_session(host_session, err); -- Nothing to see here - return; - end - t_sort(srv_hosts, compare_srv_priorities); - - local srv_choice = srv_hosts[1]; - host_session.srv_choice = 1; - if srv_choice then - connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; - log("debug", "Best record found, will connect to %s:%d", connect_host, connect_port); - end - else - log("debug", "%s has no SRV records, falling back to A/AAAA", to_host); - end - -- Try with SRV, or just the plain hostname if no SRV - local ok, err = s2sout.try_connect(host_session, connect_host, connect_port); - if not ok then - if not s2sout.attempt_connection(host_session, err) then - -- No more attempts will be made - s2s_destroy_session(host_session, err); - end - end - end, "_xmpp-server._tcp."..connect_host..".", "SRV"); - - return true; -- Attempt in progress - elseif host_session.ip_hosts then - return s2sout.try_connect(host_session, connect_host, connect_port, err); - elseif host_session.srv_hosts and #host_session.srv_hosts > host_session.srv_choice then -- Not our first attempt, and we also have SRV - host_session.srv_choice = host_session.srv_choice + 1; - local srv_choice = host_session.srv_hosts[host_session.srv_choice]; - connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; - host_session.log("info", "Connection failed (%s). Attempt #%d: This time to %s:%d", err, host_session.srv_choice, connect_host, connect_port); - else - host_session.log("info", "Failed in all attempts to connect to %s", host_session.to_host); - -- We're out of options - return false; - end - - if not (connect_host and connect_port) then - -- Likely we couldn't resolve DNS - log("warn", "Hmm, we're without a host (%s) and port (%s) to connect to for %s, giving up :(", connect_host, connect_port, to_host); - return false; - end - - return s2sout.try_connect(host_session, connect_host, connect_port); -end - -function s2sout.try_next_ip(host_session) - host_session.connecting = nil; - host_session.ip_choice = host_session.ip_choice + 1; - local ip = host_session.ip_hosts[host_session.ip_choice]; - local ok, err= s2sout.make_connect(host_session, ip.ip, ip.port); - if not ok then - if not s2sout.attempt_connection(host_session, err or "closed") then - err = err and (": "..err) or ""; - s2s_destroy_session(host_session, "Connection failed"..err); - end - end -end - -function s2sout.try_connect(host_session, connect_host, connect_port, err) - host_session.connecting = true; - local log = host_session.log or log; - - if not err then - local IPs = {}; - host_session.ip_hosts = IPs; - -- luacheck: ignore 231/handle4 231/handle6 - local handle4, handle6; - local have_other_result = not(has_ipv4) or not(has_ipv6) or false; - - if has_ipv4 then - handle4 = host_session.resolver:lookup(function (reply, err) - handle4 = nil; - - if reply and reply[#reply] and reply[#reply].a then - for _, ip in ipairs(reply) do - log("debug", "DNS reply for %s gives us %s", connect_host, ip.a); - IPs[#IPs+1] = new_ip(ip.a, "IPv4"); - end - elseif err then - log("debug", "Error in DNS lookup: %s", err); - end - - if have_other_result then - if #IPs > 0 then - rfc6724_dest(host_session.ip_hosts, sources); - for i = 1, #IPs do - IPs[i] = {ip = IPs[i], port = connect_port}; - end - host_session.ip_choice = 0; - s2sout.try_next_ip(host_session); - else - log("debug", "DNS lookup failed to get a response for %s", connect_host); - host_session.ip_hosts = nil; - if not s2sout.attempt_connection(host_session, "name resolution failed") then -- Retry if we can - log("debug", "No other records to try for %s - destroying", host_session.to_host); - err = err and (": "..err) or ""; - s2s_destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't - end - end - else - have_other_result = true; - end - end, connect_host, "A", "IN"); - else - have_other_result = true; - end - - if has_ipv6 then - handle6 = host_session.resolver:lookup(function (reply, err) - handle6 = nil; - - if reply and reply[#reply] and reply[#reply].aaaa then - for _, ip in ipairs(reply) do - log("debug", "DNS reply for %s gives us %s", connect_host, ip.aaaa); - IPs[#IPs+1] = new_ip(ip.aaaa, "IPv6"); - end - elseif err then - log("debug", "Error in DNS lookup: %s", err); - end - - if have_other_result then - if #IPs > 0 then - rfc6724_dest(host_session.ip_hosts, sources); - for i = 1, #IPs do - IPs[i] = {ip = IPs[i], port = connect_port}; - end - host_session.ip_choice = 0; - s2sout.try_next_ip(host_session); - else - log("debug", "DNS lookup failed to get a response for %s", connect_host); - host_session.ip_hosts = nil; - if not s2sout.attempt_connection(host_session, "name resolution failed") then -- Retry if we can - log("debug", "No other records to try for %s - destroying", host_session.to_host); - err = err and (": "..err) or ""; - s2s_destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't - end - end - else - have_other_result = true; - end - end, connect_host, "AAAA", "IN"); - else - have_other_result = true; - end - return true; - elseif host_session.ip_hosts and #host_session.ip_hosts > host_session.ip_choice then -- Not our first attempt, and we also have IPs left to try - s2sout.try_next_ip(host_session); - else - log("debug", "Out of IP addresses, trying next SRV record (if any)"); - host_session.ip_hosts = nil; - if not s2sout.attempt_connection(host_session, "out of IP addresses") then -- Retry if we can - log("debug", "No other records to try for %s - destroying", host_session.to_host); - err = err and (": "..err) or ""; - s2s_destroy_session(host_session, "Connecting failed"..err); -- End of the line, we can't - return false; - end - end - - return true; -end - -function s2sout.make_connect(host_session, connect_host, connect_port) - local log = host_session.log or log; - log("debug", "Beginning new connection attempt to %s ([%s]:%d)", host_session.to_host, connect_host.addr, connect_port); - - -- Reset secure flag in case this is another - -- connection attempt after a failed STARTTLS - host_session.secure = nil; - host_session.encrypted = nil; - - local conn, handler; - local proto = connect_host.proto; - if proto == "IPv4" then - conn, handler = socket.tcp(); - elseif proto == "IPv6" and socket.tcp6 then - conn, handler = socket.tcp6(); - else - handler = "Unsupported protocol: "..tostring(proto); - end - - if not conn then - log("warn", "Failed to create outgoing connection, system error: %s", handler); - return false, handler; - end - - conn:settimeout(0); - local success, err = conn:connect(connect_host.addr, connect_port); - if not success and err ~= "timeout" then - log("warn", "s2s connect() to %s (%s:%d) failed: %s", host_session.to_host, connect_host.addr, connect_port, err); - return false, err; - end - - conn = wrapclient(conn, connect_host.addr, connect_port, s2s_listener, default_mode); - host_session.conn = conn; - - -- Register this outgoing connection so that xmppserver_listener knows about it - -- otherwise it will assume it is a new incoming connection - s2s_listener.register_outgoing(conn, host_session); - - log("debug", "Connection attempt in progress..."); - return true; -end - -module:hook_global("service-added", function (event) - if event.name ~= "s2s" then return end - - local s2s_sources = portmanager.get_active_services():get("s2s"); - if not s2s_sources then - module:log_status("warn", "s2s not listening on any ports, outgoing connections may fail"); - return; - end - for source, _ in pairs(s2s_sources) do - if source == "*" or source == "0.0.0.0" then - for _, addr in ipairs(local_addresses("ipv4", true)) do - sources[#sources + 1] = new_ip(addr, "IPv4"); - end - elseif source == "::" then - for _, addr in ipairs(local_addresses("ipv6", true)) do - sources[#sources + 1] = new_ip(addr, "IPv6"); - end - else - sources[#sources + 1] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); - end - end - for i = 1,#sources do - if sources[i].proto == "IPv6" then - has_ipv6 = true; - elseif sources[i].proto == "IPv4" then - has_ipv4 = true; - end - end - if not (has_ipv4 or has_ipv6) then - module:log("warn", "No local IPv4 or IPv6 addresses detected, outgoing connections may fail"); - end -end); - -return s2sout; diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 3145cf9b..89313de1 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -14,7 +14,6 @@ local sm_make_authenticated = require "core.sessionmanager".make_authenticated; local base64 = require "util.encodings".base64; local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; -local tostring = tostring; local secure_auth_only = module:get_option_boolean("c2s_require_encryption", module:get_option_boolean("require_encryption", false)); local allow_unencrypted_plain_auth = module:get_option_boolean("allow_unencrypted_plain_auth", false) @@ -77,7 +76,7 @@ local function sasl_process_cdata(session, stanza) local status, ret, err_msg = session.sasl_handler:process(text); status, ret, err_msg = handle_status(session, status, ret, err_msg); local s = build_reply(status, ret, err_msg); - log("debug", "sasl reply: %s", tostring(s)); + log("debug", "sasl reply: %s", s); session.send(s); return true; end diff --git a/plugins/mod_stanza_debug.lua b/plugins/mod_stanza_debug.lua index 6dedb6f7..af98670c 100644 --- a/plugins/mod_stanza_debug.lua +++ b/plugins/mod_stanza_debug.lua @@ -1,18 +1,17 @@ module:set_global(); -local tostring = tostring; local filters = require "util.filters"; local function log_send(t, session) if t and t ~= "" and t ~= " " then - session.log("debug", "SEND: %s", tostring(t)); + session.log("debug", "SEND: %s", t); end return t; end local function log_recv(t, session) if t and t ~= "" and t ~= " " then - session.log("debug", "RECV: %s", tostring(t)); + session.log("debug", "RECV: %s", t); end return t; end diff --git a/plugins/mod_vcard_legacy.lua b/plugins/mod_vcard_legacy.lua index ab2c4490..89ef1a4f 100644 --- a/plugins/mod_vcard_legacy.lua +++ b/plugins/mod_vcard_legacy.lua @@ -105,6 +105,15 @@ module:hook("iq-get/bare/vcard-temp:vCard", function (event) vcard_temp:tag("WORK"):up(); end vcard_temp:up(); + elseif tag.name == "impp" then + local uri = tag:get_child_text("uri"); + if uri and uri:sub(1, 5) == "xmpp:" then + vcard_temp:text_tag("JABBERID", uri:sub(6)) + end + elseif tag.name == "org" then + vcard_temp:tag("ORG") + :text_tag("ORGNAME", tag:get_child_text("text")) + :up(); end end end @@ -216,6 +225,10 @@ function vcard_to_pep(vcard_temp) vcard4:text_tag("text", "work"); end vcard4:up():up():up(); + elseif tag.name == "JABBERID" then + vcard4:tag("impp") + :text_tag("uri", "xmpp:" .. tag:get_text()) + :up(); elseif tag.name == "PHOTO" then local avatar_type = tag:get_child_text("TYPE"); local avatar_payload = tag:get_child_text("BINVAL"); diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 008f6823..c94ea84a 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -80,7 +80,7 @@ local function session_close(session, reason) stream_error = reason; end end - log("debug", "Disconnecting client, <stream:error> is: %s", tostring(stream_error)); + log("debug", "Disconnecting client, <stream:error> is: %s", stream_error); session.send(stream_error); end @@ -272,6 +272,7 @@ function handle_request(event) end); add_filter(session, "stanzas/out", function(stanza) + stanza = st.clone(stanza); local attr = stanza.attr; attr.xmlns = attr.xmlns or xmlns_client; if stanza.name:find("^stream:") then diff --git a/plugins/muc/language.lib.lua b/plugins/muc/language.lib.lua index ee80806b..2ee2ba0f 100644 --- a/plugins/muc/language.lib.lua +++ b/plugins/muc/language.lib.lua @@ -32,6 +32,7 @@ local function add_form_option(event) label = "Language tag for room (e.g. 'en', 'de', 'fr' etc.)"; type = "text-single"; desc = "Indicate the primary language spoken in this room"; + datatype = "xs:language"; value = get_language(event.room) or ""; }); end @@ -246,7 +246,15 @@ function commands.start(arg) end --luacheck: ignore 411/ret - local ok, ret = prosodyctl.start(prosody.paths.source, arg[-1]); + local lua; + do + local i = 0; + repeat + i = i - 1; + until arg[i-1] == nil + lua = arg[i]; + end + local ok, ret = prosodyctl.start(prosody.paths.source, lua); if ok then local daemonize = configmanager.get("*", "daemonize"); if daemonize == nil then diff --git a/spec/util_array_spec.lua b/spec/util_array_spec.lua new file mode 100644 index 00000000..acd2e882 --- /dev/null +++ b/spec/util_array_spec.lua @@ -0,0 +1,154 @@ +local array = require "util.array"; +describe("util.array", function () + describe("creation", function () + describe("from tablle", function () + it("works", function () + local a = array({"a", "b", "c"}); + assert.same({"a", "b", "c"}, a); + end); + end); + + describe("from iterator", function () + it("works", function () + -- collects the first value, ie the keys + local a = array(ipairs({true, true, true})); + assert.same({1, 2, 3}, a); + end); + end); + + describe("collect", function () + it("works", function () + -- collects the first value, ie the keys + local a = array.collect(ipairs({true, true, true})); + assert.same({1, 2, 3}, a); + end); + end); + + end); + + describe("metatable", function () + describe("operator", function () + describe("addition", function () + it("works", function () + local a = array({ "a", "b" }); + local b = array({ "c", "d" }); + assert.same({"a", "b", "c", "d"}, a + b); + end); + end); + + describe("equality", function () + it("works", function () + local a1 = array({ "a", "b" }); + local a2 = array({ "a", "b" }); + local b = array({ "c", "d" }); + assert.truthy(a1 == a2); + assert.falsy(a1 == b); + end); + end); + + describe("division", function () + it("works", function () + local a = array({ "a", "b", "c" }); + local b = a / function (i) if i ~= "b" then return i .. "x" end end; + assert.same({ "ax", "cx" }, b); + end); + end); + + end); + end); + + describe("methods", function () + describe("map", function () + it("works", function () + local a = array({ "a", "b", "c" }); + local b = a:map(string.upper); + assert.same({ "A", "B", "C" }, b); + end); + end); + + describe("filter", function () + it("works", function () + local a = array({ "a", "b", "c" }); + a:filter(function (i) return i ~= "b" end); + assert.same({ "a", "c" }, a); + end); + end); + + describe("sort", function () + it("works", function () + local a = array({ 5, 4, 3, 1, 2, }); + a:sort(); + assert.same({ 1, 2, 3, 4, 5, }, a); + end); + end); + + describe("unique", function () + it("works", function () + local a = array({ "a", "b", "c", "c", "a", "b" }); + a:unique(); + assert.same({ "a", "b", "c" }, a); + end); + end); + + describe("pluck", function () + it("works", function () + local a = array({ { a = 1, b = -1 }, { a = 2, b = -2 }, }); + a:pluck("a"); + assert.same({ 1, 2 }, a); + end); + end); + + + describe("reverse", function () + it("works", function () + local a = array({ "a", "b", "c" }); + a:reverse(); + assert.same({ "c", "b", "a" }, a); + end); + end); + + -- TODO :shuffle + + describe("append", function () + it("works", function () + local a = array({ "a", "b", "c" }); + a:append(array({ "d", "e", })); + assert.same({ "a", "b", "c", "d", "e" }, a); + end); + end); + + describe("push", function () + it("works", function () + local a = array({ "a", "b", "c" }); + a:push("d"):push("e"); + assert.same({ "a", "b", "c", "d", "e" }, a); + end); + end); + + describe("pop", function () + it("works", function () + local a = array({ "a", "b", "c" }); + assert.equal("c", a:pop()); + assert.same({ "a", "b", }, a); + end); + end); + + describe("concat", function () + it("works", function () + local a = array({ "a", "b", "c" }); + assert.equal("a,b,c", a:concat(",")); + end); + end); + + describe("length", function () + it("works", function () + local a = array({ "a", "b", "c" }); + assert.equal(3, a:length()); + end); + end); + + end); + + -- TODO The various array.foo(array ina, array outa) functions +end); + diff --git a/spec/util_error_spec.lua b/spec/util_error_spec.lua new file mode 100644 index 00000000..d8534c2b --- /dev/null +++ b/spec/util_error_spec.lua @@ -0,0 +1,68 @@ +local errors = require "util.error" + +describe("util.error", function () + describe("new()", function () + it("works", function () + local err = errors.new("bork", "bork bork"); + assert.not_nil(err); + assert.equal("cancel", err.type); + assert.equal("undefined-condition", err.condition); + assert.same("bork bork", err.context); + end); + + describe("templates", function () + it("works", function () + local templates = { + ["fail"] = { + type = "wait", + condition = "internal-server-error", + }; + }; + local err = errors.new("fail", { traceback = "in some file, somewhere" }, templates); + assert.equal("wait", err.type); + assert.equal("internal-server-error", err.condition); + assert.same({ traceback = "in some file, somewhere" }, err.context); + end); + end); + + end); + + describe("is_err()", function () + it("works", function () + assert.truthy(errors.is_err(errors.new())); + assert.falsy(errors.is_err("not an error")); + end); + end); + + describe("coerce", function () + it("works", function () + local ok, err = errors.coerce(nil, "it dun goofed"); + assert.is_nil(ok); + assert.truthy(errors.is_err(err)) + end); + end); + + describe("from_stanza", function () + it("works", function () + local st = require "util.stanza"; + local m = st.message({ type = "chat" }); + local e = st.error_reply(m, "modify", "bad-request"); + local err = errors.from_stanza(e); + assert.truthy(errors.is_err(err)); + assert.equal("modify", err.type); + assert.equal("bad-request", err.condition); + assert.equal(e, err.context.stanza); + end); + end); + + describe("__tostring", function () + it("doesn't throw", function () + assert.has_no.errors(function () + -- See 6f317e51544d + tostring(errors.new()); + end); + end); + end); + +end); + diff --git a/util-src/poll.c b/util-src/poll.c index 1e7b6da3..21cb9581 100644 --- a/util-src/poll.c +++ b/util-src/poll.c @@ -172,6 +172,7 @@ static int Lset(lua_State *L) { lua_pushnil(L); lua_pushstring(L, strerror(ENOENT)); lua_pushinteger(L, ENOENT); + return 3; } if(!lua_isnoneornil(L, 3)) { @@ -229,6 +230,7 @@ static int Ldel(lua_State *L) { lua_pushnil(L); lua_pushstring(L, strerror(ENOENT)); lua_pushinteger(L, ENOENT); + return 3; } FD_CLR(fd, &state->wantread); diff --git a/util/error.lua b/util/error.lua index 344dd274..23551fe2 100644 --- a/util/error.lua +++ b/util/error.lua @@ -1,7 +1,7 @@ local error_mt = { __name = "error" }; function error_mt:__tostring() - return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text); + return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text or ""); end local function is_err(e) diff --git a/util/serialization.lua b/util/serialization.lua index 60e341cf..d70e92ba 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -267,10 +267,15 @@ local function deserialize(str) return ret; end +local default = new(); return { new = new; serialize = function (x, opt) - return new(opt)(x); + if opt == nil then + return default(x); + else + return new(opt)(x); + end end; deserialize = deserialize; }; diff --git a/util/session.lua b/util/session.lua index b9c6bec7..25b22faf 100644 --- a/util/session.lua +++ b/util/session.lua @@ -31,7 +31,7 @@ local function set_send(session) local conn = session.conn; if not conn then function session.send(data) - session.log("debug", "Discarding data sent to unconnected session: %s", tostring(data)); + session.log("debug", "Discarding data sent to unconnected session: %s", data); return false; end return session; @@ -47,7 +47,7 @@ local function set_send(session) if t then local ret, err = w(conn, t); if not ret then - session.log("debug", "Error writing to connection: %s", tostring(err)); + session.log("debug", "Error writing to connection: %s", err); return false, err; end end diff --git a/util/sql.lua b/util/sql.lua index 00c7b57f..4406d7ff 100644 --- a/util/sql.lua +++ b/util/sql.lua @@ -201,18 +201,18 @@ function engine:_transaction(func, ...) if not ok then return ok, err; end end --assert(not self.__transaction, "Recursive transactions not allowed"); - log("debug", "SQL transaction begin [%s]", tostring(func)); + log("debug", "SQL transaction begin [%s]", func); self.__transaction = true; local success, a, b, c = xpcall(func, handleerr, ...); self.__transaction = nil; if success then - log("debug", "SQL transaction success [%s]", tostring(func)); + log("debug", "SQL transaction success [%s]", func); local ok, err = self.conn:commit(); -- LuaDBI doesn't actually return an error message here, just a boolean if not ok then return ok, err or "commit failed"; end return success, a, b, c; else - log("debug", "SQL transaction failure [%s]: %s", tostring(func), a.err); + log("debug", "SQL transaction failure [%s]: %s", func, a.err); if self.conn then self.conn:rollback(); end return success, a.err; end @@ -224,7 +224,7 @@ function engine:transaction(...) if not conn or not conn:ping() then log("debug", "Database connection was closed. Will reconnect and retry."); self.conn = nil; - log("debug", "Retrying SQL transaction [%s]", tostring((...))); + log("debug", "Retrying SQL transaction [%s]", (...)); ok, ret = self:_transaction(...); log("debug", "SQL transaction retry %s", ok and "succeeded" or "failed"); else diff --git a/util/stanza.lua b/util/stanza.lua index 7fe5c7ae..55c38c73 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -98,7 +98,7 @@ function stanza_mt:query(xmlns) end function stanza_mt:body(text, attr) - return self:tag("body", attr):text(text); + return self:text_tag("body", text, attr); end function stanza_mt:text_tag(name, text, attr, namespaces) @@ -417,7 +417,7 @@ local function message(attr, body) if not body then return new_stanza("message", attr); else - return new_stanza("message", attr):tag("body"):text(body):up(); + return new_stanza("message", attr):text_tag("body", body); end end local function iq(attr) @@ -449,7 +449,7 @@ local function error_reply(orig, error_type, condition, error_message) t.attr.type = "error"; t:tag("error", {type = error_type}) --COMPAT: Some day xmlns:stanzas goes here :tag(condition, xmpp_stanzas_attr):up(); - if error_message then t:tag("text", xmpp_stanzas_attr):text(error_message):up(); end + if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end return t; -- stanza ready for adding app-specific errors end diff --git a/util/startup.lua b/util/startup.lua index 02e5f012..580410c9 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -257,9 +257,9 @@ function startup.add_global_prosody_functions() local ok, level, err = config.load(prosody.config_file); if not ok then if level == "parser" then - log("error", "There was an error parsing the configuration file: %s", tostring(err)); + log("error", "There was an error parsing the configuration file: %s", err); elseif level == "file" then - log("error", "Couldn't read the config file when trying to reload: %s", tostring(err)); + log("error", "Couldn't read the config file when trying to reload: %s", err); end else prosody.events.fire_event("config-reloaded", { diff --git a/util/xmppstream.lua b/util/xmppstream.lua index 58cbd18e..6aa1def3 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -64,6 +64,8 @@ local function new_sax_handlers(session, stream_callbacks, cb_handleprogress) local stream_default_ns = stream_callbacks.default_ns; + local stream_lang = "en"; + local stack = {}; local chardata, stanza = {}; local stanza_size = 0; @@ -101,6 +103,7 @@ local function new_sax_handlers(session, stream_callbacks, cb_handleprogress) if session.notopen then if tagname == stream_tag then non_streamns_depth = 0; + stream_lang = attr["xml:lang"] or stream_lang; if cb_streamopened then if lxp_supports_bytecount then cb_handleprogress(stanza_size); @@ -178,6 +181,9 @@ local function new_sax_handlers(session, stream_callbacks, cb_handleprogress) cb_handleprogress(stanza_size); end stanza_size = 0; + if stanza.attr["xml:lang"] == nil then + stanza.attr["xml:lang"] = stream_lang; + end if tagname ~= stream_error_tag then cb_handlestanza(session, stanza); else |