From f0cd412842b1f7ba7ed7355ee0d82548d47877d0 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Sat, 22 Oct 2011 17:51:53 +0200 Subject: util.ip: New module containing IP related functions --- util/ip.lua | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 util/ip.lua diff --git a/util/ip.lua b/util/ip.lua new file mode 100644 index 00000000..5e2f46bb --- /dev/null +++ b/util/ip.lua @@ -0,0 +1,176 @@ +-- Prosody IM +-- Copyright (C) 2008-2011 Florian Zeitz +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local ip_methods = {}; +local ip_mt = { __index = function (ip, key) return (ip_methods[key])(ip); end, + __tostring = function (ip) return ip.addr; end, + __eq = function (ipA, ipB) return ipA.addr == ipB.addr; end}; +local hex2bits = { ["0"] = "0000", ["1"] = "0001", ["2"] = "0010", ["3"] = "0011", ["4"] = "0100", ["5"] = "0101", ["6"] = "0110", ["7"] = "0111", ["8"] = "1000", ["9"] = "1001", ["A"] = "1010", ["B"] = "1011", ["C"] = "1100", ["D"] = "1101", ["E"] = "1110", ["F"] = "1111" }; + +local function new_ip(ipStr, proto) + if proto ~= "IPv4" and proto ~= "IPv6" then + return nil, "invalid protocol"; + end + + return setmetatable({ addr = ipStr, proto = proto }, ip_mt); +end + +local function toBits(ip) + local result = ""; + local fields = {}; + if ip.proto == "IPv4" then + ip = ip.toV4mapped; + end + ip = (ip.addr):upper(); + ip:gsub("([^:]*):?", function (c) fields[#fields + 1] = c end); + if not ip:match(":$") then fields[#fields] = nil; end + for i, field in ipairs(fields) do + if field:len() == 0 and i ~= 1 and i ~= #fields then + for i = 1, 16 * (9 - #fields) do + result = result .. "0"; + end + else + for i = 1, 4 - field:len() do + result = result .. "0000"; + end + for i = 1, field:len() do + result = result .. hex2bits[field:sub(i,i)]; + end + end + end + return result; +end + +local function commonPrefixLength(ipA, ipB) + ipA, ipB = toBits(ipA), toBits(ipB); + for i = 1, 128 do + if ipA:sub(i,i) ~= ipB:sub(i,i) then + return i-1; + end + end + return 128; +end + +local function v4scope(ip) + local fields = {}; + ip:gsub("([^.]*).?", function (c) fields[#fields + 1] = tonumber(c) end); + -- Loopback: + if fields[1] == 127 then + return 0x2; + -- Link-local unicast: + elseif fields[1] == 169 and fields[2] == 254 then + return 0x2; + -- Site-local unicast: + elseif (fields[1] == 10) or (fields[1] == 192 and fields[2] == 168) or (fields[1] == 172 and fields[2] > 16) then + return 0x5; + -- Global unicast: + else + return 0xE; + end +end + +local function v6scope(ip) + -- Loopback: + if ip:match("^[0:]*1$") then + return 0x2; + -- Link-local unicast: + elseif ip:match("^[Ff][Ee][89ABab]") then + return 0x2; + -- Site-local unicast: + elseif ip:match("^[Ff][Ee][CcDdEeFf]") then + return 0x5; + -- Multicast: + elseif ip:match("^[Ff][Ff]") then + return tonumber("0x"..ip:sub(4,4)); + -- Global unicast: + else + return 0xE; + end +end + +local function label(ip) + if commonPrefixLength(ip, new_ip("::1", "IPv6")) == 128 then + return 0; + elseif commonPrefixLength(ip, new_ip("2002::", "IPv6")) >= 16 then + return 2; + elseif commonPrefixLength(ip, new_ip("::", "IPv6")) >= 96 then + return 3; + elseif commonPrefixLength(ip, new_ip("::ffff:0:0", "IPv6")) >= 96 then + return 4; + else + return 1; + end +end + +local function precedence(ip) + if commonPrefixLength(ip, new_ip("::1", "IPv6")) == 128 then + return 50; + elseif commonPrefixLength(ip, new_ip("2002::", "IPv6")) >= 16 then + return 30; + elseif commonPrefixLength(ip, new_ip("::", "IPv6")) >= 96 then + return 20; + elseif commonPrefixLength(ip, new_ip("::ffff:0:0", "IPv6")) >= 96 then + return 10; + else + return 40; + end +end + +local function toV4mapped(ip) + local fields = {}; + local ret = "::ffff:"; + ip:gsub("([^.]*).?", function (c) fields[#fields + 1] = tonumber(c) end); + ret = ret .. ("%02x"):format(fields[1]); + ret = ret .. ("%02x"):format(fields[2]); + ret = ret .. ":" + ret = ret .. ("%02x"):format(fields[3]); + ret = ret .. ("%02x"):format(fields[4]); + return new_ip(ret, "IPv6"); +end + +function ip_methods:toV4mapped() + if self.proto ~= "IPv4" then return nil, "No IPv4 address" end + local value = toV4mapped(self.addr); + self.toV4mapped = value; + return value; +end + +function ip_methods:label() + local value; + if self.proto == "IPv4" then + value = label(self.toV4mapped); + else + value = label(self); + end + self.label = value; + return value; +end + +function ip_methods:precedence() + local value; + if self.proto == "IPv4" then + value = precedence(self.toV4mapped); + else + value = precedence(self); + end + self.precedence = value; + return value; +end + +function ip_methods:scope() + local value; + if self.proto == "IPv4" then + value = v4scope(self.addr); + else + value = v6scope(self.addr); + end + self.scope = value; + return value; +end + +return {new_ip = new_ip, + commonPrefixLength = commonPrefixLength}; -- cgit v1.2.3 From 78ccb156e89bfce17932052ece3f580d9b2ca8f5 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Sat, 22 Oct 2011 17:59:33 +0200 Subject: util.rfc3484: New util implementing RFC3484 sorting --- tests/test.lua | 1 + tests/test_util_rfc3484.lua | 51 ++++++++++++++++++ util/rfc3484.lua | 124 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 tests/test_util_rfc3484.lua create mode 100644 util/rfc3484.lua diff --git a/tests/test.lua b/tests/test.lua index 000c3ee9..db727ce1 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -12,6 +12,7 @@ function run_all_tests() package.loaded["net.connlisteners"] = { get = function () return {} end }; dotest "util.jid" dotest "util.multitable" + dotest "util.rfc3484" dotest "net.http" dotest "core.modulemanager" dotest "core.stanza_router" diff --git a/tests/test_util_rfc3484.lua b/tests/test_util_rfc3484.lua new file mode 100644 index 00000000..18ae310e --- /dev/null +++ b/tests/test_util_rfc3484.lua @@ -0,0 +1,51 @@ +-- Prosody IM +-- Copyright (C) 2011 Florian Zeitz +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +function source(source) + local new_ip = require"util.ip".new_ip; + assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("3ffe::1", "IPv6"), new_ip("fe80::1", "IPv6")}).addr, "3ffe::1", "prefer appropriate scope"); + assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("fec0::1", "IPv6")}).addr, "fec0::1", "prefer appropriate scope"); + assert_equal(source(new_ip("fec0::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "2001::1", "prefer appropriate scope"); + assert_equal(source(new_ip("ff05::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "fec0::1", "prefer appropriate scope"); + assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("2001::1", "IPv6"), new_ip("2002::1", "IPv6")}).addr, "2001::1", "prefer same address"); + assert_equal(source(new_ip("fec0::1", "IPv6"), {new_ip("fec0::2", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "fec0::2", "prefer appropriate scope"); + assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("2001::2", "IPv6"), new_ip("3ffe::2", "IPv6")}).addr, "2001::2", "longest matching prefix"); + assert_equal(source(new_ip("2002:836b:2179::1", "IPv6"), {new_ip("2002:836b:2179::d5e3:7953:13eb:22e8", "IPv6"), new_ip("2001::2", "IPv6")}).addr, "2002:836b:2179::d5e3:7953:13eb:22e8", "prefer matching label"); +end + +function destination(dest) + local order; + local new_ip = require"util.ip".new_ip; + order = dest({new_ip("2001::1", "IPv6"), new_ip("131.107.65.121", "IPv4")}, {new_ip("2001::2", "IPv6"), new_ip("fe80::1", "IPv6"), new_ip("169.254.13.78", "IPv4")}) + assert_equal(order[1].addr, "2001::1", "prefer matching scope"); + assert_equal(order[2].addr, "131.107.65.121", "prefer matching scope") + + order = dest({new_ip("2001::1", "IPv6"), new_ip("131.107.65.121", "IPv4")}, {new_ip("fe80::1", "IPv6"), new_ip("131.107.65.117", "IPv4")}) + assert_equal(order[1].addr, "131.107.65.121", "prefer matching scope") + assert_equal(order[2].addr, "2001::1", "prefer matching scope") + + order = dest({new_ip("2001::1", "IPv6"), new_ip("10.1.2.3", "IPv4")}, {new_ip("2001::2", "IPv6"), new_ip("fe80::1", "IPv6"), new_ip("10.1.2.4", "IPv4")}) + assert_equal(order[1].addr, "2001::1", "prefer higher precedence"); + assert_equal(order[2].addr, "10.1.2.3", "prefer higher precedence"); + + order = dest({new_ip("2001::1", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("fe80::1", "IPv6")}, {new_ip("2001::2", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("fe80::2", "IPv6")}) + assert_equal(order[1].addr, "fe80::1", "prefer smaller scope"); + assert_equal(order[2].addr, "fec0::1", "prefer smaller scope"); + assert_equal(order[3].addr, "2001::1", "prefer smaller scope"); + + order = dest({new_ip("2001::1", "IPv6"), new_ip("3ffe::1", "IPv6")}, {new_ip("2001::2", "IPv6"), new_ip("3f44::2", "IPv6"), new_ip("fe80::2", "IPv6")}) + assert_equal(order[1].addr, "2001::1", "longest matching prefix"); + assert_equal(order[2].addr, "3ffe::1", "longest matching prefix"); + + order = dest({new_ip("2002:836b:4179::1", "IPv6"), new_ip("2001::1", "IPv6")}, {new_ip("2002:836b:4179::2", "IPv6"), new_ip("fe80::2", "IPv6")}) + assert_equal(order[1].addr, "2002:836b:4179::1", "prefer matching label"); + assert_equal(order[2].addr, "2001::1", "prefer matching label"); + + order = dest({new_ip("2002:836b:4179::1", "IPv6"), new_ip("2001::1", "IPv6")}, {new_ip("2002:836b:4179::2", "IPv6"), new_ip("2001::2", "IPv6"), new_ip("fe80::2", "IPv6")}) + assert_equal(order[1].addr, "2001::1", "prefer higher precedence"); + assert_equal(order[2].addr, "2002:836b:4179::1", "prefer higher precedence"); +end diff --git a/util/rfc3484.lua b/util/rfc3484.lua new file mode 100644 index 00000000..373d3c33 --- /dev/null +++ b/util/rfc3484.lua @@ -0,0 +1,124 @@ +-- Prosody IM +-- Copyright (C) 2008-2011 Florian Zeitz +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local t_sort = table.sort; +local commonPrefixLength = require"util.ip".commonPrefixLength +local new_ip = require"util.ip".new_ip; + +function source(dest, candidates) + local function comp(ipA, ipB) + -- Rule 1: Prefer same address + if dest == ipA then + return true; + elseif dest == ipB then + return false; + end + + -- Rule 2: Prefer appropriate scope + if ipA.scope < ipB.scope then + if ipA.scope < dest.scope then + return false; + else + return true; + end + elseif ipA.scope > ipB.scope then + if ipB.scope < dest.scope then + return true; + else + return false; + end + end + + -- Rule 3: Avoid deprecated addresses + -- XXX: No way to determine this + -- Rule 4: Prefer home addresses + -- XXX: Mobility Address related, no way to determine this + -- Rule 5: Prefer outgoing interface + -- XXX: Interface to address relation. No way to determine this + -- Rule 6: Prefer matching label + if ipA.label == dest.label and ipB.label ~= dest.label then + return true; + elseif ipB.label == dest.label and ipA.label ~= dest.label then + return false; + end + + -- Rule 7: Prefer public addresses (over temporary ones) + -- XXX: No way to determine this + -- Rule 8: Use longest matching prefix + if commonPrefixLength(ipA, dest) > commonPrefixLength(ipB, dest) then + return true; + else + return false; + end + end + + t_sort(candidates, comp); + return candidates[1]; +end + +function destination(candidates, sources) + local t_sort = table.sort; + local sourceAddrs = {}; + local function comp(ipA, ipB) + local ipAsource = sourceAddrs[ipA]; + local ipBsource = sourceAddrs[ipB]; + -- Rule 1: Avoid unusable destinations + -- XXX: No such information + -- Rule 2: Prefer matching scope + if ipA.scope == ipAsource.scope and ipB.scope ~= ipBsource.scope then + return true; + elseif ipA.scope ~= ipAsource.scope and ipB.scope == ipBsource.scope then + return false; + end + + -- Rule 3: Avoid deprecated addresses + -- XXX: No way to determine this + -- Rule 4: Prefer home addresses + -- XXX: Mobility Address related, no way to determine this + -- Rule 5: Prefer matching label + if ipAsource.label == ipA.label and ipBsource.label ~= ipB.label then + return true; + elseif ipBsource.label == ipB.label and ipAsource.label ~= ipA.label then + return false; + end + + -- Rule 6: Prefer higher precedence + if ipA.precedence > ipB.precedence then + return true; + elseif ipA.precedence < ipB.precedence then + return false; + end + + -- Rule 7: Prefer native transport + -- XXX: No way to determine this + -- Rule 8: Prefer smaller scope + if ipA.scope < ipB.scope then + return true; + elseif ipA.scope > ipB.scope then + return false; + end + + -- Rule 9: Use longest matching prefix + if commonPrefixLength(ipA, ipAsource) > commonPrefixLength(ipB, ipBsource) then + return true; + elseif commonPrefixLength(ipA, ipAsource) < commonPrefixLength(ipB, ipBsource) then + return false; + end + + -- Rule 10: Otherwise, leave order unchanged + return true; + end + for _, ip in ipairs(candidates) do + sourceAddrs[ip] = source(ip, sources); + end + + t_sort(candidates, comp); + return candidates; +end + +return {source = source, + destination = destination}; -- cgit v1.2.3 From 9c0e66e2d3b6bf83df25fa31d5e6ef9707b9d805 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Sat, 22 Oct 2011 18:29:23 +0200 Subject: s2smanager, xmppserver_listener: Support for connecting to other servers via IPv6 --- core/s2smanager.lua | 143 ++++++++++++++++++++++++++++++++------------ net/xmppserver_listener.lua | 2 +- 2 files changed, 107 insertions(+), 38 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 974b2f4f..c7eb9578 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -16,8 +16,8 @@ local socket = require "socket"; local format = string.format; local t_insert, t_sort = table.insert, table.sort; local get_traceback = debug.traceback; -local tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber, setmetatable - = tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber, setmetatable; +local tostring, pairs, ipairs, getmetatable, newproxy, next, error, tonumber, setmetatable + = tostring, pairs, ipairs, getmetatable, newproxy, next, error, tonumber, setmetatable; local idna_to_ascii = require "util.encodings".idna.to_ascii; local connlisteners_get = require "net.connlisteners".get; @@ -28,6 +28,8 @@ local st = require "stanza"; local stanza = st.stanza; local nameprep = require "util.encodings".stringprep.nameprep; local cert_verify_identity = require "util.x509".verify_identity; +local new_ip = require "util.ip".new_ip; +local rfc3484_dest = require "util.rfc3484".destination; local fire_event = prosody.events.fire_event; local uuid_gen = require "util.uuid".generate; @@ -43,6 +45,7 @@ local config = require "core.configmanager"; local connect_timeout = config.get("*", "core", "s2s_timeout") or 60; local dns_timeout = config.get("*", "core", "dns_timeout") or 15; local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; +local sources; dns.settimeout(dns_timeout); @@ -265,6 +268,8 @@ function attempt_connection(host_session, err) end, "_xmpp-server._tcp."..connect_host..".", "SRV"); return true; -- Attempt in progress + elseif host_session.ip_hosts then + return 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]; @@ -285,54 +290,118 @@ function attempt_connection(host_session, err) return try_connect(host_session, connect_host, connect_port); end -function try_connect(host_session, connect_host, connect_port) +function try_next_ip(host_session, connect_port) + 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= make_connect(host_session, ip, connect_port); + if not ok then + if not attempt_connection(host_session, err or "closed") then + err = err and (": "..err) or ""; + destroy_session(host_session, "Connection failed"..err); + end + end +end + +function try_connect(host_session, connect_host, connect_port, err) host_session.connecting = true; - local handle; - handle = adns.lookup(function (reply, err) - handle = nil; - host_session.connecting = nil; - - -- COMPAT: This is a compromise for all you CNAME-(ab)users :) - if not (reply and reply[#reply] and reply[#reply].a) then - local count = max_dns_depth; - reply = dns.peek(connect_host, "CNAME", "IN"); - while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do - log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); - reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); - count = count - 1; + + if not err then + local IPs = {}; + host_session.ip_hosts = IPs; + local handle4, handle6; + local has_other = false; + + if not sources then + sources = {}; + local cfg_sources = connlisteners_get("xmppserver").default_interface or config.get("*", "core", "interface"); + for i, source in ipairs(cfg_sources) do + if source == "*" then + sources[i] = new_ip("0.0.0.0", "IPv4"); + else + sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); + end end end - -- end of CNAME resolving - - if reply and reply[#reply] and reply[#reply].a then - log("debug", "DNS reply for %s gives us %s", connect_host, reply[#reply].a); - local ok, err = make_connect(host_session, reply[#reply].a, connect_port); - if not ok then - if not attempt_connection(host_session, err or "closed") then - err = err and (": "..err) or ""; - destroy_session(host_session, "Connection failed"..err); + + handle4 = adns.lookup(function (reply, err) + handle4 = nil; + + -- COMPAT: This is a compromise for all you CNAME-(ab)users :) + if not (reply and reply[#reply] and reply[#reply].a) then + local count = max_dns_depth; + reply = dns.peek(connect_host, "CNAME", "IN"); + while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do + log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); + reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); + count = count - 1; end end - else - log("debug", "DNS lookup failed to get a response for %s", connect_host); - if not 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 ""; - destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't + -- end of CNAME resolving + + 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 + end + + if has_other then + rfc3484_dest(host_session.ip_hosts, sources); + host_session.ip_choice = 0; + try_next_ip(host_session, connect_port); + else + has_other = true; + end + end, connect_host, "A", "IN"); + + handle6 = adns.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 end + + if has_other then + rfc3484_dest(host_session.ip_hosts, sources); + host_session.ip_choice = 0; + try_next_ip(host_session, connect_port); + else + has_other = true; + end + end, connect_host, "AAAA", "IN"); + + 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 + try_next_ip(host_session, connect_port); + else + host_session.ip_hosts = nil; + if not 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 ""; + destroy_session(host_session, "Connecting failed"..err); -- End of the line, we can't + return false; end - end, connect_host, "A", "IN"); + end return true; end function make_connect(host_session, connect_host, connect_port) - (host_session.log or log)("info", "Beginning new connection attempt to %s (%s:%d)", host_session.to_host, connect_host, connect_port); + (host_session.log or log)("info", "Beginning new connection attempt to %s ([%s]:%d)", host_session.to_host, connect_host.addr, connect_port); -- Ok, we're going to try to connect local from_host, to_host = host_session.from_host, host_session.to_host; - local conn, handler = socket.tcp(); + local conn, handler; + if connect_host.proto == "IPv4" then + conn, handler = socket.tcp(); + else + conn, handler = socket.tcp6(); + end if not conn then log("warn", "Failed to create outgoing connection, system error: %s", handler); @@ -340,14 +409,14 @@ function make_connect(host_session, connect_host, connect_port) end conn:settimeout(0); - local success, err = conn:connect(connect_host, connect_port); + 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, connect_port, err); + log("warn", "s2s connect() to %s (%s:%d) failed: %s", host_session.to_host, connect_host.addr, connect_port, err); return false, err; end local cl = connlisteners_get("xmppserver"); - conn = wrapclient(conn, connect_host, connect_port, cl, cl.default_mode or 1 ); + conn = wrapclient(conn, connect_host.addr, connect_port, cl, cl.default_mode or 1 ); host_session.conn = conn; local filter = initialize_filters(host_session); diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index 3af0b962..fe661033 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -178,7 +178,7 @@ end function xmppserver.ondisconnect(conn, err) local session = sessions[conn]; if session then - if err and err ~= "closed" and session.srv_hosts then + if err and err ~= "closed" then (session.log or log)("debug", "s2s connection attempt failed: %s", err); if s2s_attempt_connect(session, err) then (session.log or log)("debug", "...so we're going to try another target"); -- cgit v1.2.3 From 8d5ffd8d1a65a773ac7b67627b8ce116fbe522d2 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Wed, 26 Oct 2011 02:03:33 +0200 Subject: s2smanager, net.dns: Fix handling for NXNAME and SRV target of "." --- core/s2smanager.lua | 37 +++++++++++++++++++++++++++++++------ net/dns.lua | 1 + 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index c7eb9578..b930ceab 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -246,6 +246,11 @@ function attempt_connection(host_session, err) 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", to_host.." does not provide a XMPP service"); + destroy_session(host_session, err); -- Nothing to see here + return; + end t_sort(srv_hosts, compare_srv_priorities); local srv_choice = srv_hosts[1]; @@ -347,9 +352,19 @@ function try_connect(host_session, connect_host, connect_port, err) end if has_other then - rfc3484_dest(host_session.ip_hosts, sources); - host_session.ip_choice = 0; - try_next_ip(host_session, connect_port); + if #IPs > 0 then + rfc3484_dest(host_session.ip_hosts, sources); + host_session.ip_choice = 0; + try_next_ip(host_session, connect_port); + else + log("debug", "DNS lookup failed to get a response for %s", connect_host); + host_session.ip_hosts = nil; + if not 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 ""; + destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't + end + end else has_other = true; end @@ -366,9 +381,19 @@ function try_connect(host_session, connect_host, connect_port, err) end if has_other then - rfc3484_dest(host_session.ip_hosts, sources); - host_session.ip_choice = 0; - try_next_ip(host_session, connect_port); + if #IPs > 0 then + rfc3484_dest(host_session.ip_hosts, sources); + host_session.ip_choice = 0; + try_next_ip(host_session, connect_port); + else + log("debug", "DNS lookup failed to get a response for %s", connect_host); + host_session.ip_hosts = nil; + if not 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 ""; + destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't + end + end else has_other = true; end diff --git a/net/dns.lua b/net/dns.lua index 8f428476..5b32a56c 100644 --- a/net/dns.lua +++ b/net/dns.lua @@ -358,6 +358,7 @@ function resolver:name() -- - - - - - - - - - - - - - - - - - - - - - name local remember, pointers = nil, 0; local len = self:byte(); local n = {}; + if len == 0 then return "." end -- Root label while len > 0 do if len >= 0xc0 then -- name is "compressed" pointers = pointers + 1; -- cgit v1.2.3 From 400449ed020462fe86f61d30b5ea7d79e76accca Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Sun, 6 Nov 2011 18:23:16 +0100 Subject: util.rfc3484: Use a stable sorting algorithm --- util/rfc3484.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/util/rfc3484.lua b/util/rfc3484.lua index 373d3c33..dd855a84 100644 --- a/util/rfc3484.lua +++ b/util/rfc3484.lua @@ -5,10 +5,20 @@ -- COPYING file in the source package for more information. -- -local t_sort = table.sort; local commonPrefixLength = require"util.ip".commonPrefixLength local new_ip = require"util.ip".new_ip; +local function t_sort(t, comp) + for i = 1, (#t - 1) do + for j = (i + 1), #t do + local a, b = t[i], t[j]; + if not comp(a,b) then + t[i], t[j] = b, a; + end + end + end +end + function source(dest, candidates) local function comp(ipA, ipB) -- Rule 1: Prefer same address @@ -61,7 +71,6 @@ function source(dest, candidates) end function destination(candidates, sources) - local t_sort = table.sort; local sourceAddrs = {}; local function comp(ipA, ipB) local ipAsource = sourceAddrs[ipA]; -- cgit v1.2.3 From c789df45269398259083eb72b80c4ee04e8b2822 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 22 Nov 2011 01:44:28 +0500 Subject: util.xmppstream: A little cleanup. --- util/xmppstream.lua | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/util/xmppstream.lua b/util/xmppstream.lua index e5271b72..018f238d 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -11,26 +11,23 @@ local lxp = require "lxp"; local st = require "util.stanza"; local stanza_mt = st.stanza_mt; +local error = error; local tostring = tostring; local t_insert = table.insert; local t_concat = table.concat; local t_remove = table.remove; local setmetatable = setmetatable; -local default_log = require "util.logger".init("xmppstream"); - -- COMPAT: w/LuaExpat 1.1.0 local lxp_supports_doctype = pcall(lxp.new, { StartDoctypeDecl = false }); - if not lxp_supports_doctype then + local default_log = require "util.logger".init("xmppstream"); default_log("warn", "The version of LuaExpat on your system leaves Prosody " .."vulnerable to denial-of-service attacks. You should upgrade to " .."LuaExpat 1.1.1 or higher as soon as possible. See " .."http://prosody.im/doc/depends#luaexpat for more information."); end -local error = error; - module "xmppstream" local new_parser = lxp.new; @@ -50,8 +47,6 @@ _M.ns_pattern = ns_pattern; function new_sax_handlers(session, stream_callbacks) local xml_handlers = {}; - local log = session.log or default_log; - local cb_streamopened = stream_callbacks.streamopened; local cb_streamclosed = stream_callbacks.streamclosed; local cb_error = stream_callbacks.error or function(session, e) error("XML stream error: "..tostring(e)); end; @@ -188,7 +183,6 @@ function new_sax_handlers(session, stream_callbacks) local function set_session(stream, new_session) session = new_session; - log = new_session.log or default_log; end return xml_handlers, { reset = reset, set_session = set_session }; -- cgit v1.2.3 From 766d32e20c0e11395142dd950721845ed7a9e2da Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 22 Nov 2011 02:13:42 +0500 Subject: util.dependencies, util.xmppstream: Move LuaExpat version checking to util.dependencies. --- util/dependencies.lua | 8 ++++++++ util/xmppstream.lua | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/util/dependencies.lua b/util/dependencies.lua index 5baea942..53d2719d 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -136,6 +136,14 @@ function log_warnings() log("error", "This version of LuaSec contains a known bug that causes disconnects, see http://prosody.im/doc/depends"); end end + if lxp then + if not pcall(lxp.new, { StartDoctypeDecl = false }) then + log("error", "The version of LuaExpat on your system leaves Prosody " + .."vulnerable to denial-of-service attacks. You should upgrade to " + .."LuaExpat 1.1.1 or higher as soon as possible. See " + .."http://prosody.im/doc/depends#luaexpat for more information."); + end + end end return _M; diff --git a/util/xmppstream.lua b/util/xmppstream.lua index 018f238d..0f80742d 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -20,13 +20,6 @@ local setmetatable = setmetatable; -- COMPAT: w/LuaExpat 1.1.0 local lxp_supports_doctype = pcall(lxp.new, { StartDoctypeDecl = false }); -if not lxp_supports_doctype then - local default_log = require "util.logger".init("xmppstream"); - default_log("warn", "The version of LuaExpat on your system leaves Prosody " - .."vulnerable to denial-of-service attacks. You should upgrade to " - .."LuaExpat 1.1.1 or higher as soon as possible. See " - .."http://prosody.im/doc/depends#luaexpat for more information."); -end module "xmppstream" -- cgit v1.2.3 From c2409b3604775f62a3f251d673bb85f138a9a95c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 22 Nov 2011 17:56:52 +0000 Subject: loggingmanager: Remove unused 'critical' level --- core/loggingmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/loggingmanager.lua b/core/loggingmanager.lua index 88f2bbbf..426425c1 100644 --- a/core/loggingmanager.lua +++ b/core/loggingmanager.lua @@ -41,7 +41,7 @@ local logging_config; local apply_sink_rules; local log_sink_types = setmetatable({}, { __newindex = function (t, k, v) rawset(t, k, v); apply_sink_rules(k); end; }); local get_levels; -local logging_levels = { "debug", "info", "warn", "error", "critical" } +local logging_levels = { "debug", "info", "warn", "error" } -- Put a rule into action. Requires that the sink type has already been registered. -- This function is called automatically when a new sink type is added [see apply_sink_rules()] -- cgit v1.2.3 From 52f805163615f2f6a6013c74f4c427393805cff4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 28 Nov 2011 03:04:50 +0000 Subject: net.server_select: Autostart SSL negotiation as the last part of socket setup to avoid a traceback (#262) (thanks Maranda) --- net/server_select.lua | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/net/server_select.lua b/net/server_select.lua index 412d03c0..8802f620 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -596,25 +596,23 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.sendbuffer = handshake handshake( socket ) -- do handshake end - handler.readbuffer = _readbuffer - handler.sendbuffer = _sendbuffer - - if sslctx then - out_put "server.lua: auto-starting ssl negotiation..." - handler.autostart_ssl = true; - handler:starttls(sslctx); - end - - else - handler.readbuffer = _readbuffer - handler.sendbuffer = _sendbuffer end + + handler.readbuffer = _readbuffer + handler.sendbuffer = _sendbuffer send = socket.send receive = socket.receive shutdown = ( ssl and id ) or socket.shutdown _socketlist[ socket ] = handler _readlistlen = addsocket(_readlist, socket, _readlistlen) + + if sslctx and luasec then + out_put "server.lua: auto-starting ssl negotiation..." + handler.autostart_ssl = true; + handler:starttls(sslctx); + end + return handler, socket end -- cgit v1.2.3 From a66c1975686980fe42b8f8a4dcefde75da975540 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 28 Nov 2011 14:27:52 +0000 Subject: net.xmppserver_listener: Set default interface to '*' (fixes traceback) (thanks Zash) --- net/xmppserver_listener.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index fe661033..a92b1a7f 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -66,7 +66,7 @@ function stream_callbacks.handlestanza(session, stanza) end local sessions = {}; -local xmppserver = { default_port = 5269, default_mode = "*a" }; +local xmppserver = { default_port = 5269, default_mode = "*a", default_interface = "*" }; -- These are session methods -- -- cgit v1.2.3 From 05214cebac73c1559b0a794d9d982cfcb099717e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 28 Nov 2011 14:30:12 +0000 Subject: s2smanager: Use config over xmppserver's default interface --- core/s2smanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index b930ceab..77a1b040 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -319,7 +319,7 @@ function try_connect(host_session, connect_host, connect_port, err) if not sources then sources = {}; - local cfg_sources = connlisteners_get("xmppserver").default_interface or config.get("*", "core", "interface"); + local cfg_sources = config.get("*", "core", "interface") or connlisteners_get("xmppserver").default_interface; for i, source in ipairs(cfg_sources) do if source == "*" then sources[i] = new_ip("0.0.0.0", "IPv4"); -- cgit v1.2.3 From 65ffc174876fadcd779692dafd642386b2b9a635 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 30 Nov 2011 17:20:16 +0000 Subject: util.ip: Fix in IP range calculation for 172.16.0.0/12 --- util/ip.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/ip.lua b/util/ip.lua index 5e2f46bb..5b866b04 100644 --- a/util/ip.lua +++ b/util/ip.lua @@ -65,7 +65,7 @@ local function v4scope(ip) elseif fields[1] == 169 and fields[2] == 254 then return 0x2; -- Site-local unicast: - elseif (fields[1] == 10) or (fields[1] == 192 and fields[2] == 168) or (fields[1] == 172 and fields[2] > 16) then + elseif (fields[1] == 10) or (fields[1] == 192 and fields[2] == 168) or (fields[1] == 172 and (fields[2] >= 16 and fields[2] < 32) then return 0x5; -- Global unicast: else -- cgit v1.2.3 From d531e402d09d48774806fead2b497d34ea8d79c1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 30 Nov 2011 19:37:13 +0000 Subject: util.ip: Fix syntax error (while I search for my pre-commit hooks) --- util/ip.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/ip.lua b/util/ip.lua index 5b866b04..2f09c034 100644 --- a/util/ip.lua +++ b/util/ip.lua @@ -65,7 +65,7 @@ local function v4scope(ip) elseif fields[1] == 169 and fields[2] == 254 then return 0x2; -- Site-local unicast: - elseif (fields[1] == 10) or (fields[1] == 192 and fields[2] == 168) or (fields[1] == 172 and (fields[2] >= 16 and fields[2] < 32) then + elseif (fields[1] == 10) or (fields[1] == 192 and fields[2] == 168) or (fields[1] == 172 and (fields[2] >= 16 and fields[2] < 32)) then return 0x5; -- Global unicast: else -- cgit v1.2.3 From 6033bbbd91b4c925b78cc05c873c4f249177c555 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 2 Dec 2011 19:24:54 +0100 Subject: util.dataforms: Fix form verification --- util/dataforms.lua | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/util/dataforms.lua b/util/dataforms.lua index e4d24cf6..b69df819 100644 --- a/util/dataforms.lua +++ b/util/dataforms.lua @@ -120,12 +120,18 @@ function form_t.data(layout, stanza) end end - local reader = field_readers[field.type]; - local verifier = field.verifier or field_verifiers[field.type]; - if reader then - data[field.name] = reader(tag); - if verifier then - errors[field.name] = verifier(data[field.name], tag, field.required); + if not tag then + if field.required then + errors[field.name] = "Required value missing"; + end + else + local reader = field_readers[field.type]; + local verifier = field.verifier or field_verifiers[field.type]; + if reader then + data[field.name] = reader(tag); + if verifier then + errors[field.name] = verifier(data[field.name], tag, field.required); + end end end end @@ -161,7 +167,7 @@ field_readers["jid-single"] = field_verifiers["jid-single"] = function (data, field_tag, required) - if #data == 0 and required then + if ((not data) or (#data == 0)) and required then return "Required value missing"; end if not jid_prep(data) then -- cgit v1.2.3 From ea0a2a6ecb4b696189f04e4b8f3eaf5e0969699f Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Sat, 3 Dec 2011 17:10:48 +0100 Subject: util.dataforms: Fix verfication for booleans --- util/dataforms.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/dataforms.lua b/util/dataforms.lua index b69df819..d4a1865c 100644 --- a/util/dataforms.lua +++ b/util/dataforms.lua @@ -252,7 +252,7 @@ field_readers["boolean"] = field_verifiers["boolean"] = function (data, field_tag, required) data = field_readers["text-single"](field_tag); - if #data == 0 and required then + if ((not data) or (#data == 0)) and required then return "Required value missing"; end if data ~= "1" and data ~= "true" and data ~= "0" and data ~= "false" then -- cgit v1.2.3 From 02027fafeb00508b0a5f04d197d22d9508ae3b9d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 7 Dec 2011 02:58:22 +0000 Subject: mod_bosh: Store time to destroy session in inactive_sessions, removing dependency on session.bosh_max_inactive in cleanup timer --- plugins/mod_bosh.lua | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 834b128a..ffba5c11 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -91,9 +91,10 @@ function on_destroy_request(request) end -- If this session now has no requests open, mark it as inactive - if #requests == 0 and session.bosh_max_inactive and not inactive_sessions[session] then - inactive_sessions[session] = os_time(); - (session.log or log)("debug", "BOSH session marked as inactive at %d", inactive_sessions[session]); + local max_inactive = session.bosh_max_inactive; + if max_inactive and #requests == 0 then + inactive_sessions[session] = os_time() + max_inactive; + (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive); end end end @@ -402,17 +403,13 @@ function on_timer() now = now - 3; local n_dead_sessions = 0; - for session, inactive_since in pairs(inactive_sessions) do - if session.bosh_max_inactive then - if now - inactive_since > session.bosh_max_inactive then - (session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now); - sessions[session.sid] = nil; - inactive_sessions[session] = nil; - n_dead_sessions = n_dead_sessions + 1; - dead_sessions[n_dead_sessions] = session; - end - else + for session, close_after in pairs(inactive_sessions) do + if close_after < now then + (session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now); + sessions[session.sid] = nil; inactive_sessions[session] = nil; + n_dead_sessions = n_dead_sessions + 1; + dead_sessions[n_dead_sessions] = session; end end -- cgit v1.2.3 From 046b9c1dc0020a12ab668fa927c688c433fceecb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 7 Dec 2011 03:54:28 +0000 Subject: mod_bosh: Remove a session from inactive_sessions before destroying it --- plugins/mod_bosh.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index ffba5c11..3541f614 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -214,6 +214,7 @@ local function bosh_close_stream(session, reason) held_request:destroy(); end sessions[session.sid] = nil; + inactive_sessions[session] = nil; sm_destroy_session(session); end -- cgit v1.2.3 From 1ef4ed4ad00f546da835900daa15687613982b6b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 7 Dec 2011 04:57:51 +0000 Subject: mod_bosh: Move stream:features sending until after the current request has been added to session.requests. Ensures correct inactivity logic. --- plugins/mod_bosh.lua | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 3541f614..b8759f60 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -120,10 +120,17 @@ function handle_request(method, body, request) request.on_destroy = on_destroy_request; local stream = new_xmpp_stream(request, stream_callbacks); + -- stream:feed() calls the stream_callbacks, so all stanzas in -- the body are processed in this next line before it returns. + -- In particular, the streamopened() stream callback is where + -- much of the session logic happens, because it's where we first + -- get to see the 'sid' of this request. stream:feed(body); + -- Stanzas (if any) in the request have now been processed, and + -- we take care of the high-level BOSH logic here, including + -- giving a response or putting the request "on hold". local session = sessions[request.sid]; if session then -- Session was marked as inactive, since we have @@ -218,6 +225,7 @@ local function bosh_close_stream(session, reason) sm_destroy_session(session); end +-- Handle the tag in the request payload. function stream_callbacks.streamopened(request, attr) local sid = attr.sid; log("debug", "BOSH body open (sid: %s)", sid or ""); @@ -340,14 +348,6 @@ function stream_callbacks.streamopened(request, attr) session.rid = rid; end - if session.notopen then - local features = st.stanza("stream:features"); - hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); - fire_event("stream-features", session, features); - session.send(features); - session.notopen = nil; - end - if attr.type == "terminate" then -- Client wants to end this session, which we'll do -- after processing any stanzas in this request @@ -357,6 +357,14 @@ function stream_callbacks.streamopened(request, attr) request.notopen = nil; -- Signals that we accept this opening tag t_insert(session.requests, request); request.sid = sid; + + if session.notopen then + local features = st.stanza("stream:features"); + hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); + fire_event("stream-features", session, features); + session.send(features); + session.notopen = nil; + end end function stream_callbacks.handlestanza(request, stanza) -- cgit v1.2.3 From 0572f1ef240e909b4e16feda8964e56cfb1aa982 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 7 Dec 2011 05:04:55 +0000 Subject: util.array: Add pluck() method to pick a given property from each item --- util/array.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/array.lua b/util/array.lua index 5dbd3037..cf586214 100644 --- a/util/array.lua +++ b/util/array.lua @@ -60,6 +60,13 @@ function array_base.sort(outa, ina, ...) return outa; end +function array_base.pluck(outa, ina, key) + for i=1,#ina do + outa[i] = ina[i][key]; + end + return outa; +end + --- These methods only mutate function array_methods:random() return self[math.random(1,#self)]; -- cgit v1.2.3 From 6c51171c432a9068270a580edd1ddd4753fde62c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 7 Dec 2011 05:14:58 +0000 Subject: util.array: Expand some of the more basic methods to act more sensibly than their names suggested --- util/array.lua | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/util/array.lua b/util/array.lua index cf586214..cbb051d3 100644 --- a/util/array.lua +++ b/util/array.lua @@ -25,6 +25,15 @@ end setmetatable(array, { __call = new_array }); +-- Read-only methods +function array_methods:random() + return self[math.random(1,#self)]; +end + +-- These methods can be called two ways: +-- array.method(existing_array, [params [, ...]]) -- Create new array for result +-- existing_array:method([params, ...]) -- Transform existing array into result +-- function array_base.map(outa, ina, func) for k,v in ipairs(ina) do outa[k] = func(v); @@ -67,11 +76,7 @@ function array_base.pluck(outa, ina, key) return outa; end ---- These methods only mutate -function array_methods:random() - return self[math.random(1,#self)]; -end - +--- These methods only mutate the array function array_methods:shuffle(outa, ina) local len = #self; for i=1,#self do @@ -98,10 +103,23 @@ function array_methods:append(array) return self; end -array_methods.push = table.insert; -array_methods.pop = table.remove; -array_methods.concat = table.concat; -array_methods.length = function (t) return #t; end +function array_methods:push(x) + table.insert(self, x); +end + +function array_methods:pop(x) + local v = self[x]; + table.remove(self, x); + return v; +end + +function array_methods:concat(sep) + return table.concat(array.map(self, tostring), sep); +end + +function array_methods:length() + return #self; +end --- These methods always create a new array function array.collect(f, s, var) -- cgit v1.2.3 From ab71edddf05e451599a138ee7ebd4cd97f69fa46 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 7 Dec 2011 05:17:39 +0000 Subject: util.iterators: it2table: Fix variable name --- util/iterators.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/iterators.lua b/util/iterators.lua index 2a87e97a..aa0b172b 100644 --- a/util/iterators.lua +++ b/util/iterators.lua @@ -140,7 +140,7 @@ end -- Treat the return of an iterator as key,value pairs, -- and build a table function it2table(f, s, var) - local t, var = {}; + local t, var2 = {}; while true do var, var2 = f(s, var); if var == nil then break; end -- cgit v1.2.3 From 600357125883443e1dd8d47f12e77b62321f8952 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 7 Dec 2011 05:54:17 +0000 Subject: mod_bosh: Experimental option 'bosh_auto_cork' which witholds any response to a request until all stanzas in it have been processed. --- plugins/mod_bosh.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index b8759f60..e45ebeb4 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -35,6 +35,7 @@ local BOSH_DEFAULT_POLLING = module:get_option_number("bosh_max_polling", 5); local BOSH_DEFAULT_REQUESTS = module:get_option_number("bosh_max_requests", 2); local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure"); +local auto_cork = module:get_option_boolean("bosh_auto_cork", false); local default_headers = { ["Content-Type"] = "text/xml; charset=utf-8" }; @@ -268,7 +269,7 @@ function stream_callbacks.streamopened(request, attr) end --log("debug", "Sending BOSH data: %s", tostring(s)); local oldest_request = r[1]; - if oldest_request then + if oldest_request and (not(auto_cork) or waiting_requests[oldest_request]) then log("debug", "We have an open request, so sending on that"); response.body = t_concat({ " Date: Wed, 7 Dec 2011 23:37:56 +0100 Subject: s2smanager: Store port specified by SRV records --- core/s2smanager.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 77a1b040..08c1543b 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -295,11 +295,11 @@ function attempt_connection(host_session, err) return try_connect(host_session, connect_host, connect_port); end -function try_next_ip(host_session, connect_port) +function 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= make_connect(host_session, ip, connect_port); + local ok, err= make_connect(host_session, ip.ip, ip.port); if not ok then if not attempt_connection(host_session, err or "closed") then err = err and (": "..err) or ""; @@ -354,8 +354,11 @@ function try_connect(host_session, connect_host, connect_port, err) if has_other then if #IPs > 0 then rfc3484_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; - try_next_ip(host_session, connect_port); + try_next_ip(host_session); else log("debug", "DNS lookup failed to get a response for %s", connect_host); host_session.ip_hosts = nil; @@ -383,8 +386,11 @@ function try_connect(host_session, connect_host, connect_port, err) if has_other then if #IPs > 0 then rfc3484_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; - try_next_ip(host_session, connect_port); + try_next_ip(host_session); else log("debug", "DNS lookup failed to get a response for %s", connect_host); host_session.ip_hosts = nil; @@ -401,7 +407,7 @@ function try_connect(host_session, connect_host, connect_port, err) 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 - try_next_ip(host_session, connect_port); + try_next_ip(host_session); else host_session.ip_hosts = nil; if not attempt_connection(host_session, "out of IP addresses") then -- Retry if we can -- cgit v1.2.3 From a4ab78295d0d95644d57981ced9d2629170a7b47 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 8 Dec 2011 07:41:28 +0000 Subject: configure: Remove obsolete option '--require-config' --- configure | 6 ------ 1 file changed, 6 deletions(-) diff --git a/configure b/configure index af046223..00ed2658 100755 --- a/configure +++ b/configure @@ -59,8 +59,6 @@ Configure Prosody prior to building. Default is $CC --linker=CC The linker to use when building modules. Default is $LD ---require-config Will cause Prosody to refuse to run when - it fails to find a configuration file EOF } @@ -127,9 +125,6 @@ do DATADIR="$value" DATADIR_SET=yes ;; - --require-config) - REQUIRE_CONFIG=yes - ;; --lua-suffix=*) LUA_SUFFIX="$value" LUA_SUFFIX_SET=yes @@ -327,7 +322,6 @@ LUA_DIR=$LUA_DIR LUA_INCDIR=$LUA_INCDIR LUA_LIBDIR=$LUA_LIBDIR LUA_BINDIR=$LUA_BINDIR -REQUIRE_CONFIG=$REQUIRE_CONFIG IDN_LIB=$IDN_LIB IDNA_LIBS=$IDNA_LIBS OPENSSL_LIB=$OPENSSL_LIB -- cgit v1.2.3 From 7f60996cffb70e443c286adef321f86f9334cb45 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Fri, 9 Dec 2011 11:37:30 +0500 Subject: migrator/jabberd14: Minor refactoring. --- tools/migration/migrator/jabberd14.lua | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tools/migration/migrator/jabberd14.lua b/tools/migration/migrator/jabberd14.lua index 47004a52..54c45d94 100644 --- a/tools/migration/migrator/jabberd14.lua +++ b/tools/migration/migrator/jabberd14.lua @@ -67,15 +67,12 @@ local parse_xml = (function() end)(); local function load_xml(path) - if path then - local f, err = io_open(path); - if not f then return f, err; end - local data = f:read("*a"); - f:close(); - if data then - return parse_xml(data); - end - end + local f, err = io_open(path); + if not f then return f, err; end + local data = f:read("*a"); + f:close(); + if not data then return; end + return parse_xml(data); end local function load_spool_file(host, filename, path) -- cgit v1.2.3 From 3c7605bff46bf3f6368a08cc5d80f3c394a5552d Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Fri, 9 Dec 2011 11:38:35 +0500 Subject: net.httpserver: Removed unused import. --- net/httpserver.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/net/httpserver.lua b/net/httpserver.lua index 74f61c56..44e8e24d 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -7,7 +7,6 @@ -- -local server = require "net.server" local url_parse = require "socket.url".parse; local httpstream_new = require "util.httpstream".new; -- cgit v1.2.3 From 5ff78636e02bf0375c53b19c436f0a419d247c93 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Fri, 9 Dec 2011 11:44:00 +0500 Subject: mod_bosh: Fixed use of a private HTTP request property. --- plugins/mod_bosh.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index e45ebeb4..f4b38bfd 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -58,7 +58,7 @@ end local trusted_proxies = module:get_option_set("trusted_proxies", {"127.0.0.1"})._items; local function get_ip_from_request(request) - local ip = request.handler:ip(); + local ip = request.conn:ip(); local forwarded_for = request.headers["x-forwarded-for"]; if forwarded_for then forwarded_for = forwarded_for..", "..ip; -- cgit v1.2.3 From c4e0cdb9f5320543e2decfab0241114d7987b984 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Fri, 9 Dec 2011 11:57:14 +0500 Subject: util.array: Make array:push() chainable. --- util/array.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/util/array.lua b/util/array.lua index cbb051d3..fdf3c9a3 100644 --- a/util/array.lua +++ b/util/array.lua @@ -105,6 +105,7 @@ end function array_methods:push(x) table.insert(self, x); + return self; end function array_methods:pop(x) -- cgit v1.2.3 From a2f97afa0c57587b77bff3819d04aa694bc60db3 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Fri, 9 Dec 2011 12:02:21 +0500 Subject: util.array: Avoid globals. --- util/array.lua | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/util/array.lua b/util/array.lua index fdf3c9a3..5dc604ba 100644 --- a/util/array.lua +++ b/util/array.lua @@ -9,6 +9,11 @@ local t_insert, t_sort, t_remove, t_concat = table.insert, table.sort, table.remove, table.concat; +local setmetatable = setmetatable; +local math_random = math.random; +local pairs, ipairs = pairs, ipairs; +local tostring = tostring; + local array = {}; local array_base = {}; local array_methods = {}; @@ -27,7 +32,7 @@ setmetatable(array, { __call = new_array }); -- Read-only methods function array_methods:random() - return self[math.random(1,#self)]; + return self[math_random(1,#self)]; end -- These methods can be called two ways: @@ -80,7 +85,7 @@ end function array_methods:shuffle(outa, ina) local len = #self; for i=1,#self do - local r = math.random(i,len); + local r = math_random(i,len); self[i], self[r] = self[r], self[i]; end return self; @@ -104,18 +109,18 @@ function array_methods:append(array) end function array_methods:push(x) - table.insert(self, x); + t_insert(self, x); return self; end function array_methods:pop(x) local v = self[x]; - table.remove(self, x); + t_remove(self, x); return v; end function array_methods:concat(sep) - return table.concat(array.map(self, tostring), sep); + return t_concat(array.map(self, tostring), sep); end function array_methods:length() @@ -128,7 +133,7 @@ function array.collect(f, s, var) while true do var = f(s, var); if var == nil then break; end - table.insert(t, var); + t_insert(t, var); end return setmetatable(t, array_mt); end -- cgit v1.2.3 From 77135ff9c038d029b2ebe9f274ce8cb14b8b5d3d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 10 Dec 2011 05:45:07 +0000 Subject: mod_adhoc: Use module:handle_items() --- plugins/adhoc/mod_adhoc.lua | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/plugins/adhoc/mod_adhoc.lua b/plugins/adhoc/mod_adhoc.lua index 6d826338..49d07103 100644 --- a/plugins/adhoc/mod_adhoc.lua +++ b/plugins/adhoc/mod_adhoc.lua @@ -90,19 +90,13 @@ module:hook("iq/host/"..xmlns_cmd..":command", function (event) end end, 500); -local function handle_item_added(item) +local function adhoc_added(event) + local item = event.item; commands[item.node] = item; end -module:hook("item-added/adhoc", function (event) - return handle_item_added(event.item); -end, 500); - -module:hook("item-removed/adhoc", function (event) +local function adhoc_removed(event) commands[event.item.node] = nil; -end, 500); - --- Pick up any items that are already added -for _, item in ipairs(module:get_host_items("adhoc")) do - handle_item_added(item); end + +module:handle_items("adhoc", adhoc_added, adhoc_removed); -- cgit v1.2.3 From b803655fc3922d51ccfb44eebe8540e3ba562b2b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 10 Dec 2011 17:21:19 +0000 Subject: configmanager: get(): Make section (core) optional (hurrah) --- core/configmanager.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/configmanager.lua b/core/configmanager.lua index 85919492..e2253171 100644 --- a/core/configmanager.lua +++ b/core/configmanager.lua @@ -41,6 +41,9 @@ function getconfig() end function get(host, section, key) + if not key then + section, key = "core", section; + end local sec = config[host][section]; if sec then return sec[key]; -- cgit v1.2.3 From 960450680b4811aedefe53827d68fa936379375d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 11 Dec 2011 17:08:34 +0000 Subject: configure: Add 'openbsd' preset (thanks xavier) --- configure | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 00ed2658..dd6af49c 100755 --- a/configure +++ b/configure @@ -59,6 +59,8 @@ Configure Prosody prior to building. Default is $CC --linker=CC The linker to use when building modules. Default is $LD +--require-config Will cause Prosody to refuse to run when + it fails to find a configuration file EOF } @@ -110,7 +112,7 @@ do CFLAGS="-Wall -fPIC" LDFLAGS="-shared" fi - if [ "$OSTYPE" = "freebsd" ] + if [ "$OSTYPE" = "freebsd" -o "$OSTYPE" = "openbsd" ] then LUA_INCDIR="/usr/local/include/lua51" LUA_INCDIR_SET=yes CFLAGS="-Wall -fPIC -I/usr/local/include" @@ -120,11 +122,17 @@ do LUA_DIR=/usr/local LUA_DIR_SET=yes fi + if [ "$OSTYPE" = "openbsd" ] + then LUA_INCDIR="/usr/local/include"; + fi ;; --datadir=*) DATADIR="$value" DATADIR_SET=yes ;; + --require-config) + REQUIRE_CONFIG=yes + ;; --lua-suffix=*) LUA_SUFFIX="$value" LUA_SUFFIX_SET=yes @@ -322,6 +330,7 @@ LUA_DIR=$LUA_DIR LUA_INCDIR=$LUA_INCDIR LUA_LIBDIR=$LUA_LIBDIR LUA_BINDIR=$LUA_BINDIR +REQUIRE_CONFIG=$REQUIRE_CONFIG IDN_LIB=$IDN_LIB IDNA_LIBS=$IDNA_LIBS OPENSSL_LIB=$OPENSSL_LIB -- cgit v1.2.3 From b66913bf3b9097832eced4ddb6b94dd34df0b06b Mon Sep 17 00:00:00 2001 From: James Callahan Date: Mon, 12 Dec 2011 17:08:48 +1100 Subject: util/datamanager: Use package.config to figure out directory seperator --- util/datamanager.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/datamanager.lua b/util/datamanager.lua index d5e9c88c..a5d676cc 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -1,7 +1,7 @@ -- 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. -- @@ -20,7 +20,7 @@ local error = error; local next = next; local t_insert = table.insert; local append = require "util.serialization".append; -local path_separator = "/"; if os.getenv("WINDIR") then path_separator = "\\" end +local path_separator = assert ( package.config:match ( "^([^\n]+)" ) , "package.config not in standard form" ) -- Extract directory seperator from package.config (an undocumented string that comes with lua) local lfs = require "lfs"; local prosody = prosody; local raw_mkdir; @@ -72,7 +72,7 @@ local function callback(username, host, datastore, data) username, host, datastore, data = f(username, host, datastore, data); if username == false then break; end end - + return username, host, datastore, data; end function add_callback(func) -- cgit v1.2.3 From 7f9a5266d9eaf0b6d89df77f5b7a096d6ded39bd Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Mon, 12 Dec 2011 14:53:12 +0500 Subject: mod_watchregistrations: Fixed an undefined global access (thanks Medics). --- plugins/mod_watchregistrations.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_watchregistrations.lua b/plugins/mod_watchregistrations.lua index ee51566b..42f9c017 100644 --- a/plugins/mod_watchregistrations.lua +++ b/plugins/mod_watchregistrations.lua @@ -18,7 +18,7 @@ module:hook("user-registered", function (user) module:log("debug", "Notifying of new registration"); local message = st.message{ type = "chat", from = host } :tag("body") - :text(registration_alert:gsub("%$(%w+)", function (v) + :text(registration_notification:gsub("%$(%w+)", function (v) return user[v] or user.session and user.session[v] or nil; end)); for _, jid in ipairs(registration_watchers) do -- cgit v1.2.3 From 054526af75a0961800094176930c935f325c48e0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 13 Dec 2011 13:34:21 +0000 Subject: modulemanager: Remove 'config' from module environments (no modules use it that I'm aware of) --- core/modulemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 2d1eeb77..c4d95695 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -117,7 +117,7 @@ function load(host, module_name, config) end local _log = logger.init(host..":"..module_name); - local api_instance = setmetatable({ name = module_name, host = host, path = err, config = config, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); + local api_instance = setmetatable({ name = module_name, host = host, path = err, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); api_instance.environment = pluginenv; -- cgit v1.2.3 From 347fce20ef2be3f2b03d379ae5396f8e9a7a9189 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 13 Dec 2011 15:40:37 +0000 Subject: s2smanager: Remove unused reference to modulemanager --- core/s2smanager.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 08c1543b..df5523e1 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -23,7 +23,6 @@ local idna_to_ascii = require "util.encodings".idna.to_ascii; local connlisteners_get = require "net.connlisteners".get; local initialize_filters = require "util.filters".initialize; local wrapclient = require "net.server".wrapclient; -local modulemanager = require "core.modulemanager"; local st = require "stanza"; local stanza = st.stanza; local nameprep = require "util.encodings".stringprep.nameprep; -- cgit v1.2.3 From 9b3bd4fcc3b88ee76bda9564ea86daa8f01f9cdb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 13 Dec 2011 15:42:21 +0000 Subject: sessionmanager: Remove unused reference to modulemanager --- core/sessionmanager.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 1de6c41a..b1ec819f 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -16,7 +16,6 @@ local hosts = hosts; local full_sessions = full_sessions; local bare_sessions = bare_sessions; -local modulemanager = require "core.modulemanager"; local logger = require "util.logger"; local log = logger.init("sessionmanager"); local error = error; -- cgit v1.2.3 From 5c9595e00fe0b8a02476ead3c156c4fb7236963b Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 14 Dec 2011 06:42:23 +0500 Subject: mod_bosh: Remove unused reference to lxp --- plugins/mod_bosh.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index f4b38bfd..9de27b4b 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -9,7 +9,6 @@ module.host = "*" -- Global module local hosts = _G.hosts; -local lxp = require "lxp"; local new_xmpp_stream = require "util.xmppstream".new; local httpserver = require "net.httpserver"; local sm = require "core.sessionmanager"; -- cgit v1.2.3 From 8770e9aedc62822e917171a60ed2055cb239db73 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 14 Dec 2011 06:46:24 +0500 Subject: s2smanager: Don't throw an error when the "interface" config option is a string (which it is by default). --- core/s2smanager.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index df5523e1..240b9ba8 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -319,6 +319,9 @@ function try_connect(host_session, connect_host, connect_port, err) if not sources then sources = {}; local cfg_sources = config.get("*", "core", "interface") or connlisteners_get("xmppserver").default_interface; + if type(cfg_sources) == "string" then + cfg_sources = { cfg_sources }; + end for i, source in ipairs(cfg_sources) do if source == "*" then sources[i] = new_ip("0.0.0.0", "IPv4"); -- cgit v1.2.3 From fded13009e8308e7f9008cc99de07a9a64f51825 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 14 Dec 2011 07:19:58 +0500 Subject: usermanager: Prep admin JIDs (fixes issue#276). --- core/usermanager.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/usermanager.lua b/core/usermanager.lua index 0152afd7..9e5a016c 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -11,6 +11,7 @@ local log = require "util.logger".init("usermanager"); local type = type; local ipairs = ipairs; local jid_bare = require "util.jid".bare; +local jid_prep = require "util.jid".prep; local config = require "core.configmanager"; local hosts = hosts; local sasl_new = require "util.sasl".new; @@ -97,6 +98,7 @@ end function is_admin(jid, host) if host and not hosts[host] then return false; end + if type(jid) ~= "string" then return false; end local is_admin; jid = jid_bare(jid); @@ -108,7 +110,7 @@ function is_admin(jid, host) if host_admins and host_admins ~= global_admins then if type(host_admins) == "table" then for _,admin in ipairs(host_admins) do - if admin == jid then + if jid_prep(admin) == jid then is_admin = true; break; end @@ -121,7 +123,7 @@ function is_admin(jid, host) if not is_admin and global_admins then if type(global_admins) == "table" then for _,admin in ipairs(global_admins) do - if admin == jid then + if jid_prep(admin) == jid then is_admin = true; break; end -- cgit v1.2.3 From 445a8774513e0c6319b0e41c29e1d4ce11983f1e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 16 Dec 2011 16:01:59 +0000 Subject: hostmanager: Add send() method to hosts --- core/hostmanager.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 9e74cd6b..0dd1d426 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -53,6 +53,17 @@ end prosody_events.add_handler("server-starting", load_enabled_hosts); +local function host_send(stanza) + local name, type = stanza.name, stanza.attr.type; + if type == "error" or (name == "iq" and type == "result") then + local dest_host_name = select(2, jid_split(stanza.attr.to)); + local dest_host = hosts[dest_host_name] or { type = "unknown" }; + log("warn", "Unhandled response sent to %s host %s: %s", dest_host.type, dest_host_name, tostring(stanza)); + return; + end + core_route_stanza(nil, stanza); +end + function activate(host, host_config) if hosts[host] then return nil, "The host "..host.." is already activated"; end host_config = host_config or configmanager.getconfig()[host]; @@ -63,6 +74,7 @@ function activate(host, host_config) events = events_new(); dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen(); disallow_s2s = configmanager.get(host, "core", "disallow_s2s"); + send = host_send; }; if not host_config.core.component_module then -- host host_session.type = "local"; -- cgit v1.2.3 From 2ffa0eecf38d4588ba81a9869582830b0838db1f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 20 Dec 2011 17:36:38 +0000 Subject: s2smanager: Fix missing import of 'type' (thanks darkrain) --- core/s2smanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 240b9ba8..f44921c3 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -16,8 +16,8 @@ local socket = require "socket"; local format = string.format; local t_insert, t_sort = table.insert, table.sort; local get_traceback = debug.traceback; -local tostring, pairs, ipairs, getmetatable, newproxy, next, error, tonumber, setmetatable - = tostring, pairs, ipairs, getmetatable, newproxy, next, error, tonumber, setmetatable; +local tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable + = tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable; local idna_to_ascii = require "util.encodings".idna.to_ascii; local connlisteners_get = require "net.connlisteners".get; -- cgit v1.2.3 From a60732b6660b4318caa847ecc17b9f106006f9ba Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 21 Dec 2011 07:58:22 +0000 Subject: util.stanza: Remove unused __add metamethod --- util/stanza.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/util/stanza.lua b/util/stanza.lua index de83977f..600212a4 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -258,11 +258,6 @@ function stanza_mt.get_error(stanza) return type, condition or "undefined-condition", text; end -function stanza_mt.__add(s1, s2) - return s1:add_direct_child(s2); -end - - do local id = 0; function new_id() -- cgit v1.2.3 From c5693a2483a30f7aab76a2c225b66a0033a92b5c Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 6 Jan 2012 21:43:30 +0000 Subject: net.xmppcomponent_listener: removed unused variable reference. --- net/xmppcomponent_listener.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index 90293559..dd7b2b91 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -7,8 +7,6 @@ -- -local hosts = _G.hosts; - local t_concat = table.concat; local tostring = tostring; local type = type; -- cgit v1.2.3 From b70702ed03c381f5da90f101f7663d511b2e4e8d Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 6 Jan 2012 21:45:33 +0000 Subject: mod_component: removed unused variable reference, added "flagging" to assert if a component is connected or not. --- plugins/mod_component.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua index 97c46a8c..f7d09930 100644 --- a/plugins/mod_component.lua +++ b/plugins/mod_component.lua @@ -10,8 +10,6 @@ if module:get_host_type() ~= "component" then error("Don't load mod_component manually, it should be for a component, please see http://prosody.im/doc/components", 0); end -local hosts = _G.hosts; - local t_concat = table.concat; local sha1 = require "util.hashes".sha1; @@ -23,6 +21,7 @@ local main_session, send; local function on_destroy(session, err) if main_session == session then + connected = false; main_session = nil; send = nil; session.on_destroy = nil; @@ -83,6 +82,7 @@ function handle_component_auth(event) -- If component not already created for this host, create one now if not main_session then + connected = true; send = session.send; main_session = session; session.on_destroy = on_destroy; -- cgit v1.2.3 From 8eba0e5ebdb9a49deaa9ce2768f07a89854a46d7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 13 Jan 2012 15:46:36 +0000 Subject: util.debug: Adjust level within get_locals_table() to account for the additional depth of this function itself --- util/debug.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/util/debug.lua b/util/debug.lua index 22d02bf2..2170a6d1 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -9,6 +9,7 @@ local censored_names = { }; local function get_locals_table(level) + level = level + 1; -- Skip this function itself local locals = {}; for local_num = 1, math.huge do local name, value = debug.getlocal(level, local_num); -- cgit v1.2.3 From ca964ddb88627847099e3dc18c29cdf263a8b8e5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 13 Jan 2012 23:30:00 +0000 Subject: util.throttle: Import setmetatable --- util/throttle.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/util/throttle.lua b/util/throttle.lua index 8b62e797..82b0a67b 100644 --- a/util/throttle.lua +++ b/util/throttle.lua @@ -1,5 +1,6 @@ local gettime = require "socket".gettime; +local setmetatable = setmetatable; module "throttle" -- cgit v1.2.3 From d39558af1788626626d13e66f5b13cbd1228efa0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 13 Jan 2012 23:30:32 +0000 Subject: util.throttle: Fix 'outstanding' return value --- util/throttle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/throttle.lua b/util/throttle.lua index 82b0a67b..2e901158 100644 --- a/util/throttle.lua +++ b/util/throttle.lua @@ -34,7 +34,7 @@ function throttle:poll(cost, split) if split then self.balance = 0; end - return false, balance, (cost-self.balance); + return false, balance, (cost-balance); end end -- cgit v1.2.3 From 422af47bdddc607f7f9bc7673a335bc86fe03188 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 16 Jan 2012 04:44:23 +0000 Subject: net.server_event: Fix :pause() to actually stop reading from the socket, rather than ignoring socket-readable events (!), and :resume() to restart the event listener --- net/server_event.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index f4745c34..dbf5161f 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -295,7 +295,10 @@ do end function interface_mt:resume() - return self:_lock(self.nointerface, false, self.nowriting); + self:_lock(self.nointerface, false, self.nowriting); + if not self.eventread then + self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT ); -- register callback + end end function interface_mt:counter(c) @@ -642,6 +645,10 @@ do return -1 end end + if interface.noreading then + interface.eventread = nil; + return -1; + end return EV_READ, cfg.READ_TIMEOUT end end -- cgit v1.2.3 From fc3658aa3a7f4c210c94856af3fbc886171f31ea Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 17 Jan 2012 17:56:36 +0000 Subject: xmppserver_listener: Only re-attempt connection on disconnect if it was an unauthenticated s2sout (thanks Medics for the log) --- net/xmppserver_listener.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index a92b1a7f..048c6c7d 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -178,7 +178,7 @@ end function xmppserver.ondisconnect(conn, err) local session = sessions[conn]; if session then - if err and err ~= "closed" then + if err and err ~= "closed" and session.type == "s2sout_unauthed" then (session.log or log)("debug", "s2s connection attempt failed: %s", err); if s2s_attempt_connect(session, err) then (session.log or log)("debug", "...so we're going to try another target"); -- cgit v1.2.3 From c65490db56caf69caaa6ae061e544d97a7ed4910 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 18 Jan 2012 08:54:26 +0500 Subject: util.json: Added function encode_ordered(object). --- util/json.lua | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/util/json.lua b/util/json.lua index 5d0b0876..efc602f0 100644 --- a/util/json.lua +++ b/util/json.lua @@ -1,6 +1,6 @@ local type = type; -local t_insert, t_concat, t_remove = table.insert, table.concat, table.remove; +local t_insert, t_concat, t_remove, t_sort = table.insert, table.concat, table.remove, table.sort; local s_char = string.char; local tostring, tonumber = tostring, tonumber; local pairs, ipairs = pairs, ipairs; @@ -79,11 +79,25 @@ function tablesave(o, buffer) if next(__hash) ~= nil or next(hash) ~= nil or next(__array) == nil then t_insert(buffer, "{"); local mark = #buffer; - for k,v in pairs(hash) do - stringsave(k, buffer); - t_insert(buffer, ":"); - simplesave(v, buffer); - t_insert(buffer, ","); + if buffer.ordered then + local keys = {}; + for k in pairs(hash) do + t_insert(keys, k); + end + t_sort(keys); + for _,k in ipairs(keys) do + stringsave(k, buffer); + t_insert(buffer, ":"); + simplesave(hash[k], buffer); + t_insert(buffer, ","); + end + else + for k,v in pairs(hash) do + stringsave(k, buffer); + t_insert(buffer, ":"); + simplesave(v, buffer); + t_insert(buffer, ","); + end end if next(__hash) ~= nil then t_insert(buffer, "\"__hash\":["); @@ -129,6 +143,11 @@ function json.encode(obj) simplesave(obj, t); return t_concat(t); end +function json.encode_ordered(obj) + local t = { ordered = true }; + simplesave(obj, t); + return t_concat(t); +end ----------------------------------- -- cgit v1.2.3 From 97507e207a3f618dc5ee6f9a9a0f4e0759b2013c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 18 Jan 2012 15:07:26 +0000 Subject: mod_tls: Fix log statement (thanks Zash) --- plugins/mod_tls.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index cace2d69..707ae8f5 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -75,7 +75,7 @@ end); module:hook_stanza("http://etherx.jabber.org/streams", "features", function (session, stanza) module:log("debug", "Received features element"); if can_do_tls(session) and stanza:child_with_ns(xmlns_starttls) then - module:log("%s is offering TLS, taking up the offer...", session.to_host); + module:log("debug", "%s is offering TLS, taking up the offer...", session.to_host); session.sends2s(""); return true; end -- cgit v1.2.3 From dabccd65ac4cf3da3b241434d83e026df5d5bfdc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 19 Jan 2012 16:38:04 +0100 Subject: mod_compression: Use get_option_number --- plugins/mod_compression.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/mod_compression.lua b/plugins/mod_compression.lua index 82403016..b5031d72 100644 --- a/plugins/mod_compression.lua +++ b/plugins/mod_compression.lua @@ -16,12 +16,8 @@ local xmlns_stream = "http://etherx.jabber.org/streams"; local compression_stream_feature = st.stanza("compression", {xmlns=xmlns_compression_feature}):tag("method"):text("zlib"):up(); local add_filter = require "util.filters".add_filter; -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; +local compression_level = module:get_option_number("compression_level", 9); - -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)); module:log("warn", "Module loading aborted. Compression won't be available."); -- cgit v1.2.3 From 3d9ca327ce10f2bb4d5af85c1695fe70dcf6dbe3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 19 Jan 2012 16:47:12 +0100 Subject: mod_compression: Change default compression level to 7 --- plugins/mod_compression.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_compression.lua b/plugins/mod_compression.lua index b5031d72..62c0fa2e 100644 --- a/plugins/mod_compression.lua +++ b/plugins/mod_compression.lua @@ -16,7 +16,7 @@ local xmlns_stream = "http://etherx.jabber.org/streams"; local compression_stream_feature = st.stanza("compression", {xmlns=xmlns_compression_feature}):tag("method"):text("zlib"):up(); local add_filter = require "util.filters".add_filter; -local compression_level = module:get_option_number("compression_level", 9); +local compression_level = module:get_option_number("compression_level", 7); 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)); -- cgit v1.2.3 From 72fcc7b12d296135eb828ed3f8b151171d9d5f1e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 20 Jan 2012 21:58:04 +0100 Subject: util.prosodyctl: Add getline() and show_prompt() --- util/prosodyctl.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index d0045abc..8c58f2cd 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -16,6 +16,7 @@ local signal = require "util.signal"; local set = require "util.set"; local lfs = require "lfs"; local pcall = pcall; +local type = type; local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep; @@ -63,6 +64,13 @@ function getchar(n) end end +function getline() + local ok, line = pcall(io.read, "*l"); + if ok then + return line; + end +end + function getpass() local stty_ret = os.execute("stty -echo 2>/dev/null"); if stty_ret ~= 0 then @@ -112,6 +120,13 @@ function read_password() return password; end +function show_prompt(prompt) + io.write(prompt, " "); + local line = getline(); + line = line and line:gsub("\n$",""); + return (line and #line > 0) and line or nil; +end + -- Server control function adduser(params) local user, host, password = nodeprep(params.user), nameprep(params.host), params.password; -- cgit v1.2.3 From 3c51125734a2352f7d80efd97963ad91063b0a59 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 20 Jan 2012 21:59:13 +0100 Subject: user.x509: Add some utility functions for generating OpenSSL configs --- util/x509.lua | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/util/x509.lua b/util/x509.lua index d3c55bb4..f106e6fa 100644 --- a/util/x509.lua +++ b/util/x509.lua @@ -21,6 +21,10 @@ local nameprep = require "util.encodings".stringprep.nameprep; local idna_to_ascii = require "util.encodings".idna.to_ascii; local log = require "util.logger".init("x509"); +local pairs, ipairs = pairs, ipairs; +local s_format = string.format; +local t_insert = table.insert; +local t_concat = table.concat; module "x509" @@ -208,4 +212,109 @@ function verify_identity(host, service, cert) return false end +-- TODO Rename? Split out subroutines? +-- Also, this is probably openssl specific, what TODO about that? +function genx509san(hosts, config, certhosts, raw) -- recive config through that or some better way? + local function utf8string(s) + -- This is how we tell openssl not to encode UTF-8 strings as Latin1 + return s_format("FORMAT:UTF8,UTF8:%s", s); + end + + local function ia5string(s) + return s_format("IA5STRING:%s", s); + end + + local function dnsname(t, host) + t_insert(t.DNS, idna_to_ascii(host)); + end + + local function srvname(t, host, service) + t_insert(t.otherName, s_format("%s;%s", oid_dnssrv, ia5string("_" .. service .."." .. idna_to_ascii(host)))); + end + + local function xmppAddr(t, host) + t_insert(t.otherName, s_format("%s;%s", oid_xmppaddr, utf8string(host))); + end + + ----------------------------- + + local san = { + DNS = {}; + otherName = {}; + }; + + local sslsanconf = { }; + + for i = 1,#certhosts do + local certhost = certhosts[i]; + for name, host in pairs(hosts) do + if name == certhost or name:sub(-1-#certhost) == "."..certhost then + dnsname(san, name); + --print(name .. "#component_module: " .. (config.get(name, "core", "component_module") or "nil")); + if config.get(name, "core", "component_module") == nil then + srvname(san, name, "xmpp-client"); + end + --print(name .. "#anonymous_login: " .. tostring(config.get(name, "core", "anonymous_login"))); + if not (config.get(name, "core", "anonymous_login") or + config.get(name, "core", "authentication") == "anonymous") then + srvname(san, name, "xmpp-server"); + end + xmppAddr(san, name); + end + end + end + + for t, n in pairs(san) do + for i = 1,#n do + t_insert(sslsanconf, s_format("%s.%d = %s", t, i -1, n[i])); + end + end + + return raw and sslsanconf or t_concat(sslsanconf, "\n"); +end + +function baseconf() + return { + req = { + distinguished_name = "distinguished_name", + req_extensions = "v3_extensions", + x509_extensions = "v3_extensions", + prompt = "no", + }, + distinguished_name = { + commonName = "example.com", + countryName = "GB", + localityName = "The Internet", + organizationName = "Your Organisation", + organizationalUnitName = "XMPP Department", + emailAddress = "xmpp@example.com", + }, + v3_extensions = { + basicConstraints = "CA:FALSE", + keyUsage = "digitalSignature,keyEncipherment", + extendedKeyUsage = "serverAuth,clientAuth", + subjectAltName = "@subject_alternative_name", + }, + subject_alternative_name = { }, + } +end + +function serialize_conf(conf) + local s = ""; + for k, t in pairs(conf) do + s = s .. ("[%s]\n"):format(k); + if t[1] then + for i, v in ipairs(t) do + s = s .. ("%s\n"):format(v); + end + else + for k, v in pairs(t) do + s = s .. ("%s = %s\n"):format(k, v); + end + end + s = s .. "\n"; + end + return s; +end + return _M; -- cgit v1.2.3 From 59e3094002cff2da6ab8ce70fc2439a7bc4897af Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 20 Jan 2012 22:04:28 +0100 Subject: prosodyctl: Add commands for generating certificates and keys --- prosodyctl | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/prosodyctl b/prosodyctl index f0ba68ce..cdeaaf26 100755 --- a/prosodyctl +++ b/prosodyctl @@ -236,6 +236,7 @@ local show_message, show_warning = prosodyctl.show_message, prosodyctl.show_warn local show_usage = prosodyctl.show_usage; local getchar, getpass = prosodyctl.getchar, prosodyctl.getpass; local show_yesno = prosodyctl.show_yesno; +local show_prompt = prosodyctl.show_prompt; local read_password = prosodyctl.read_password; local prosodyctl_timeout = (config.get("*", "core", "prosodyctl_timeout") or 5) * 2; @@ -612,6 +613,106 @@ function commands.unregister(arg) return 1; end +local x509 = require "util.x509"; +local genx509san = x509.genx509san; +local opensslbaseconf = x509.baseconf; +local seralizeopensslbaseconf = x509.serialize_conf; + +local cert_commands = {}; + +-- TODO Should this be moved to util.prosodyctl or x509? +function cert_commands.config(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local conf_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".cnf"; + if os.execute("test -f "..conf_filename) == 0 + and not show_yesno("Overwrite "..conf_filename .. "?") then + return nil, conf_filename; + end + local conf = opensslbaseconf(); + conf.subject_alternative_name = genx509san(hosts, config, arg, true) + for k, v in pairs(conf.distinguished_name) do + local nv; + if k == "commonName" then + v = arg[1] + elseif k == "emailAddress" then + v = "xmpp@" .. arg[1]; + end + nv = show_prompt(("%s (%s):"):format(k, nv or v)); + nv = (not nv or nv == "") and v or nv; + conf.distinguished_name[k] = nv ~= "." and nv or nil; + end + local conf_file = io.open(conf_filename, "w"); + conf_file:write(seralizeopensslbaseconf(conf)); + conf_file:close(); + print(""); + show_message("Config written to " .. conf_filename); + return nil, conf_filename; + else + show_usage("cert config HOSTNAME", "generates config for OpenSSL") + end +end + +function cert_commands.key(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local key_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".key"; + if os.execute("test -f "..key_filename) == 0 + and not show_yesno("Overwrite "..key_filename .. "?") then + return nil, key_filename; + end + local key_size = tonumber(arg[2] or show_prompt("Choose key size (2048):") or 2048); + os.execute(("openssl genrsa -out %s %d"):format(key_filename, tonumber(key_size))); + os.execute(("chmod 400 %s"):format(key_filename)); + show_message("Key written to ".. key_filename); + return nil, key_filename; + else + show_usage("cert key HOSTNAME ", "Generates a RSA key") + end +end + +function cert_commands.request(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local req_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".req"; + if os.execute("test -f "..req_filename) == 0 + and not show_yesno("Overwrite "..req_filename .. "?") then + return nil, req_filename; + end + local _, key_filename = cert_commands.key({arg[1]}); + local _, conf_filename = cert_commands.config({arg[1]}); + os.execute(("openssl req -new -key %s -utf8 -config %s -out %s") + :format(key_filename, conf_filename, req_filename)); + show_message("Certificate request written to ".. req_filename); + else + show_usage("cert request HOSTNAME", "Generates a certificate request") + end +end + +function cert_commands.generate(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local cert_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".cert"; + if os.execute("test -f "..cert_filename) == 0 + and not show_yesno("Overwrite "..cert_filename .. "?") then + return nil, cert_filename; + end + local _, key_filename = cert_commands.key({arg[1]}); + local _, conf_filename = cert_commands.config({arg[1]}); + os.execute(("openssl req -new -x509 -nodes -key %s -days 365 -sha1 -utf8 -config %s -out %s") + :format(key_filename, conf_filename, cert_filename)); + show_message("Certificate written to ".. cert_filename); + else + show_usage("cert generate HOSTNAME", "Generates a self-signed certificate") + end +end + +function commands.cert(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local subcmd = table.remove(arg, 1); + if type(cert_commands[subcmd]) == "function" then + return cert_commands[subcmd](arg); + end + end + show_usage("cert config|request|generate|key", "Helpers for X.509 certificates.") +end + --------------------- if command and command:match("^mod_") then -- Is a command in a module -- cgit v1.2.3 From 594d90204c9ebb27d2b6e3603d2f17b07d520552 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:41:55 +0000 Subject: modulemanager: Move in-module API functions to core.moduleapi (half the file size, yay) --- core/moduleapi.lua | 239 +++++++++++++++++++++++++++++++++++++++++++++++++ core/modulemanager.lua | 213 +------------------------------------------ 2 files changed, 240 insertions(+), 212 deletions(-) create mode 100644 core/moduleapi.lua diff --git a/core/moduleapi.lua b/core/moduleapi.lua new file mode 100644 index 00000000..ee960709 --- /dev/null +++ b/core/moduleapi.lua @@ -0,0 +1,239 @@ +-- Prosody IM +-- Copyright (C) 2008-2012 Matthew Wild +-- Copyright (C) 2008-2012 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local config = require "core.configmanager"; +local modulemanager = require "modulemanager"; +local array = require "util.array"; +local set = require "util.set"; +local logger = require "util.logger"; +local pluginloader = require "util.pluginloader"; + +local multitable_new = require "util.multitable".new; + +local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; +local error, setmetatable, setfenv, type = error, setmetatable, setfenv, type; +local ipairs, pairs, select, unpack = ipairs, pairs, select, unpack; +local tonumber, tostring = tonumber, tostring; + +local prosody = prosody; +local hosts = prosody.hosts; + +-- Registry of shared module data +local shared_data = setmetatable({}, { __mode = "v" }); + +local NULL = {}; + +local api = {}; + +-- Returns the name of the current module +function api:get_name() + return self.name; +end + +-- Returns the host that the current module is serving +function api:get_host() + return self.host; +end + +function api:get_host_type() + return hosts[self.host].type; +end + +function api:set_global() + self.host = "*"; + -- Update the logger + local _log = logger.init("mod_"..self.name); + self.log = function (self, ...) return _log(...); end; + self._log = _log; +end + +function api:add_feature(xmlns) + self:add_item("feature", xmlns); +end +function api:add_identity(category, type, name) + self:add_item("identity", {category = category, type = type, name = name}); +end +function api:add_extension(data) + self:add_item("extension", data); +end + +function api:fire_event(...) + return (hosts[self.host] or prosody).events.fire_event(...); +end + +function api:hook(event, handler, priority) + hooks:set(self.host, self.name, event, handler, true); + (hosts[self.host] or prosody).events.add_handler(event, handler, priority); +end + +function api:hook_global(event, handler, priority) + hooks:set("*", self.name, event, handler, true); + prosody.events.add_handler(event, handler, priority); +end + +function api:hook_stanza(xmlns, name, handler, priority) + if not handler and type(name) == "function" then + -- If only 2 options then they specified no xmlns + xmlns, name, handler, priority = nil, xmlns, name, handler; + elseif not (handler and name) then + self:log("warn", "Error: Insufficient parameters to module:hook_stanza()"); + return; + end + return self:hook("stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority); +end + +function api:require(lib) + local f, n = pluginloader.load_code(self.name, lib..".lib.lua"); + if not f then + f, n = pluginloader.load_code(lib, lib..".lib.lua"); + end + if not f then error("Failed to load plugin library '"..lib.."', error: "..n); end -- FIXME better error message + setfenv(f, self.environment); + return f(); +end + +function api:get_option(name, default_value) + local value = config.get(self.host, self.name, name); + if value == nil then + value = config.get(self.host, "core", name); + if value == nil then + value = default_value; + end + end + return value; +end + +function api:get_option_string(name, default_value) + local value = self:get_option(name, default_value); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + if value == nil then + return nil; + end + return tostring(value); +end + +function api:get_option_number(name, ...) + local value = self:get_option(name, ...); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + local ret = tonumber(value); + if value ~= nil and ret == nil then + self:log("error", "Config option '%s' not understood, expecting a number", name); + end + return ret; +end + +function api:get_option_boolean(name, ...) + local value = self:get_option(name, ...); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + if value == nil then + return nil; + end + local ret = value == true or value == "true" or value == 1 or nil; + if ret == nil then + ret = (value == false or value == "false" or value == 0); + if ret then + ret = false; + else + ret = nil; + end + end + if ret == nil then + self:log("error", "Config option '%s' not understood, expecting true/false", name); + end + return ret; +end + +function api:get_option_array(name, ...) + local value = self:get_option(name, ...); + + if value == nil then + return nil; + end + + if type(value) ~= "table" then + return array{ value }; -- Assume any non-list is a single-item list + end + + return array():append(value); -- Clone +end + +function api:get_option_set(name, ...) + local value = self:get_option_array(name, ...); + + if value == nil then + return nil; + end + + return set.new(value); +end + +local module_items = multitable_new(); +function api:add_item(key, value) + self.items = self.items or {}; + self.items[key] = self.items[key] or {}; + t_insert(self.items[key], value); + self:fire_event("item-added/"..key, {source = self, item = value}); +end +function api:remove_item(key, value) + local t = self.items and self.items[key] or NULL; + for i = #t,1,-1 do + if t[i] == value then + t_remove(self.items[key], i); + self:fire_event("item-removed/"..key, {source = self, item = value}); + return value; + end + end +end + +function api:get_host_items(key) + local result = {}; + for mod_name, module in pairs(modulemanager.get_modules(self.host)) do + module = module.module; + if module.items then + for _, item in ipairs(module.items[key] or NULL) do + t_insert(result, item); + end + end + end + for mod_name, module in pairs(modulemanager.get_modules("*")) do + module = module.module; + if module.items then + for _, item in ipairs(module.items[key] or NULL) do + t_insert(result, item); + end + end + end + return result; +end + +function api:handle_items(type, added_cb, removed_cb, existing) + self:hook("item-added/"..type, added_cb); + self:hook("item-removed/"..type, removed_cb); + if existing ~= false then + for _, item in ipairs(self:get_host_items(type)) do + added_cb({ item = item }); + end + end +end + +return api; diff --git a/core/modulemanager.lua b/core/modulemanager.lua index c4d95695..bbe24e32 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -45,8 +45,7 @@ local _G = _G; module "modulemanager" -api = {}; -local api = api; -- Module API container +local api = _G.require "core.moduleapi"; -- Module API container local modulemap = { ["*"] = {} }; @@ -253,214 +252,4 @@ function call_module_method(module, method, ...) end end ------ API functions exposed to modules ----------- --- Must all be in api.* - --- Returns the name of the current module -function api:get_name() - return self.name; -end - --- Returns the host that the current module is serving -function api:get_host() - return self.host; -end - -function api:get_host_type() - return hosts[self.host].type; -end - -function api:set_global() - self.host = "*"; - -- Update the logger - local _log = logger.init("mod_"..self.name); - self.log = function (self, ...) return _log(...); end; - self._log = _log; -end - -function api:add_feature(xmlns) - self:add_item("feature", xmlns); -end -function api:add_identity(category, type, name) - self:add_item("identity", {category = category, type = type, name = name}); -end -function api:add_extension(data) - self:add_item("extension", data); -end - -function api:fire_event(...) - return (hosts[self.host] or prosody).events.fire_event(...); -end - -function api:hook(event, handler, priority) - hooks:set(self.host, self.name, event, handler, true); - (hosts[self.host] or prosody).events.add_handler(event, handler, priority); -end - -function api:hook_global(event, handler, priority) - hooks:set("*", self.name, event, handler, true); - prosody.events.add_handler(event, handler, priority); -end - -function api:hook_stanza(xmlns, name, handler, priority) - if not handler and type(name) == "function" then - -- If only 2 options then they specified no xmlns - xmlns, name, handler, priority = nil, xmlns, name, handler; - elseif not (handler and name) then - self:log("warn", "Error: Insufficient parameters to module:hook_stanza()"); - return; - end - return api.hook(self, "stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority); -end - -function api:require(lib) - local f, n = pluginloader.load_code(self.name, lib..".lib.lua"); - if not f then - f, n = pluginloader.load_code(lib, lib..".lib.lua"); - end - if not f then error("Failed to load plugin library '"..lib.."', error: "..n); end -- FIXME better error message - setfenv(f, self.environment); - return f(); -end - -function api:get_option(name, default_value) - local value = config.get(self.host, self.name, name); - if value == nil then - value = config.get(self.host, "core", name); - if value == nil then - value = default_value; - end - end - return value; -end - -function api:get_option_string(name, default_value) - local value = self:get_option(name, default_value); - if type(value) == "table" then - if #value > 1 then - self:log("error", "Config option '%s' does not take a list, using just the first item", name); - end - value = value[1]; - end - if value == nil then - return nil; - end - return tostring(value); -end - -function api:get_option_number(name, ...) - local value = self:get_option(name, ...); - if type(value) == "table" then - if #value > 1 then - self:log("error", "Config option '%s' does not take a list, using just the first item", name); - end - value = value[1]; - end - local ret = tonumber(value); - if value ~= nil and ret == nil then - self:log("error", "Config option '%s' not understood, expecting a number", name); - end - return ret; -end - -function api:get_option_boolean(name, ...) - local value = self:get_option(name, ...); - if type(value) == "table" then - if #value > 1 then - self:log("error", "Config option '%s' does not take a list, using just the first item", name); - end - value = value[1]; - end - if value == nil then - return nil; - end - local ret = value == true or value == "true" or value == 1 or nil; - if ret == nil then - ret = (value == false or value == "false" or value == 0); - if ret then - ret = false; - else - ret = nil; - end - end - if ret == nil then - self:log("error", "Config option '%s' not understood, expecting true/false", name); - end - return ret; -end - -function api:get_option_array(name, ...) - local value = self:get_option(name, ...); - - if value == nil then - return nil; - end - - if type(value) ~= "table" then - return array{ value }; -- Assume any non-list is a single-item list - end - - return array():append(value); -- Clone -end - -function api:get_option_set(name, ...) - local value = self:get_option_array(name, ...); - - if value == nil then - return nil; - end - - return set.new(value); -end - -local t_remove = _G.table.remove; -local module_items = multitable_new(); -function api:add_item(key, value) - self.items = self.items or {}; - self.items[key] = self.items[key] or {}; - t_insert(self.items[key], value); - self:fire_event("item-added/"..key, {source = self, item = value}); -end -function api:remove_item(key, value) - local t = self.items and self.items[key] or NULL; - for i = #t,1,-1 do - if t[i] == value then - t_remove(self.items[key], i); - self:fire_event("item-removed/"..key, {source = self, item = value}); - return value; - end - end -end - -function api:get_host_items(key) - local result = {}; - for mod_name, module in pairs(modulemap[self.host]) do - module = module.module; - if module.items then - for _, item in ipairs(module.items[key] or NULL) do - t_insert(result, item); - end - end - end - for mod_name, module in pairs(modulemap["*"]) do - module = module.module; - if module.items then - for _, item in ipairs(module.items[key] or NULL) do - t_insert(result, item); - end - end - end - return result; -end - -function api:handle_items(type, added_cb, removed_cb, existing) - self:hook("item-added/"..type, added_cb); - self:hook("item-removed/"..type, removed_cb); - if existing ~= false then - for _, item in ipairs(self:get_host_items(type)) do - added_cb({ item = item }); - end - end -end - return _M; -- cgit v1.2.3 From 5ae5e9a1e5e37d4e088e77c82238055aca816967 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:46:17 +0000 Subject: modulemanager: Some reorganisation. Only external change is (should be) that module-unloaded and module-loaded are no longer fired when reloading a module, the new event module-reloaded is fired instead. --- core/modulemanager.lua | 156 +++++++++++++++++++++++++++++-------------------- 1 file changed, 94 insertions(+), 62 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index bbe24e32..1846e0f0 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -90,7 +90,45 @@ end prosody_events.add_handler("host-activated", load_modules_for_host); -- -function load(host, module_name, config) +--- Private helpers --- + +local function do_unload_module(host, name) + local mod = get_module(host, name); + if not mod then return nil, "module-not-loaded"; end + + if module_has_method(mod, "unload") then + local ok, err = call_module_method(mod, "unload"); + if (not ok) and err then + log("warn", "Non-fatal error unloading module '%s' on '%s': %s", name, host, err); + end + end + -- unhook event handlers hooked by module:hook + for event, handlers in pairs(hooks:get(host, name) or NULL) do + for handler in pairs(handlers or NULL) do + (hosts[host] or prosody).events.remove_handler(event, handler); + end + end + -- unhook event handlers hooked by module:hook_global + for event, handlers in pairs(hooks:get("*", name) or NULL) do + for handler in pairs(handlers or NULL) do + prosody.events.remove_handler(event, handler); + 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 = mod.module, item = value}); + end + end + end + modulemap[host][name] = nil; + return true; +end + +local function do_load_module(host, module_name) if not (host and module_name) then return nil, "insufficient-parameters"; elseif not hosts[host] then @@ -116,7 +154,9 @@ function load(host, module_name, config) end local _log = logger.init(host..":"..module_name); - local api_instance = setmetatable({ name = module_name, host = host, path = err, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); + local api_instance = setmetatable({ name = module_name, host = host, path = err, + _log = _log, log = function (self, ...) return _log(...); end } + , { __index = api }); local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); api_instance.environment = pluginenv; @@ -125,11 +165,12 @@ function load(host, module_name, config) hosts[host].modules = modulemap[host]; modulemap[host][module_name] = pluginenv; - local success, err = pcall(mod); - if success then + local ok, err = pcall(mod); + if ok then + -- Call module's "load" if module_has_method(pluginenv, "load") then - success, err = call_module_method(pluginenv, "load"); - if not success then + ok, err = call_module_method(pluginenv, "load"); + if not ok then log("warn", "Error loading module '%s' on '%s': %s", module_name, host, err or "nil"); end end @@ -142,62 +183,12 @@ function load(host, module_name, config) end else log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); + do_unload_module(api_instance.host, module_name); -- Ignore error, module may be partially-loaded end - if success then - (hosts[api_instance.host] or prosody).events.fire_event("module-loaded", { module = module_name, host = host }); - return true; - else -- load failed, unloading - unload(api_instance.host, module_name); - return nil, err; - end -end - -function get_module(host, name) - return modulemap[host] and modulemap[host][name]; -end - -function is_loaded(host, name) - return modulemap[host] and modulemap[host][name] and true; + return ok and mod, err; end -function unload(host, name, ...) - local mod = get_module(host, name); - if not mod then return nil, "module-not-loaded"; end - - if module_has_method(mod, "unload") then - local ok, err = call_module_method(mod, "unload"); - if (not ok) and err then - log("warn", "Non-fatal error unloading module '%s' on '%s': %s", name, host, err); - end - end - -- unhook event handlers hooked by module:hook - for event, handlers in pairs(hooks:get(host, name) or NULL) do - for handler in pairs(handlers or NULL) do - (hosts[host] or prosody).events.remove_handler(event, handler); - end - end - -- unhook event handlers hooked by module:hook_global - for event, handlers in pairs(hooks:get("*", name) or NULL) do - for handler in pairs(handlers or NULL) do - prosody.events.remove_handler(event, handler); - 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 = mod.module, item = value}); - end - end - end - modulemap[host][name] = nil; - (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host }); - return true; -end - -function reload(host, name, ...) +local function do_reload_module(host, name) local mod = get_module(host, name); if not mod then return nil, "module-not-loaded"; end @@ -224,8 +215,8 @@ function reload(host, name, ...) end end - unload(host, name, ...); - local ok, err = load(host, name, ...); + do_unload_module(host, name); + local ok, err = do_load_module(host, name); if ok then mod = get_module(host, name); if module_has_method(mod, "restore") then @@ -234,11 +225,52 @@ function reload(host, name, ...) log("warn", "Error restoring module '%s' from '%s': %s", name, host, err); end end - return true; + end + return ok and mod, err; +end + +--- Public API --- + +-- Load a module and fire module-loaded event +function load(host, name) + local mod, err = do_load_module(host, name); + if mod then + (hosts[mod.host] or prosody).events.fire_event("module-loaded", { module = module_name, host = host }); + end + return mod, err; +end + +-- Unload a module and fire module-unloaded +function unload(host, name) + local ok, err = do_unload_module(host, name); + if ok then + (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host }); + end + return ok, err; +end + +function reload(host, name) + local ok, err = do_reload_module(host, name); + if ok then + (hosts[host] or prosody).events.fire_event("module-reloaded", { module = name, host = host }); + elseif not is_loaded(host, name) then + (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host }); end return ok, err; end +function get_module(host, name) + return modulemap[host] and modulemap[host][name]; +end + +function get_modules(host) + return modulemap[host]; +end + +function is_loaded(host, name) + return modulemap[host] and modulemap[host][name] and true; +end + function module_has_method(module, method) return type(module.module[method]) == "function"; end -- cgit v1.2.3 From 4595904c7580d6ac38f9e659da4c9d9b255526b0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:47:33 +0000 Subject: modulemanager: Drop unnecessary prosody_events local --- core/modulemanager.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 1846e0f0..9f90e34e 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -15,7 +15,6 @@ local pluginloader = require "util.pluginloader"; local hosts = hosts; local prosody = prosody; -local prosody_events = prosody.events; local loadfile, pcall, xpcall = loadfile, pcall, xpcall; local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv; @@ -87,8 +86,7 @@ function load_modules_for_host(host) load(host, module); end end -prosody_events.add_handler("host-activated", load_modules_for_host); --- +prosody.events.add_handler("host-activated", load_modules_for_host); --- Private helpers --- -- cgit v1.2.3 From f2a5abecac32a7095f1f72e24337e669ba04036c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:49:11 +0000 Subject: modulemanager, moduleapi: Replace hooks multitable with an event_handlers map stored in individual modules. Also adds module:hook_object_event() to hook events on any util.events compatible object. --- core/moduleapi.lua | 11 +++++++---- core/modulemanager.lua | 22 +++++----------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index ee960709..3a28ec62 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -66,14 +66,17 @@ function api:fire_event(...) return (hosts[self.host] or prosody).events.fire_event(...); end +function api:hook_object_event(object, event, handler, priority) + self.event_handlers[handler] = { name = event, priority = priority, object = object }; + return object.add_handler(event, handler, priority); +end + function api:hook(event, handler, priority) - hooks:set(self.host, self.name, event, handler, true); - (hosts[self.host] or prosody).events.add_handler(event, handler, priority); + return self:hook_object_event((hosts[self.host] or prosody).events, event, handler, priority); end function api:hook_global(event, handler, priority) - hooks:set("*", self.name, event, handler, true); - prosody.events.add_handler(event, handler, priority); + return self:hook_object_event(prosody.events, event, handler, priority); end function api:hook_stanza(xmlns, name, handler, priority) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 9f90e34e..f86298c9 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -48,10 +48,6 @@ local api = _G.require "core.moduleapi"; -- Module API container local modulemap = { ["*"] = {} }; -local modulehelpers = setmetatable({}, { __index = _G }); - -local hooks = multitable_new(); - local NULL = {}; -- Load modules when a host is activated @@ -100,19 +96,11 @@ local function do_unload_module(host, name) log("warn", "Non-fatal error unloading module '%s' on '%s': %s", name, host, err); end end - -- unhook event handlers hooked by module:hook - for event, handlers in pairs(hooks:get(host, name) or NULL) do - for handler in pairs(handlers or NULL) do - (hosts[host] or prosody).events.remove_handler(event, handler); - end - end - -- unhook event handlers hooked by module:hook_global - for event, handlers in pairs(hooks:get("*", name) or NULL) do - for handler in pairs(handlers or NULL) do - prosody.events.remove_handler(event, handler); - end + + for handler, event in pairs(mod.module.event_handlers) do + event.object.remove_handler(event.name, handler); 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 @@ -153,7 +141,7 @@ local function do_load_module(host, module_name) local _log = logger.init(host..":"..module_name); local api_instance = setmetatable({ name = module_name, host = host, path = err, - _log = _log, log = function (self, ...) return _log(...); end } + _log = _log, log = function (self, ...) return _log(...); end, event_handlers = {} } , { __index = api }); local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); -- cgit v1.2.3 From ef3a0660f4e37357db269ed0b39d31b298b9e9a1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:49:49 +0000 Subject: modulemanager: Cleanup some unused variables, imports, whitespace and add a comment. --- core/modulemanager.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f86298c9..a6d1ad35 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -9,8 +9,6 @@ local logger = require "util.logger"; local log = logger.init("modulemanager"); local config = require "core.configmanager"; -local multitable_new = require "util.multitable".new; -local st = require "util.stanza"; local pluginloader = require "util.pluginloader"; local hosts = hosts; @@ -46,6 +44,7 @@ module "modulemanager" local api = _G.require "core.moduleapi"; -- Module API container +-- [host] = { [module] = module_env } local modulemap = { ["*"] = {} }; local NULL = {}; @@ -185,7 +184,6 @@ local function do_reload_module(host, name) end local saved; - if module_has_method(mod, "save") then local ok, ret, err = call_module_method(mod, "save"); if ok then -- cgit v1.2.3 From 89adfeefbfb954272bd53a172c07e3633186198e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 19:10:13 +0000 Subject: util.helpers: After nearly 'fixing' this code, I conclude it instead only deserves a bigger smile --- util/helpers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/helpers.lua b/util/helpers.lua index 11356176..a8c8c612 100644 --- a/util/helpers.lua +++ b/util/helpers.lua @@ -28,7 +28,7 @@ function log_events(events, name, logger) end function revert_log_events(events) - events.fire_event, events[events.fire_event] = events[events.fire_event], nil; -- :) + events.fire_event, events[events.fire_event] = events[events.fire_event], nil; -- :)) end function get_upvalue(f, get_name) -- cgit v1.2.3 From 7780d6690a22585ae15d11691d15712269d37f57 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 19:27:06 +0000 Subject: modulemanager: load(): Return and use the correct module object --- core/modulemanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index a6d1ad35..a192e637 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -170,7 +170,7 @@ local function do_load_module(host, module_name) log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); do_unload_module(api_instance.host, module_name); -- Ignore error, module may be partially-loaded end - return ok and mod, err; + return ok and pluginenv, err; end local function do_reload_module(host, name) @@ -219,7 +219,7 @@ end function load(host, name) local mod, err = do_load_module(host, name); if mod then - (hosts[mod.host] or prosody).events.fire_event("module-loaded", { module = module_name, host = host }); + (hosts[mod.module.host] or prosody).events.fire_event("module-loaded", { module = name, host = host }); end return mod, err; end -- cgit v1.2.3 From 94327cb7b23a75cbec35f8fc9a18dca3b044dd6e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 19:35:50 +0000 Subject: moduleapi: Add module:depends(), a way to safely depend upon another module at runtime --- core/moduleapi.lua | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 3a28ec62..44ae9a07 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -100,6 +100,35 @@ function api:require(lib) return f(); end +function api:depends(name) + if not self.dependencies then + self.dependencies = {}; + self:hook("module-reloaded", function (event) + if self.dependencies[event.module] then + self:log("info", "Auto-reloading due to reload of %s:%s", event.host, event.module); + modulemanager.reload(self.host, self.name); + return; + end + end); + self:hook("module-unloaded", function (event) + if self.dependencies[event.module] then + self:log("info", "Auto-unloading due to unload of %s:%s", event.host, event.module); + modulemanager.unload(self.host, self.name); + end + end); + end + local mod = modulemanager.get_module(self.host, name) or modulemanager.get_module("*", name); + if not mod then + local err; + mod, err = modulemanager.load(self.host, name); + if not mod then + return error(("Unable to load required module, mod_%s: %s"):format(name, ((err or "unknown error"):gsub("%-", " ")) )); + end + end + self.dependencies[name] = true; + return mod; +end + function api:get_option(name, default_value) local value = config.get(self.host, self.name, name); if value == nil then -- cgit v1.2.3 From befc1f6162b68bdd6cdbdb9a35cdad9fc39de31f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 19:48:53 +0000 Subject: moduleapi: Add module:shared(), a way to easily share data between multiple loaded modules --- core/moduleapi.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 44ae9a07..7a4d1fa6 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -129,6 +129,29 @@ function api:depends(name) return mod; end +-- Returns one or more shared tables at the specified virtual paths +-- Intentionally does not allow the table at a path to be _set_, it +-- is auto-created if it does not exist. +function api:shared(...) + local paths = { n = select("#", ...), ... }; + local data_array = {}; + local default_path_components = { self.host, self.name }; + for i = 1, paths.n do + local path = paths[i]; + if path:sub(1,1) ~= "/" then -- Prepend default components + local n_components = select(2, path:gsub("/", "%1")); + path = (n_components<#default_path_components and "/" or "")..t_concat(default_path_components, "/", 1, #default_path_components-n_components).."/"..path; + end + local shared = shared_data[path]; + if not shared then + shared = {}; + shared_data[path] = shared; + end + t_insert(data_array, shared); + end + return unpack(data_array); +end + function api:get_option(name, default_value) local value = config.get(self.host, self.name, name); if value == nil then -- cgit v1.2.3 From 075f0cbd7c732c108e03ec8596e9934db7b8b06a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 19:50:08 +0000 Subject: mod_admin_telnet: Use module:shared() to expose commands table and default console environment --- plugins/mod_admin_telnet.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index b211d271..b118e27a 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -21,12 +21,10 @@ local jid_bare = require "util.jid".bare; local set, array = require "util.set", require "util.array"; local cert_verify_identity = require "util.x509".verify_identity; -local commands = {}; -local def_env = {}; +local commands = module:shared("commands") +local def_env = module:shared("env"); local default_env_mt = { __index = def_env }; -prosody.console = { commands = commands, env = def_env }; - local function redirect_output(_G, session) local env = setmetatable({ print = session.print }, { __index = function (t, k) return rawget(_G, k); end }); env.dofile = function(name) -- cgit v1.2.3 From 346ca73cc20ec8f286be470b1f4f5910f5216b8c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 23:55:48 +0000 Subject: portmanager: One manager to, in the darkness, bind them --- core/portmanager.lua | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 core/portmanager.lua diff --git a/core/portmanager.lua b/core/portmanager.lua new file mode 100644 index 00000000..a460cd6a --- /dev/null +++ b/core/portmanager.lua @@ -0,0 +1,132 @@ + +local multitable = require "util.multitable"; +local fire_event = prosody.events.fire_event; + +--- Config + +local default_interfaces = { "*" }; +local default_local_interfaces = { "127.0.0.1" }; +if config.get("*", "use_ipv6") then + table.insert(default_interfaces, "::"); + table.insert(default_local_interfaces, "::1"); +end + +--- Private state + +-- service_name -> service_info +local services = {}; + +-- service_name, interface (string), port (number) +local active_services = multitable.new(); + +--- Private helpers + +local function error_to_friendly_message(service_name, err) + local friendly_message = err; + if err:match(" in use") then + -- FIXME: Use service_name here + 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 + return friendly_message; +end + +module("portmanager", package.seeall); + +--- Public API + +function activate_service(service_name) + local service_info = services[service_name]; + if not service_info then + return nil, "Unknown service: "..service_name; + end + + local bind_interfaces = set.new(config.get("*", service_name.."_interfaces") + or config.get("*", service_name.."_interface") -- COMPAT w/pre-0.9 + or config.get("*", "interfaces") + or config.get("*", "interface") -- COMPAT w/pre-0.9 + or (service_info.private and default_local_interfaces) + or service_info.default_interface -- COMPAT w/pre0.9 + or default_interfaces); + + local bind_ports = set.new(config.get("*", service_name.."_ports") + or (service_info.multiplex and config.get("*", "ports")) + or service_info.default_ports + or {service_info.default_port}); + + local listener = service_info.listener; + local mode = listener.default_mode or "*a"; + local ssl; + if service_info.encryption == "ssl" then + ssl = prosody.global_ssl_ctx; + if not ssl then + return nil, "global-ssl-context-required"; + end + end + + for interface in bind_interfaces do + for port in bind_ports do + if not service_info.multiplex and #active_services:search(nil, interface, port) > 0 then + log("error", "Multiple services configured to listen on the same port: %s, %s", table.concat(active_services:search(nil, interface, port), ", "), service_name); + else + local handler, err = server.addserver(interface, port, listener, mode, ssl); + if not handler then + log("error", "Failed to open server port %d on %s, %s", port, interface, error_to_friendly_message(service_name, err)); + else + log("debug", "Added listening service %s to [%s]:%d", service_name, interface, port); + active_services:add(service_name, interface, port, { + server = handler; + service = service_info; + }); + end + end + end + end + log("info", "Activated service '%s'", service_name); +end + +function deactivate(service_name) + local active = active_services:search(service_name)[1]; + if not active then return; end + for interface, ports in pairs(active) do + for port, active_service in pairs(ports) do + active_service:close(); + active_services:remove(service_name, interface, port, active_service); + log("debug", "Removed listening service %s from [%s]:%d", service_name, interface, port); + end + end + log("info", "Deactivated service '%s'", service_name); +end + +function register_service(service_name, service_info) + services[service_name] = service_info; + + if not active_services[service_name] then + activate_service(service_name); + end + + fire_event("service-added", { name = service_name, service = service_info }); + return true; +end + + +return _M; -- cgit v1.2.3 From 70b25deb380e7ce510cb9ff1b0be2cd579d28692 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 23:57:13 +0000 Subject: mod_c2s, sessionmanager, xmppclient_listener: Move all c2s network and stream logic into a new module, mod_c2s --- core/sessionmanager.lua | 49 ---------- net/xmppclient_listener.lua | 179 ----------------------------------- plugins/mod_c2s.lua | 224 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+), 228 deletions(-) delete mode 100644 net/xmppclient_listener.lua create mode 100644 plugins/mod_c2s.lua diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index b1ec819f..17c718dc 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -6,11 +6,8 @@ -- COPYING file in the source package for more information. -- - - local tonumber, tostring, setmetatable = tonumber, tostring, setmetatable; local ipairs, pairs, print, next= ipairs, pairs, print, next; -local format = string.format; local hosts = hosts; local full_sessions = full_sessions; @@ -19,10 +16,8 @@ local bare_sessions = bare_sessions; local logger = require "util.logger"; local log = logger.init("sessionmanager"); local error = error; -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 nodeprep = require "util.encodings".stringprep.nodeprep; @@ -216,50 +211,6 @@ function bind_resource(session, resource) return true; end -function streamopened(session, attr) - local send = session.send; - session.host = attr.to; - if not session.host then - session:close{ condition = "improper-addressing", - text = "A 'to' attribute is required on stream headers" }; - return; - end - session.host = nameprep(session.host); - session.version = tonumber(attr.version) or 0; - session.streamid = uuid_generate(); - (session.log or session)("debug", "Client sent opening to %s", session.host); - - if not hosts[session.host] then - -- We don't serve this host... - session:close{ condition = "host-unknown", text = "This server does not serve "..tostring(session.host)}; - return; - end - - send(""); - send(format("", session.streamid, session.host)); - - (session.log or log)("debug", "Sent reply to client"); - session.notopen = nil; - - -- If session.secure is *false* (not nil) then it means we /were/ encrypting - -- since we now have a new stream header, session is secured - if session.secure == false then - session.secure = true; - end - - local features = st.stanza("stream:features"); - hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); - fire_event("stream-features", session, features); - - send(features); - -end - -function streamclosed(session) - session.log("debug", "Received "); - session:close(); -end - function send_to_available_resources(user, host, stanza) local jid = user.."@"..host; local count = 0; diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua deleted file mode 100644 index 4cc90cbf..00000000 --- a/net/xmppclient_listener.lua +++ /dev/null @@ -1,179 +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. --- - - - -local logger = require "logger"; -local log = logger.init("xmppclient_listener"); -local new_xmpp_stream = require "util.xmppstream".new; - -local connlisteners_register = require "net.connlisteners".register; - -local sessionmanager = require "core.sessionmanager"; -local sm_new_session, sm_destroy_session = sessionmanager.new_session, sessionmanager.destroy_session; -local sm_streamopened = sessionmanager.streamopened; -local sm_streamclosed = sessionmanager.streamclosed; -local st = require "util.stanza"; -local xpcall = xpcall; -local tostring = tostring; -local type = type; -local traceback = debug.traceback; - -local config = require "core.configmanager"; -local opt_keepalives = config.get("*", "core", "tcp_keepalives"); - -local stream_callbacks = { default_ns = "jabber:client", - streamopened = sm_streamopened, streamclosed = sm_streamclosed, handlestanza = core_process_stanza }; - -local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; - -function stream_callbacks.error(session, error, data) - if error == "no-stream" then - session.log("debug", "Invalid opening stream header"); - session:close("invalid-namespace"); - elseif error == "parse-error" then - (session.log or log)("debug", "Client XML parse error: %s", tostring(data)); - session:close("not-well-formed"); - elseif error == "stream-error" then - local condition, text = "undefined-condition"; - for child in data:children() do - if child.attr.xmlns == xmlns_xmpp_streams then - if child.name ~= "text" then - condition = child.name; - else - text = child:get_text(); - end - if condition ~= "undefined-condition" and text then - break; - end - end - end - text = condition .. (text and (" ("..text..")") or ""); - session.log("info", "Session closed by remote with error: %s", text); - session:close(nil, text); - end -end - -local function handleerr(err) log("error", "Traceback[c2s]: %s: %s", tostring(err), traceback()); end -function stream_callbacks.handlestanza(session, stanza) - stanza = session.filter("stanzas/in", stanza); - if stanza then - return xpcall(function () return core_process_stanza(session, stanza) end, handleerr); - end -end - -local sessions = {}; -local xmppclient = { default_port = 5222, default_mode = "*a" }; - --- These are session methods -- - -local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; -local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; -local function session_close(session, reason) - local log = session.log or log; - if session.conn then - if session.notopen then - session.send(""); - session.send(st.stanza("stream:stream", default_stream_attr):top_tag()); - end - if reason then - if type(reason) == "string" then -- assume stream error - log("info", "Disconnecting client, is: %s", reason); - session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' })); - elseif type(reason) == "table" then - if reason.condition then - local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up(); - if reason.text then - stanza:tag("text", stream_xmlns_attr):text(reason.text):up(); - end - if reason.extra then - stanza:add_child(reason.extra); - end - log("info", "Disconnecting client, is: %s", tostring(stanza)); - session.send(stanza); - elseif reason.name then -- a stanza - log("info", "Disconnecting client, is: %s", tostring(reason)); - session.send(reason); - end - end - end - session.send(""); - session.conn:close(); - xmppclient.ondisconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); - end -end - - --- End of session methods -- - -function xmppclient.onconnect(conn) - local session = sm_new_session(conn); - sessions[conn] = session; - - session.log("info", "Client connected"); - - -- Client is using legacy SSL (otherwise mod_tls sets this flag) - if conn:ssl() then - session.secure = true; - end - - if opt_keepalives ~= nil then - conn:setoption("keepalive", opt_keepalives); - end - - session.close = session_close; - - local stream = new_xmpp_stream(session, stream_callbacks); - session.stream = stream; - - session.notopen = true; - - function session.reset_stream() - session.notopen = true; - session.stream:reset(); - end - - local filter = session.filter; - function session.data(data) - data = filter("bytes/in", data); - if data then - local ok, err = stream:feed(data); - if ok then return; end - log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); - session:close("not-well-formed"); - end - end - - local handlestanza = stream_callbacks.handlestanza; - function session.dispatch_stanza(session, stanza) - return handlestanza(session, stanza); - end -end - -function xmppclient.onincoming(conn, data) - local session = sessions[conn]; - if session then - session.data(data); - end -end - -function xmppclient.ondisconnect(conn, err) - local session = sessions[conn]; - if session then - (session.log or log)("info", "Client disconnected: %s", err); - sm_destroy_session(session, err); - sessions[conn] = nil; - session = nil; - end -end - -function xmppclient.associate_session(conn, session) - sessions[conn] = session; -end - -connlisteners_register("xmppclient", xmppclient); diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua new file mode 100644 index 00000000..2fbed1c9 --- /dev/null +++ b/plugins/mod_c2s.lua @@ -0,0 +1,224 @@ +-- 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:set_global(); + +local new_xmpp_stream = require "util.xmppstream".new; +local nameprep = require "util.encodings".stringprep.nameprep; +local portmanager = require "core.portmanager"; +local sessionmanager = require "core.sessionmanager"; +local st = require "util.stanza"; +local sm_new_session, sm_destroy_session = sessionmanager.new_session, sessionmanager.destroy_session; +local uuid_generate = require "util.uuid".generate; + +local xpcall, tostring, type = xpcall, tostring, type; +local format = string.format; +local traceback = debug.traceback; + +local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; + +local log = module._log; + +local opt_keepalives = module:get_option_boolean("tcp_keepalives", false); + +local sessions = module:shared("sessions"); + +local stream_callbacks = { default_ns = "jabber:client", handlestanza = core_process_stanza }; +local listener = { default_port = 5222, default_mode = "*a" }; + +--- Stream events handlers +local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; +local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; + +function stream_callbacks.streamopened(session, attr) + local send = session.send; + session.host = attr.to; + if not session.host then + session:close{ condition = "improper-addressing", + text = "A 'to' attribute is required on stream headers" }; + return; + end + session.host = nameprep(session.host); + session.version = tonumber(attr.version) or 0; + session.streamid = uuid_generate(); + (session.log or session)("debug", "Client sent opening to %s", session.host); + + if not hosts[session.host] then + -- We don't serve this host... + session:close{ condition = "host-unknown", text = "This server does not serve "..tostring(session.host)}; + return; + end + + send(""); + send(format("", session.streamid, session.host)); + + (session.log or log)("debug", "Sent reply to client"); + session.notopen = nil; + + -- If session.secure is *false* (not nil) then it means we /were/ encrypting + -- since we now have a new stream header, session is secured + if session.secure == false then + session.secure = true; + end + + local features = st.stanza("stream:features"); + hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); + module:fire_event("stream-features", session, features); + + send(features); +end + +function stream_callbacks.streamclosed(session) + session.log("debug", "Received "); + session:close(); +end + +function stream_callbacks.error(session, error, data) + if error == "no-stream" then + session.log("debug", "Invalid opening stream header"); + session:close("invalid-namespace"); + elseif error == "parse-error" then + (session.log or log)("debug", "Client XML parse error: %s", tostring(data)); + session:close("not-well-formed"); + elseif error == "stream-error" then + local condition, text = "undefined-condition"; + for child in data:children() do + if child.attr.xmlns == xmlns_xmpp_streams then + if child.name ~= "text" then + condition = child.name; + else + text = child:get_text(); + end + if condition ~= "undefined-condition" and text then + break; + end + end + end + text = condition .. (text and (" ("..text..")") or ""); + session.log("info", "Session closed by remote with error: %s", text); + session:close(nil, text); + end +end + +local function handleerr(err) log("error", "Traceback[c2s]: %s: %s", tostring(err), traceback()); end +function stream_callbacks.handlestanza(session, stanza) + stanza = session.filter("stanzas/in", stanza); + if stanza then + return xpcall(function () return core_process_stanza(session, stanza) end, handleerr); + end +end + +--- Session methods +local function session_close(session, reason) + local log = session.log or log; + if session.conn then + if session.notopen then + session.send(""); + session.send(st.stanza("stream:stream", default_stream_attr):top_tag()); + end + if reason then + if type(reason) == "string" then -- assume stream error + log("info", "Disconnecting client, is: %s", reason); + session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' })); + elseif type(reason) == "table" then + if reason.condition then + local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up(); + if reason.text then + stanza:tag("text", stream_xmlns_attr):text(reason.text):up(); + end + if reason.extra then + stanza:add_child(reason.extra); + end + log("info", "Disconnecting client, is: %s", tostring(stanza)); + session.send(stanza); + elseif reason.name then -- a stanza + log("info", "Disconnecting client, is: %s", tostring(reason)); + session.send(reason); + end + end + end + session.send(""); + session.conn:close(); + listener.ondisconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); + end +end + +--- Port listener +function listener.onconnect(conn) + local session = sm_new_session(conn); + sessions[conn] = session; + + session.log("info", "Client connected"); + + -- Client is using legacy SSL (otherwise mod_tls sets this flag) + if conn:ssl() then + session.secure = true; + end + + if opt_keepalives then + conn:setoption("keepalive", opt_keepalives); + end + + session.close = session_close; + + local stream = new_xmpp_stream(session, stream_callbacks); + session.stream = stream; + session.notopen = true; + + function session.reset_stream() + session.notopen = true; + session.stream:reset(); + end + + local filter = session.filter; + function session.data(data) + data = filter("bytes/in", data); + if data then + local ok, err = stream:feed(data); + if ok then return; end + log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); + session:close("not-well-formed"); + end + end + + session.dispatch_stanza = stream_callbacks.handlestanza; +end + +function listener.onincoming(conn, data) + local session = sessions[conn]; + if session then + session.data(data); + end +end + +function listener.ondisconnect(conn, err) + local session = sessions[conn]; + if session then + (session.log or log)("info", "Client disconnected: %s", err); + sm_destroy_session(session, err); + sessions[conn] = nil; + session = nil; + end +end + +function listener.associate_session(conn, session) + sessions[conn] = session; +end + +portmanager.register_service("c2s", { + listener = listener; + default_port = 5222; + encryption = "starttls"; +}); + +portmanager.register_service("legacy_ssl", { + listener = listener; + encryption = "ssl"; +}); + + -- cgit v1.2.3 From 72f03580e8be39593ed6a37c8630e572807f0864 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 23:59:19 +0000 Subject: util.set: Accept nil to add_list() --- util/set.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/util/set.lua b/util/set.lua index e4cc2dff..050446ec 100644 --- a/util/set.lua +++ b/util/set.lua @@ -82,8 +82,10 @@ function new(list) end function set:add_list(list) - for _, item in ipairs(list) do - items[item] = true; + if list then + for _, item in ipairs(list) do + items[item] = true; + end end end -- cgit v1.2.3 From a37f067c905d472aba0d70130718660f1470163e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 00:00:10 +0000 Subject: util.iterators: Make a standard library (no longer injects into global namespace) --- util/iterators.lua | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/util/iterators.lua b/util/iterators.lua index aa0b172b..fb89f4a5 100644 --- a/util/iterators.lua +++ b/util/iterators.lua @@ -8,8 +8,10 @@ --[[ Iterators ]]-- +local it = {}; + -- Reverse an iterator -function reverse(f, s, var) +function it.reverse(f, s, var) local results = {}; -- First call the normal iterator @@ -34,12 +36,12 @@ end local function _keys_it(t, key) return (next(t, key)); end -function keys(t) +function it.keys(t) return _keys_it, t; end -- Iterate only over values in a table -function values(t) +function it.values(t) local key, val; return function (t) key, val = next(t, key); @@ -48,7 +50,7 @@ function values(t) end -- Given an iterator, iterate only over unique items -function unique(f, s, var) +function it.unique(f, s, var) local set = {}; return function () @@ -65,7 +67,7 @@ function unique(f, s, var) end --[[ Return the number of items an iterator returns ]]-- -function count(f, s, var) +function it.count(f, s, var) local x = 0; while true do @@ -79,7 +81,7 @@ function count(f, s, var) end -- Return the first n items an iterator returns -function head(n, f, s, var) +function it.head(n, f, s, var) local c = 0; return function (s, var) if c >= n then @@ -91,7 +93,7 @@ function head(n, f, s, var) end -- Skip the first n items an iterator returns -function skip(n, f, s, var) +function it.skip(n, f, s, var) for i=1,n do var = f(s, var); end @@ -99,7 +101,7 @@ function skip(n, f, s, var) end -- Return the last n items an iterator returns -function tail(n, f, s, var) +function it.tail(n, f, s, var) local results, count = {}, 0; while true do local ret = { f(s, var) }; @@ -121,13 +123,13 @@ function tail(n, f, s, var) end local function _range_iter(max, curr) if curr < max then return curr + 1; end end -function range(x, y) +function it.range(x, y) if not y then x, y = 1, x; end -- Default to 1..x if y not given return _range_iter, y, x-1; end -- Convert the values returned by an iterator to an array -function it2array(f, s, var) +function it.to_array(f, s, var) local t, var = {}; while true do var = f(s, var); @@ -139,7 +141,7 @@ end -- Treat the return of an iterator as key,value pairs, -- and build a table -function it2table(f, s, var) +function it.to_table(f, s, var) local t, var2 = {}; while true do var, var2 = f(s, var); @@ -149,3 +151,4 @@ function it2table(f, s, var) return t; end +return it; -- cgit v1.2.3 From 335e19315e20464e6290c0d92f8d8b87bb485361 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 00:07:15 +0000 Subject: portmanager: Pass port to friendly_error_message() --- core/portmanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index a460cd6a..353c4a89 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -21,7 +21,7 @@ local active_services = multitable.new(); --- Private helpers -local function error_to_friendly_message(service_name, err) +local function error_to_friendly_message(service_name, port, err) local friendly_message = err; if err:match(" in use") then -- FIXME: Use service_name here @@ -90,7 +90,7 @@ function activate_service(service_name) else local handler, err = server.addserver(interface, port, listener, mode, ssl); if not handler then - log("error", "Failed to open server port %d on %s, %s", port, interface, error_to_friendly_message(service_name, err)); + log("error", "Failed to open server port %d on %s, %s", port, interface, error_to_friendly_message(service_name, port, err)); else log("debug", "Added listening service %s to [%s]:%d", service_name, interface, port); active_services:add(service_name, interface, port, { -- cgit v1.2.3 From 3e28afcd6c5a926b2d46b9e09d21a39dae882019 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 00:11:01 +0000 Subject: prosody: Remove connlisteners, and net_activate_ports --- prosody | 72 ----------------------------------------------------------------- 1 file changed, 72 deletions(-) diff --git a/prosody b/prosody index e18b4e61..385997a6 100755 --- a/prosody +++ b/prosody @@ -254,63 +254,6 @@ function init_global_state() local global_ssl_ctx = certmanager.create_context("*", "server"); prosody.global_ssl_ctx = global_ssl_ctx; - local cl = require "net.connlisteners"; - function prosody.net_activate_ports(option, listener, default, conntype) - conntype = conntype or (global_ssl_ctx and "tls") or "tcp"; - local ports_option = option and option.."_ports" or "ports"; - if not cl.get(listener) then return; end - local ports = config.get("*", "core", ports_option) or default; - if type(ports) == "number" then ports = {ports} end; - - if type(ports) ~= "table" then - log("error", "core."..ports_option.." is not a table"); - else - for _, port in ipairs(ports) do - port = tonumber(port); - if type(port) ~= "number" then - log("error", "Non-numeric "..ports_option..": "..tostring(port)); - else - local ok, errors = cl.start(listener, { - ssl = conntype == "ssl" and global_ssl_ctx, - port = port, - interface = (option and config.get("*", "core", option.."_interface")) - or cl.get(listener).default_interface - or config.get("*", "core", "interface"), - type = conntype - }); - if not ok then - for addr, err in pairs(errors) do - 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 on %s, %s", port, addr, friendly_message); - end - end - end - end - end - end end function read_version() @@ -359,7 +302,6 @@ function load_secondary_libraries() if remdebug then remdebug.engine.start() end ]] - require "net.connlisteners"; require "net.httpserver"; require "util.stanza" @@ -374,20 +316,6 @@ function prepare_to_start() log("info", "Prosody is using the %s backend for connection handling", server.get_backend()); -- Signal to modules that we are ready to start prosody.events.fire_event("server-starting"); - - -- start listening on sockets - if config.get("*", "core", "ports") then - prosody.net_activate_ports(nil, "multiplex", {5222, 5269}); - if config.get("*", "core", "ssl_ports") then - prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl"); - end - else - prosody.net_activate_ports("c2s", "xmppclient", {5222}); - prosody.net_activate_ports("s2s", "xmppserver", {5269}); - prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp"); - prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl"); - end - prosody.start_time = os.time(); end -- cgit v1.2.3 From 308cf1c32f9145ed83160014a090f01a36f02720 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 00:56:57 +0000 Subject: sessionmanager, mod_c2s: Move timeout logic to mod_c2s --- core/sessionmanager.lua | 10 ---------- plugins/mod_c2s.lua | 11 +++++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 17c718dc..276cd8f5 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -28,8 +28,6 @@ local gettime = require "socket".gettime; local st = require "util.stanza"; -local c2s_timeout = config_get("*", "core", "c2s_timeout"); - local newproxy = newproxy; local getmetatable = getmetatable; @@ -62,14 +60,6 @@ function new_session(conn) session.ip = conn:ip(); local conn_name = "c2s"..tostring(conn):match("[a-f0-9]+$"); session.log = logger.init(conn_name); - - if c2s_timeout then - add_task(c2s_timeout, function () - if session.type == "c2s_unauthed" then - session:close("connection-timeout"); - end - end); - end return session; end diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 2fbed1c9..4d7318a5 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -8,6 +8,7 @@ module:set_global(); +local add_task = require "util.timer".add_task; local new_xmpp_stream = require "util.xmppstream".new; local nameprep = require "util.encodings".stringprep.nameprep; local portmanager = require "core.portmanager"; @@ -24,6 +25,7 @@ local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; local log = module._log; +local c2s_timeout = module:get_option_number("c2s_timeout"); local opt_keepalives = module:get_option_boolean("tcp_keepalives", false); local sessions = module:shared("sessions"); @@ -186,6 +188,15 @@ function listener.onconnect(conn) end end + + if c2s_timeout then + add_task(c2s_timeout, function () + if session.type == "c2s_unauthed" then + session:close("connection-timeout"); + end + end); + end + session.dispatch_stanza = stream_callbacks.handlestanza; end -- cgit v1.2.3 From 925ea11f71ab1794a727d0a4563fea50e4d94e53 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 01:04:15 +0000 Subject: prosody: sandboxed require(): Point __index of _G at current env for modules that need to reference globals they already set --- prosody | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/prosody b/prosody index 385997a6..f79910b7 100755 --- a/prosody +++ b/prosody @@ -150,10 +150,14 @@ function sandbox_require() local curr_env_mt = getmetatable(getfenv(2)); local _realG_mt = getmetatable(_realG); if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then - local old_newindex + local old_newindex, old_index; old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env; + old_index, _realG_mt.__index = _realG_mt.__index, function (_G, k) + return rawget(curr_env, k); + end; local ret = _real_require(...); _realG_mt.__newindex = old_newindex; + _realG_mt.__index = old_index; return ret; end return _real_require(...); -- cgit v1.2.3 From 38859b26f93776a9f105953b4b05312194202b4d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 01:05:32 +0000 Subject: mod_admin_telnet: Port to portmanager --- plugins/mod_admin_telnet.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index b118e27a..9e9065bb 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -12,9 +12,8 @@ local _G = _G; local prosody = _G.prosody; local hosts = prosody.hosts; -local connlisteners_register = require "net.connlisteners".register; -local console_listener = { default_port = 5582; default_mode = "*l"; default_interface = "127.0.0.1" }; +local console_listener = { default_port = 5582; default_mode = "*l"; interface = "127.0.0.1" }; require "util.iterators"; local jid_bare = require "util.jid".bare; @@ -147,8 +146,6 @@ function console_listener.ondisconnect(conn, err) end end -connlisteners_register('console', console_listener); - -- Console commands -- -- These are simple commands, not valid standalone in Lua @@ -776,4 +773,7 @@ if option and option ~= "short" and option ~= "full" and option ~= "graphic" the end end -prosody.net_activate_ports("console", "console", {5582}, "tcp"); +require "core.portmanager".register_service("console", { + listener = console_listener; + default_port = 5582; +}); -- cgit v1.2.3 From c366bf57d997088226d0f9307dfbc8b5e7d7de48 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 02:15:28 +0000 Subject: mod_c2s: Code reduction --- plugins/mod_c2s.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 4d7318a5..682c4e3b 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -39,13 +39,12 @@ local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/strea function stream_callbacks.streamopened(session, attr) local send = session.send; - session.host = attr.to; + session.host = nameprep(attr.to); if not session.host then session:close{ condition = "improper-addressing", - text = "A 'to' attribute is required on stream headers" }; + text = "A valid 'to' attribute is required on stream headers" }; return; end - session.host = nameprep(session.host); session.version = tonumber(attr.version) or 0; session.streamid = uuid_generate(); (session.log or session)("debug", "Client sent opening to %s", session.host); -- cgit v1.2.3 From e2b553bd2a4f946b827bac15c0220b506ef90756 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 02:23:23 +0000 Subject: sessionmanager: Require uuid_generate() --- core/sessionmanager.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 276cd8f5..c101bf4e 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -20,6 +20,7 @@ local rm_load_roster = require "core.rostermanager".load_roster; local config_get = require "core.configmanager".get; local resourceprep = require "util.encodings".stringprep.resourceprep; local nodeprep = require "util.encodings".stringprep.nodeprep; +local uuid_generate = require "util.uuid".generate; local initialize_filters = require "util.filters".initialize; local fire_event = prosody.events.fire_event; -- cgit v1.2.3 From 8d6b12845ccaa811bf917059e5c2a1d55d5164f8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 16:24:06 +0000 Subject: stanza_router: Remove obsolete commented code --- core/stanza_router.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/stanza_router.lua b/core/stanza_router.lua index 406ad2f0..4a30279d 100644 --- a/core/stanza_router.lua +++ b/core/stanza_router.lua @@ -105,11 +105,6 @@ function core_process_stanza(origin, stanza) stanza.attr.from = from; end - --[[if to and not(hosts[to]) and not(hosts[to_bare]) and (hosts[host] and hosts[host].type ~= "local") then -- not for us? - log("warn", "stanza recieved for a non-local server"); - return; -- FIXME what should we do here? - end]] -- FIXME - if (origin.type == "s2sin" or origin.type == "c2s" or origin.type == "component") and xmlns == nil then if origin.type == "s2sin" and not origin.dummy then local host_status = origin.hosts[from_host]; -- cgit v1.2.3 From 6605de32125051a501e4d8d3bf0c088404160159 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 16:25:21 +0000 Subject: stanza_router: Replace s2s send logic with firing of a 'route/remote' event --- core/stanza_router.lua | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/core/stanza_router.lua b/core/stanza_router.lua index 4a30279d..54c5a1a6 100644 --- a/core/stanza_router.lua +++ b/core/stanza_router.lua @@ -11,7 +11,6 @@ local log = require "util.logger".init("stanzarouter") local hosts = _G.prosody.hosts; local tostring = tostring; local st = require "util.stanza"; -local send_s2s = require "core.s2smanager".send_to_host; local jid_split = require "util.jid".split; local jid_prepped_split = require "util.jid".prepped_split; @@ -184,26 +183,18 @@ function core_route_stanza(origin, stanza) if hosts[host] then -- old stanza routing code removed core_post_stanza(origin, stanza); - elseif origin.type == "c2s" then - -- Remote host + else + log("debug", "Routing to remote..."); if not hosts[from_host] then log("error", "No hosts[from_host] (please report): %s", tostring(stanza)); - end - if (not hosts[from_host]) or (not hosts[from_host].disallow_s2s) then + else local xmlns = stanza.attr.xmlns; - --stanza.attr.xmlns = "jabber:server"; stanza.attr.xmlns = nil; - log("debug", "sending s2s stanza: %s", tostring(stanza.top_tag and stanza:top_tag()) or stanza); - send_s2s(origin.host, host, stanza); -- TODO handle remote routing errors + local routed = prosody.events.fire_event("route/remote", { origin = origin, stanza = stanza, from_host = from_host, to_host = host }); --FIXME: Should be per-host (shared modules!) stanza.attr.xmlns = xmlns; -- reset - else - core_route_stanza(hosts[from_host], st.error_reply(stanza, "cancel", "not-allowed", "Communication with remote servers is not allowed")); + if routed == nil then + core_route_stanza(hosts[from_host], st.error_reply(stanza, "cancel", "not-allowed", "Communication with remote domains is not enabled")); + end end - elseif origin.type == "component" or origin.type == "local" then - -- Route via s2s for components and modules - log("debug", "Routing outgoing stanza for %s to %s", from_host, host); - send_s2s(from_host, host, stanza); - else - log("warn", "received %s stanza from unhandled connection type: %s", tostring(stanza.name), tostring(origin.type)); end end -- cgit v1.2.3 From 68bf2a395659bf458efe9e49e4c8abed90fd084f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 16:28:20 +0000 Subject: s2smanager, mod_s2s, mod_s2s/s2sout: Split connection handling out of s2smanager into mod_s2s, and further split connection logic for s2sout to a module lib, s2sout.lib.lua --- core/s2smanager.lua | 581 +-------------------------------------------- plugins/s2s/mod_s2s.lua | 447 ++++++++++++++++++++++++++++++++++ plugins/s2s/s2sout.lib.lua | 314 ++++++++++++++++++++++++ 3 files changed, 772 insertions(+), 570 deletions(-) create mode 100644 plugins/s2s/mod_s2s.lua create mode 100644 plugins/s2s/s2sout.lib.lua diff --git a/core/s2smanager.lua b/core/s2smanager.lua index f44921c3..e61aaccb 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -9,18 +9,13 @@ local hosts = hosts; -local sessions = sessions; local core_process_stanza = function(a, b) core_process_stanza(a, b); end -local add_task = require "util.timer".add_task; -local socket = require "socket"; local format = string.format; local t_insert, t_sort = table.insert, table.sort; local get_traceback = debug.traceback; local tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable = tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable; -local idna_to_ascii = require "util.encodings".idna.to_ascii; -local connlisteners_get = require "net.connlisteners".get; local initialize_filters = require "util.filters".initialize; local wrapclient = require "net.server".wrapclient; local st = require "stanza"; @@ -41,11 +36,13 @@ local sha256_hash = require "util.hashes".sha256; local adns, dns = require "net.adns", require "net.dns"; local config = require "core.configmanager"; -local connect_timeout = config.get("*", "core", "s2s_timeout") or 60; local dns_timeout = config.get("*", "core", "dns_timeout") or 15; local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; +local cfg_sources = config.get("*", "core", "s2s_interface") + or config.get("*", "core", "interface"); local sources; +--FIXME: s2sout should create its own resolver w/ timeout dns.settimeout(dns_timeout); local prosody = _G.prosody; @@ -55,89 +52,6 @@ local incoming_s2s = incoming_s2s; module "s2smanager" -function compare_srv_priorities(a,b) - return a.priority < b.priority or (a.priority == b.priority and a.weight > b.weight); -end - -local bouncy_stanzas = { message = true, presence = true, iq = true }; -local function bounce_sendq(session, reason) - local sendq = session.sendq; - if sendq then - session.log("info", "sending error replies for "..#sendq.." queued stanzas because of failed outgoing connection to "..tostring(session.to_host)); - local dummy = { - type = "s2sin"; - send = function(s) - (session.log or log)("error", "Replying to to an s2s error reply, please report this! Traceback: %s", get_traceback()); - end; - dummy = true; - }; - 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"}) - :tag("remote-server-not-found", {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(); - end - core_process_stanza(dummy, reply); - end - sendq[i] = nil; - end - session.sendq = nil; - end -end - -function send_to_host(from_host, to_host, data) - if not hosts[from_host] then - log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host); - return false; - end - local host = hosts[from_host].s2sout[to_host]; - if host then - -- We have a connection to this host already - if host.type == "s2sout_unauthed" and (data.name ~= "db:verify" or not host.dialback_key) then - (host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host); - - -- Queue stanza until we are able to send it - if host.sendq then t_insert(host.sendq, {tostring(data), data.attr.type ~= "error" and data.attr.type ~= "result" and st.reply(data)}); - else host.sendq = { {tostring(data), data.attr.type ~= "error" and data.attr.type ~= "result" and st.reply(data)} }; end - host.log("debug", "stanza [%s] queued ", data.name); - elseif host.type == "local" or host.type == "component" then - log("error", "Trying to send a stanza to ourselves??") - log("error", "Traceback: %s", get_traceback()); - log("error", "Stanza: %s", tostring(data)); - return false; - else - (host.log or log)("debug", "going to send stanza to "..to_host.." from "..from_host); - -- FIXME - if host.from_host ~= from_host then - log("error", "WARNING! This might, possibly, be a bug, but it might not..."); - log("error", "We are going to send from %s instead of %s", tostring(host.from_host), tostring(from_host)); - end - host.sends2s(data); - host.log("debug", "stanza sent over "..host.type); - end - else - log("debug", "opening a new outgoing connection for this stanza"); - local host_session = new_outgoing(from_host, to_host); - - -- Store in buffer - host_session.sendq = { {tostring(data), data.attr.type ~= "error" and data.attr.type ~= "result" and st.reply(data)} }; - log("debug", "stanza [%s] queued until connection complete", tostring(data.name)); - if (not host_session.connecting) and (not host_session.conn) then - log("warn", "Connection to %s failed already, destroying session...", to_host); - if not destroy_session(host_session, "Connection failed") then - -- Already destroyed, we need to bounce our stanza - bounce_sendq(host_session, host_session.destruction_reason); - end - return false; - end - end - return true; -end - local open_sessions = 0; function new_incoming(conn) @@ -147,491 +61,18 @@ function new_incoming(conn) getmetatable(session.trace).__gc = function () open_sessions = open_sessions - 1; end; end open_sessions = open_sessions + 1; - local w, log = conn.write, logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$")); - session.log = log; - local filter = initialize_filters(session); - session.sends2s = function (t) - log("debug", "sending: %s", t.top_tag and t:top_tag() or t:match("^([^>]*>?)")); - if t.name then - t = filter("stanzas/out", t); - end - if t then - t = filter("bytes/out", tostring(t)); - if t then - return w(conn, t); - end - end - end + session.log = logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$")); incoming_s2s[session] = true; - add_task(connect_timeout, function () - if session.conn ~= conn or - session.type == "s2sin" then - return; -- Ok, we're connect[ed|ing] - end - -- Not connected, need to close session and clean up - (session.log or log)("debug", "Destroying incomplete session %s->%s due to inactivity", - session.from_host or "(unknown)", session.to_host or "(unknown)"); - session:close("connection-timeout"); - end); return session; end function new_outgoing(from_host, to_host, connect) - local host_session = { to_host = to_host, from_host = from_host, host = from_host, - notopen = true, type = "s2sout_unauthed", direction = "outgoing", - open_stream = session_open_stream }; - - hosts[from_host].s2sout[to_host] = host_session; - - host_session.close = destroy_session; -- This gets replaced by xmppserver_listener later - - local log; - do - local conn_name = "s2sout"..tostring(host_session):match("[a-f0-9]*$"); - log = logger_init(conn_name); - host_session.log = log; - end - - initialize_filters(host_session); - - if connect ~= false then - -- Kick the connection attempting machine into life - if not attempt_connection(host_session) then - -- Intentionally not returning here, the - -- session is needed, connected or not - destroy_session(host_session); - end - 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", to_host); - buffer[#buffer+1] = data; - log("debug", "Buffered item %d: %s", #buffer, tostring(data)); - end - end - - return host_session; -end - - -function attempt_connection(host_session, err) - local from_host, to_host = host_session.from_host, host_session.to_host; - local connect_host, connect_port = to_host and idna_to_ascii(to_host), 5269; - - 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; - local handle; - handle = adns.lookup(function (answer) - handle = nil; - host_session.connecting = nil; - if answer then - log("debug", to_host.." has SRV records, handling..."); - local srv_hosts = {}; - host_session.srv_hosts = srv_hosts; - 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", to_host.." does not provide a XMPP service"); - 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", to_host.." has no SRV records, falling back to A"); - end - -- Try with SRV, or just the plain hostname if no SRV - local ok, err = try_connect(host_session, connect_host, connect_port); - if not ok then - if not attempt_connection(host_session, err) then - -- No more attempts will be made - 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 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", tostring(err), host_session.srv_choice, connect_host, connect_port); - else - host_session.log("info", "Out of connection options, can't connect to %s", tostring(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 :(", tostring(connect_host), tostring(connect_port), tostring(to_host)); - return false; - end - - return try_connect(host_session, connect_host, connect_port); -end - -function 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= make_connect(host_session, ip.ip, ip.port); - if not ok then - if not attempt_connection(host_session, err or "closed") then - err = err and (": "..err) or ""; - destroy_session(host_session, "Connection failed"..err); - end - end -end - -function try_connect(host_session, connect_host, connect_port, err) - host_session.connecting = true; - - if not err then - local IPs = {}; - host_session.ip_hosts = IPs; - local handle4, handle6; - local has_other = false; - - if not sources then - sources = {}; - local cfg_sources = config.get("*", "core", "interface") or connlisteners_get("xmppserver").default_interface; - if type(cfg_sources) == "string" then - cfg_sources = { cfg_sources }; - end - for i, source in ipairs(cfg_sources) do - if source == "*" then - sources[i] = new_ip("0.0.0.0", "IPv4"); - else - sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); - end - end - end - - handle4 = adns.lookup(function (reply, err) - handle4 = nil; - - -- COMPAT: This is a compromise for all you CNAME-(ab)users :) - if not (reply and reply[#reply] and reply[#reply].a) then - local count = max_dns_depth; - reply = dns.peek(connect_host, "CNAME", "IN"); - while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do - log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); - reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); - count = count - 1; - end - end - -- end of CNAME resolving - - 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 - end - - if has_other then - if #IPs > 0 then - rfc3484_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; - 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 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 ""; - destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't - end - end - else - has_other = true; - end - end, connect_host, "A", "IN"); - - handle6 = adns.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 - end - - if has_other then - if #IPs > 0 then - rfc3484_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; - 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 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 ""; - destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't - end - end - else - has_other = true; - end - end, connect_host, "AAAA", "IN"); - - 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 - try_next_ip(host_session); - else - host_session.ip_hosts = nil; - if not 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 ""; - destroy_session(host_session, "Connecting failed"..err); -- End of the line, we can't - return false; - end - end - - return true; -end - -function make_connect(host_session, connect_host, connect_port) - (host_session.log or log)("info", "Beginning new connection attempt to %s ([%s]:%d)", host_session.to_host, connect_host.addr, connect_port); - -- Ok, we're going to try to connect - - local from_host, to_host = host_session.from_host, host_session.to_host; - - local conn, handler; - if connect_host.proto == "IPv4" then - conn, handler = socket.tcp(); - else - conn, handler = socket.tcp6(); - 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 - - local cl = connlisteners_get("xmppserver"); - conn = wrapclient(conn, connect_host.addr, connect_port, cl, cl.default_mode or 1 ); - host_session.conn = conn; - - local filter = initialize_filters(host_session); - local w, log = conn.write, host_session.log; - host_session.sends2s = function (t) - log("debug", "sending: %s", (t.top_tag and t:top_tag()) or t:match("^[^>]*>?")); - if t.name then - t = filter("stanzas/out", t); - end - if t then - t = filter("bytes/out", tostring(t)); - if t then - return w(conn, tostring(t)); - end - end - end - - -- Register this outgoing connection so that xmppserver_listener knows about it - -- otherwise it will assume it is a new incoming connection - cl.register_outgoing(conn, host_session); - - host_session:open_stream(from_host, to_host); - - log("debug", "Connection attempt in progress..."); - add_task(connect_timeout, function () - if host_session.conn ~= conn or - host_session.type == "s2sout" or - host_session.connecting then - return; -- Ok, we're connect[ed|ing] - end - -- Not connected, need to close session and clean up - (host_session.log or log)("warn", "Destroying incomplete session %s->%s due to inactivity", - host_session.from_host or "(unknown)", host_session.to_host or "(unknown)"); - host_session:close("connection-timeout"); - end); - return true; -end - -function session_open_stream(session, from, to) - session.sends2s(st.stanza("stream:stream", { - xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', - ["xmlns:stream"]='http://etherx.jabber.org/streams', - from=from, to=to, version='1.0', ["xml:lang"]='en'}):top_tag()); -end - -local function check_cert_status(session) - local conn = session.conn:socket() - local cert - if conn.getpeercertificate then - cert = conn:getpeercertificate() - end - - if cert then - local chain_valid, errors = conn:getpeerverification() - -- Is there any interest in printing out all/the number of errors here? - if not chain_valid then - (session.log or log)("debug", "certificate chain validation result: invalid"); - session.cert_chain_status = "invalid"; - else - (session.log or log)("debug", "certificate chain validation result: valid"); - session.cert_chain_status = "valid"; - - local host = session.direction == "incoming" and session.from_host or session.to_host - - -- We'll go ahead and verify the asserted identity if the - -- connecting server specified one. - if host then - if cert_verify_identity(host, "xmpp-server", cert) then - session.cert_identity_status = "valid" - else - session.cert_identity_status = "invalid" - end - end - end - end -end - -function streamopened(session, attr) - local send = session.sends2s; - - -- TODO: #29: SASL/TLS on s2s streams - session.version = tonumber(attr.version) or 0; - - -- TODO: Rename session.secure to session.encrypted - if session.secure == false then - session.secure = true; - end - - if session.direction == "incoming" then - -- Send a reply stream header - session.to_host = attr.to and nameprep(attr.to); - session.from_host = attr.from and nameprep(attr.from); - - session.streamid = uuid_gen(); - (session.log or log)("debug", "Incoming s2s received "); - if session.to_host then - if not hosts[session.to_host] then - -- Attempting to connect to a host we don't serve - session:close({ - condition = "host-unknown"; - text = "This host does not serve "..session.to_host - }); - return; - elseif hosts[session.to_host].disallow_s2s then - -- Attempting to connect to a host that disallows s2s - session:close({ - condition = "policy-violation"; - text = "Server-to-server communication is not allowed to this host"; - }); - return; - end - end - - if session.secure and not session.cert_chain_status then check_cert_status(session); end - - send(""); - send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', - ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, to=session.from_host, version=(session.version > 0 and "1.0" or nil) }):top_tag()); - if session.version >= 1.0 then - local features = st.stanza("stream:features"); - - if session.to_host then - hosts[session.to_host].events.fire_event("s2s-stream-features", { origin = session, features = features }); - else - (session.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", session.from_host or "unknown host"); - end - - log("debug", "Sending stream features: %s", tostring(features)); - send(features); - end - elseif session.direction == "outgoing" then - -- If we are just using the connection for verifying dialback keys, we won't try and auth it - if not attr.id then error("stream response did not give us a streamid!!!"); end - session.streamid = attr.id; - - if session.secure and not session.cert_chain_status then check_cert_status(session); end - - -- Send unauthed buffer - -- (stanzas which are fine to send before dialback) - -- Note that this is *not* the stanza queue (which - -- we can only send if auth succeeds) :) - local send_buffer = session.send_buffer; - if send_buffer and #send_buffer > 0 then - log("debug", "Sending s2s send_buffer now..."); - for i, data in ipairs(send_buffer) do - session.sends2s(tostring(data)); - send_buffer[i] = nil; - end - end - session.send_buffer = nil; - - -- If server is pre-1.0, don't wait for features, just do dialback - if session.version < 1.0 then - if not session.dialback_verifying then - log("debug", "Initiating dialback..."); - initiate_dialback(session); - else - mark_connected(session); - end - end - end - session.notopen = nil; -end - -function streamclosed(session) - (session.log or log)("debug", "Received "); - session:close(); -end - -function initiate_dialback(session) - -- generate dialback key - session.dialback_key = generate_dialback(session.streamid, session.to_host, session.from_host); - session.sends2s(format("%s", session.from_host, session.to_host, session.dialback_key)); - session.log("info", "sent dialback key on outgoing s2s stream"); -end - -function generate_dialback(id, to, from) - return sha256_hash(id..to..from..hosts[from].dialback_secret, true); -end - -function verify_dialback(id, to, from, key) - return key == generate_dialback(id, to, from); + local host_session = { to_host = to_host, from_host = from_host, host = from_host, + notopen = true, type = "s2sout_unauthed", direction = "outgoing" }; + hosts[from_host].s2sout[to_host] = host_session; + local conn_name = "s2sout"..tostring(host_session):match("[a-f0-9]*$"); + host_session.log = logger_init(conn_name); + return host_session; end function make_authenticated(session, host) @@ -733,7 +174,7 @@ function destroy_session(session, reason) if session.direction == "outgoing" then hosts[session.from_host].s2sout[session.to_host] = nil; - bounce_sendq(session, reason); + session:bounce_sendq(reason); elseif session.direction == "incoming" then incoming_s2s[session] = nil; end diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua new file mode 100644 index 00000000..5be4d84b --- /dev/null +++ b/plugins/s2s/mod_s2s.lua @@ -0,0 +1,447 @@ +-- 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:set_global(); + +local tostring, type = tostring, type; +local xpcall, traceback = xpcall, debug.traceback; + +local add_task = require "util.timer".add_task; +local st = require "util.stanza"; +local initialize_filters = require "util.filters".initialize; +local new_xmpp_stream = require "util.xmppstream".new; +local s2s_new_incoming = require "core.s2smanager".new_incoming; +local s2s_new_outgoing = require "core.s2smanager".new_outgoing; +local s2s_destroy_session = require "core.s2smanager".destroy_session; + +local s2sout = module:require("s2sout"); + +local connect_timeout = module:get_option_number("s2s_timeout", 60); + +local sessions = module:shared("sessions"); + +--- Handle stanzas to remote domains + +local bouncy_stanzas = { message = true, presence = true, iq = true }; +local function bounce_sendq(session, reason) + local sendq = session.sendq; + if not sendq then return; end + session.log("info", "sending error replies for "..#sendq.." queued stanzas because of failed outgoing connection to "..tostring(session.to_host)); + local dummy = { + type = "s2sin"; + send = function(s) + (session.log or log)("error", "Replying to to an s2s error reply, please report this! Traceback: %s", get_traceback()); + end; + dummy = true; + }; + 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"}) + :tag("remote-server-not-found", {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(); + end + core_process_stanza(dummy, reply); + end + sendq[i] = nil; + end + session.sendq = nil; +end + +function send_to_host(from_host, to_host, stanza) + if not hosts[from_host] then + log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host); + return false; + end + local host = hosts[from_host].s2sout[to_host]; + if host then + -- We have a connection to this host already + if host.type == "s2sout_unauthed" and (stanza.name ~= "db:verify" or not host.dialback_key) then + (host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host); + + -- Queue stanza until we are able to send it + if host.sendq then t_insert(host.sendq, {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)}); + else host.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} }; end + host.log("debug", "stanza [%s] queued ", stanza.name); + elseif host.type == "local" or host.type == "component" then + log("error", "Trying to send a stanza to ourselves??") + log("error", "Traceback: %s", get_traceback()); + log("error", "Stanza: %s", tostring(stanza)); + return false; + else + (host.log or log)("debug", "going to send stanza to "..to_host.." from "..from_host); + -- FIXME + if host.from_host ~= from_host then + log("error", "WARNING! This might, possibly, be a bug, but it might not..."); + log("error", "We are going to send from %s instead of %s", tostring(host.from_host), tostring(from_host)); + end + host.sends2s(stanza); + host.log("debug", "stanza sent over "..host.type); + end + else + log("debug", "opening a new outgoing connection for this stanza"); + local host_session = s2s_new_outgoing(from_host, to_host); + + -- 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)); + if (not host_session.connecting) and (not host_session.conn) then + log("warn", "Connection to %s failed already, destroying session...", to_host); + if not s2s_destroy_session(host_session, "Connection failed") then + -- Already destroyed, we need to bounce our stanza + host_session:bounce_sendq(host_session.destruction_reason); + end + return false; + end + s2sout.initiate_connection(host_session); + end + return true; +end + +module:hook("route/remote", function (event) + return send_to_host(event.from_host, event.to_host, event.stanza); +end); + +--- Helper to check that a session peer's certificate is valid +local function check_cert_status(session) + local conn = session.conn:socket() + local cert + if conn.getpeercertificate then + cert = conn:getpeercertificate() + end + + if cert then + local chain_valid, errors = conn:getpeerverification() + -- Is there any interest in printing out all/the number of errors here? + if not chain_valid then + (session.log or log)("debug", "certificate chain validation result: invalid"); + session.cert_chain_status = "invalid"; + else + (session.log or log)("debug", "certificate chain validation result: valid"); + session.cert_chain_status = "valid"; + + local host = session.direction == "incoming" and session.from_host or session.to_host + + -- We'll go ahead and verify the asserted identity if the + -- connecting server specified one. + if host then + if cert_verify_identity(host, "xmpp-server", cert) then + session.cert_identity_status = "valid" + else + session.cert_identity_status = "invalid" + end + end + end + end +end + +--- XMPP stream event handlers + +local stream_callbacks = { default_ns = "jabber:server", handlestanza = core_process_stanza }; + +local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; + +function stream_callbacks.streamopened(session, attr) + local send = session.sends2s; + + -- TODO: #29: SASL/TLS on s2s streams + session.version = tonumber(attr.version) or 0; + + -- TODO: Rename session.secure to session.encrypted + if session.secure == false then + session.secure = true; + end + + if session.direction == "incoming" then + -- Send a reply stream header + session.to_host = attr.to and nameprep(attr.to); + session.from_host = attr.from and nameprep(attr.from); + + session.streamid = uuid_gen(); + (session.log or log)("debug", "Incoming s2s received "); + if session.to_host then + if not hosts[session.to_host] then + -- Attempting to connect to a host we don't serve + session:close({ + condition = "host-unknown"; + text = "This host does not serve "..session.to_host + }); + return; + elseif hosts[session.to_host].disallow_s2s then + -- Attempting to connect to a host that disallows s2s + session:close({ + condition = "policy-violation"; + text = "Server-to-server communication is not allowed to this host"; + }); + return; + end + end + + if session.secure and not session.cert_chain_status then check_cert_status(session); end + + send(""); + send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', + ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, to=session.from_host, version=(session.version > 0 and "1.0" or nil) }):top_tag()); + if session.version >= 1.0 then + local features = st.stanza("stream:features"); + + if session.to_host then + hosts[session.to_host].events.fire_event("s2s-stream-features", { origin = session, features = features }); + else + (session.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", session.from_host or "unknown host"); + end + + log("debug", "Sending stream features: %s", tostring(features)); + send(features); + end + elseif session.direction == "outgoing" then + -- If we are just using the connection for verifying dialback keys, we won't try and auth it + if not attr.id then error("stream response did not give us a streamid!!!"); end + session.streamid = attr.id; + + if session.secure and not session.cert_chain_status then check_cert_status(session); end + + -- Send unauthed buffer + -- (stanzas which are fine to send before dialback) + -- Note that this is *not* the stanza queue (which + -- we can only send if auth succeeds) :) + local send_buffer = session.send_buffer; + if send_buffer and #send_buffer > 0 then + log("debug", "Sending s2s send_buffer now..."); + for i, data in ipairs(send_buffer) do + session.sends2s(tostring(data)); + send_buffer[i] = nil; + end + end + session.send_buffer = nil; + + -- If server is pre-1.0, don't wait for features, just do dialback + if session.version < 1.0 then + if not session.dialback_verifying then + log("debug", "Initiating dialback..."); + initiate_dialback(session); + else + s2s_mark_connected(session); + end + end + end + session.notopen = nil; +end + +function stream_callbacks.streamclosed(session) + (session.log or log)("debug", "Received "); + session:close(); +end + +function stream_callbacks.streamdisconnected(session, err) + if err and err ~= "closed" then + (session.log or log)("debug", "s2s connection attempt failed: %s", err); + if s2sout.attempt_connection(session, err) then + (session.log or log)("debug", "...so we're going to try another target"); + return true; -- Session lives for now + end + end + (session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed")); + sessions[session.conn] = nil; + s2s_destroy_session(session, err); +end + +function stream_callbacks.error(session, error, data) + if error == "no-stream" then + session:close("invalid-namespace"); + elseif error == "parse-error" then + session.log("debug", "Server-to-server XML parse error: %s", tostring(error)); + session:close("not-well-formed"); + elseif error == "stream-error" then + local condition, text = "undefined-condition"; + for child in data:children() do + if child.attr.xmlns == xmlns_xmpp_streams then + if child.name ~= "text" then + condition = child.name; + else + text = child:get_text(); + end + if condition ~= "undefined-condition" and text then + break; + end + end + end + text = condition .. (text and (" ("..text..")") or ""); + session.log("info", "Session closed by remote with error: %s", text); + session:close(nil, text); + end +end + +local function handleerr(err) log("error", "Traceback[s2s]: %s: %s", tostring(err), traceback()); end +function stream_callbacks.handlestanza(session, stanza) + if stanza.attr.xmlns == "jabber:client" then --COMPAT: Prosody pre-0.6.2 may send jabber:client + stanza.attr.xmlns = nil; + end + stanza = session.filter("stanzas/in", stanza); + if stanza then + return xpcall(function () return core_process_stanza(session, stanza) end, handleerr); + end +end + +local listener = { default_port = 5269, default_mode = "*a", default_interface = "*" }; + +--- Session methods +local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; +local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; +local function session_close(session, reason, remote_reason) + local log = session.log or log; + if session.conn then + if session.notopen then + session.sends2s(""); + session.sends2s(st.stanza("stream:stream", default_stream_attr):top_tag()); + end + if reason then + if type(reason) == "string" then -- assume stream error + log("info", "Disconnecting %s[%s], is: %s", session.host or "(unknown host)", session.type, reason); + session.sends2s(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' })); + elseif type(reason) == "table" then + if reason.condition then + local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up(); + if reason.text then + stanza:tag("text", stream_xmlns_attr):text(reason.text):up(); + end + if reason.extra then + stanza:add_child(reason.extra); + end + log("info", "Disconnecting %s[%s], is: %s", session.host or "(unknown host)", session.type, tostring(stanza)); + session.sends2s(stanza); + elseif reason.name then -- a stanza + log("info", "Disconnecting %s->%s[%s], is: %s", session.from_host or "(unknown host)", session.to_host or "(unknown host)", session.type, tostring(reason)); + session.sends2s(reason); + end + end + end + session.sends2s(""); + if session.notopen or not session.conn:close() then + session.conn:close(true); -- Force FIXME: timer? + end + session.conn:close(); + listener.ondisconnect(session.conn, remote_reason or (reason and (reason.text or reason.condition)) or reason or "stream closed"); + end +end + +-- Session initialization logic shared by incoming and outgoing +local function initialize_session(session) + local stream = new_xmpp_stream(session, stream_callbacks); + session.stream = stream; + + session.notopen = true; + + function session.reset_stream() + session.notopen = true; + session.stream:reset(); + end + + local filter = session.filter; + function session.data(data) + data = filter("bytes/in", data); + if data then + local ok, err = stream:feed(data); + if ok then return; end + (session.log or log)("warn", "Received invalid XML: %s", data); + (session.log or log)("warn", "Problem was: %s", err); + session:close("not-well-formed"); + end + end + + session.close = session_close; + + local handlestanza = stream_callbacks.handlestanza; + function session.dispatch_stanza(session, stanza) + return handlestanza(session, stanza); + end + + local conn = session.conn; + add_task(connect_timeout, function () + if session.conn ~= conn or session.connecting + or session.type == "s2sin" or session.type == "s2sout" then + return; -- Ok, we're connect[ed|ing] + end + -- Not connected, need to close session and clean up + (session.log or log)("debug", "Destroying incomplete session %s->%s due to inactivity", + session.from_host or "(unknown)", session.to_host or "(unknown)"); + session:close("connection-timeout"); + end); +end + +function listener.onconnect(conn) + if not sessions[conn] then -- May be an existing outgoing session + local session = s2s_new_incoming(conn); + sessions[conn] = session; + session.log("debug", "Incoming s2s connection"); + + local filter = initialize_filters(session); + local w = conn.write; + session.sends2s = function (t) + log("debug", "sending: %s", t.top_tag and t:top_tag() or t:match("^([^>]*>?)")); + if t.name then + t = filter("stanzas/out", t); + end + if t then + t = filter("bytes/out", tostring(t)); + if t then + return w(conn, t); + end + end + end + + initialize_session(session); + end +end + +function listener.onincoming(conn, data) + local session = sessions[conn]; + if session then + session.data(data); + end +end + +function listener.onstatus(conn, status) + if status == "ssl-handshake-complete" then + local session = sessions[conn]; + if session and session.direction == "outgoing" then + local to_host, from_host = session.to_host, session.from_host; + session.log("debug", "Sending stream header..."); + session:open_stream(session.from_host, session.to_host); + end + end +end + +function listener.ondisconnect(conn, err) + local session = sessions[conn]; + if session then + if stream_callbacks.streamdisconnected(session, err) then + return; -- Connection lives, for now + end + end + sessions[conn] = nil; +end + +function listener.register_outgoing(conn, session) + session.direction = "outgoing"; + sessions[conn] = session; + initialize_session(session); +end + +s2sout.set_listener(listener); + +require "core.portmanager".register_service("s2s", { + listener = listener; + default_port = 5269; + encryption = "starttls"; +}); + diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua new file mode 100644 index 00000000..094126e7 --- /dev/null +++ b/plugins/s2s/s2sout.lib.lua @@ -0,0 +1,314 @@ +-- 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 + +local initialize_filters = require "util.filters".initialize; +local idna_to_ascii = require "util.encodings".idna.to_ascii; +local add_task = require "util.timer".add_task; +local socket = require "socket"; + +local s2s_destroy_session = require "core.s2smanager".destroy_session; + +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 + +local function session_open_stream(session, from, to) + session.sends2s(st.stanza("stream:stream", { + xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', + ["xmlns:stream"]='http://etherx.jabber.org/streams', + from=from, to=to, version='1.0', ["xml:lang"]='en'}):top_tag()); +end + +function s2sout.initiate_connection(host_session) + initialize_filters(host_session); + session.open_stream = session_open_stream; + + -- 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", to_host); + buffer[#buffer+1] = data; + log("debug", "Buffered item %d: %s", #buffer, tostring(data)); + end + end +end + +function s2sout.attempt_connection(host_session, err) + local from_host, to_host = host_session.from_host, host_session.to_host; + local connect_host, connect_port = to_host and idna_to_ascii(to_host), 5269; + + 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; + local handle; + handle = adns.lookup(function (answer) + handle = nil; + host_session.connecting = nil; + if answer then + log("debug", to_host.." has SRV records, handling..."); + local srv_hosts = {}; + host_session.srv_hosts = srv_hosts; + 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", to_host.." does not provide a XMPP service"); + 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", to_host.." has no SRV records, falling back to A"); + 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", tostring(err), host_session.srv_choice, connect_host, connect_port); + else + host_session.log("info", "Out of connection options, can't connect to %s", tostring(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 :(", tostring(connect_host), tostring(connect_port), tostring(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; + + if not err then + local IPs = {}; + host_session.ip_hosts = IPs; + local handle4, handle6; + local has_other = false; + + if not sources then + sources = {}; + for i, source in ipairs(cfg_sources) do + if source == "*" then + sources[i] = new_ip("0.0.0.0", "IPv4"); + else + sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); + end + end + end + + handle4 = adns.lookup(function (reply, err) + handle4 = nil; + + -- COMPAT: This is a compromise for all you CNAME-(ab)users :) + if not (reply and reply[#reply] and reply[#reply].a) then + local count = max_dns_depth; + reply = dns.peek(connect_host, "CNAME", "IN"); + while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do + log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); + reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); + count = count - 1; + end + end + -- end of CNAME resolving + + 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 + end + + if has_other then + if #IPs > 0 then + rfc3484_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 + has_other = true; + end + end, connect_host, "A", "IN"); + + handle6 = adns.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 + end + + if has_other then + if #IPs > 0 then + rfc3484_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 + has_other = true; + end + end, connect_host, "AAAA", "IN"); + + 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 + 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) + (host_session.log or log)("info", "Beginning new connection attempt to %s ([%s]:%d)", host_session.to_host, connect_host.addr, connect_port); + -- Ok, we're going to try to connect + + local from_host, to_host = host_session.from_host, host_session.to_host; + + local conn, handler; + if connect_host.proto == "IPv4" then + conn, handler = socket.tcp(); + else + conn, handler = socket.tcp6(); + 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, "*a"); + host_session.conn = conn; + + local filter = initialize_filters(host_session); + local w, log = conn.write, host_session.log; + host_session.sends2s = function (t) + log("debug", "sending: %s", (t.top_tag and t:top_tag()) or t:match("^[^>]*>?")); + if t.name then + t = filter("stanzas/out", t); + end + if t then + t = filter("bytes/out", tostring(t)); + if t then + return w(conn, tostring(t)); + end + end + end + + -- 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); + + host_session:open_stream(from_host, to_host); + + log("debug", "Connection attempt in progress..."); + return true; +end + +return s2sout; -- cgit v1.2.3 From 07e3cb5a4241a15fe1b5caa1aecad3a7cb920180 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 17:12:05 +0000 Subject: xmppserver_listener: Remove --- net/xmppserver_listener.lua | 209 -------------------------------------------- 1 file changed, 209 deletions(-) delete mode 100644 net/xmppserver_listener.lua diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua deleted file mode 100644 index 048c6c7d..00000000 --- a/net/xmppserver_listener.lua +++ /dev/null @@ -1,209 +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. --- - - -local tostring = tostring; -local type = type; -local xpcall = xpcall; -local s_format = string.format; -local traceback = debug.traceback; - -local logger = require "logger"; -local log = logger.init("xmppserver_listener"); -local st = require "util.stanza"; -local connlisteners_register = require "net.connlisteners".register; -local new_xmpp_stream = require "util.xmppstream".new; -local s2s_new_incoming = require "core.s2smanager".new_incoming; -local s2s_streamopened = require "core.s2smanager".streamopened; -local s2s_streamclosed = require "core.s2smanager".streamclosed; -local s2s_destroy_session = require "core.s2smanager".destroy_session; -local s2s_attempt_connect = require "core.s2smanager".attempt_connection; -local stream_callbacks = { default_ns = "jabber:server", - streamopened = s2s_streamopened, streamclosed = s2s_streamclosed, handlestanza = core_process_stanza }; - -local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; - -function stream_callbacks.error(session, error, data) - if error == "no-stream" then - session:close("invalid-namespace"); - elseif error == "parse-error" then - session.log("debug", "Server-to-server XML parse error: %s", tostring(error)); - session:close("not-well-formed"); - elseif error == "stream-error" then - local condition, text = "undefined-condition"; - for child in data:children() do - if child.attr.xmlns == xmlns_xmpp_streams then - if child.name ~= "text" then - condition = child.name; - else - text = child:get_text(); - end - if condition ~= "undefined-condition" and text then - break; - end - end - end - text = condition .. (text and (" ("..text..")") or ""); - session.log("info", "Session closed by remote with error: %s", text); - session:close(nil, text); - end -end - -local function handleerr(err) log("error", "Traceback[s2s]: %s: %s", tostring(err), traceback()); end -function stream_callbacks.handlestanza(session, stanza) - if stanza.attr.xmlns == "jabber:client" then --COMPAT: Prosody pre-0.6.2 may send jabber:client - stanza.attr.xmlns = nil; - end - stanza = session.filter("stanzas/in", stanza); - if stanza then - return xpcall(function () return core_process_stanza(session, stanza) end, handleerr); - end -end - -local sessions = {}; -local xmppserver = { default_port = 5269, default_mode = "*a", default_interface = "*" }; - --- These are session methods -- - -local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; -local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; -local function session_close(session, reason, remote_reason) - local log = session.log or log; - if session.conn then - if session.notopen then - session.sends2s(""); - session.sends2s(st.stanza("stream:stream", default_stream_attr):top_tag()); - end - if reason then - if type(reason) == "string" then -- assume stream error - log("info", "Disconnecting %s[%s], is: %s", session.host or "(unknown host)", session.type, reason); - session.sends2s(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' })); - elseif type(reason) == "table" then - if reason.condition then - local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up(); - if reason.text then - stanza:tag("text", stream_xmlns_attr):text(reason.text):up(); - end - if reason.extra then - stanza:add_child(reason.extra); - end - log("info", "Disconnecting %s[%s], is: %s", session.host or "(unknown host)", session.type, tostring(stanza)); - session.sends2s(stanza); - elseif reason.name then -- a stanza - log("info", "Disconnecting %s->%s[%s], is: %s", session.from_host or "(unknown host)", session.to_host or "(unknown host)", session.type, tostring(reason)); - session.sends2s(reason); - end - end - end - session.sends2s(""); - if session.notopen or not session.conn:close() then - session.conn:close(true); -- Force FIXME: timer? - end - session.conn:close(); - xmppserver.ondisconnect(session.conn, remote_reason or (reason and (reason.text or reason.condition)) or reason or "stream closed"); - end -end - - --- End of session methods -- - -local function initialize_session(session) - local stream = new_xmpp_stream(session, stream_callbacks); - session.stream = stream; - - session.notopen = true; - - function session.reset_stream() - session.notopen = true; - session.stream:reset(); - end - - local filter = session.filter; - function session.data(data) - data = filter("bytes/in", data); - if data then - local ok, err = stream:feed(data); - if ok then return; end - (session.log or log)("warn", "Received invalid XML: %s", data); - (session.log or log)("warn", "Problem was: %s", err); - session:close("not-well-formed"); - end - end - - session.close = session_close; - local handlestanza = stream_callbacks.handlestanza; - function session.dispatch_stanza(session, stanza) - return handlestanza(session, stanza); - end -end - -function xmppserver.onconnect(conn) - if not sessions[conn] then -- May be an existing outgoing session - local session = s2s_new_incoming(conn); - sessions[conn] = session; - - -- Logging functions -- - local conn_name = "s2sin"..tostring(conn):match("[a-f0-9]+$"); - session.log = logger.init(conn_name); - - session.log("info", "Incoming s2s connection"); - - initialize_session(session); - end -end - -function xmppserver.onincoming(conn, data) - local session = sessions[conn]; - if session then - session.data(data); - end -end - -function xmppserver.onstatus(conn, status) - if status == "ssl-handshake-complete" then - local session = sessions[conn]; - if session and session.direction == "outgoing" then - local to_host, from_host = session.to_host, session.from_host; - session.log("debug", "Sending stream header..."); - session.sends2s(s_format([[]], from_host, to_host)); - end - end -end - -function xmppserver.ondisconnect(conn, err) - local session = sessions[conn]; - if session then - if err and err ~= "closed" and session.type == "s2sout_unauthed" then - (session.log or log)("debug", "s2s connection attempt failed: %s", err); - if s2s_attempt_connect(session, err) then - (session.log or log)("debug", "...so we're going to try another target"); - return; -- Session lives for now - end - end - (session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed")); - s2s_destroy_session(session, err); - sessions[conn] = nil; - session = nil; - end -end - -function xmppserver.register_outgoing(conn, session) - session.direction = "outgoing"; - sessions[conn] = session; - - initialize_session(session); -end - -connlisteners_register("xmppserver", xmppserver); - - --- We need to perform some initialisation when a connection is created --- We also need to perform that same initialisation at other points (SASL, TLS, ...) - --- ...and we need to handle data --- ...and record all sessions associated with connections -- cgit v1.2.3 From e9a6d6dd419bfda9a05f7733583210a7486cf64e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 17:19:16 +0000 Subject: net.http, httpclient_listener: Merge listener into net.http --- net/http.lua | 52 +++++++++++++++++++++++++++++++---- net/httpclient_listener.lua | 66 --------------------------------------------- 2 files changed, 47 insertions(+), 71 deletions(-) delete mode 100644 net/httpclient_listener.lua diff --git a/net/http.lua b/net/http.lua index 6287f408..59f2c080 100644 --- a/net/http.lua +++ b/net/http.lua @@ -13,9 +13,6 @@ local httpstream_new = require "util.httpstream".new; local server = require "net.server" -local connlisteners_get = require "net.connlisteners".get; -local listener = connlisteners_get("httpclient") or error("No httpclient listener!"); - local t_insert, t_concat = table.insert, table.concat; local pairs, ipairs = pairs, ipairs; local tonumber, tostring, xpcall, select, debug_traceback, char, format = @@ -25,6 +22,52 @@ local log = require "util.logger".init("http"); module "http" +local requests = {}; -- Open requests + +local listener = { default_port = 80, default_mode = "*a" }; + +function listener.onconnect(conn) + local req = requests[conn]; + -- Send the request + local request_line = { req.method or "GET", " ", req.path, " HTTP/1.1\r\n" }; + if req.query then + t_insert(request_line, 4, "?"..req.query); + end + + conn:write(t_concat(request_line)); + local t = { [2] = ": ", [4] = "\r\n" }; + for k, v in pairs(req.headers) do + t[1], t[3] = k, v; + conn:write(t_concat(t)); + end + conn:write("\r\n"); + + if req.body then + conn:write(req.body); + end +end + +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)); + return; + end + + if data and request.reader then + request:reader(data); + end +end + +function listener.ondisconnect(conn, err) + local request = requests[conn]; + if request and request.conn then + request:reader(nil); + end + requests[conn] = nil; +end + function urlencode(s) return s and (s:gsub("%W", function (c) return format("%%%02x", c:byte()); end)); end function urldecode(s) return s and (s:gsub("%%(%x%x)", function (c) return char(tonumber(c,16)); end)); end @@ -152,8 +195,7 @@ function request(u, ex, callback) req.reader = request_reader; req.state = "status"; - listener.register_request(req.handler, req); - + requests[req.handler] = req; return req; end diff --git a/net/httpclient_listener.lua b/net/httpclient_listener.lua deleted file mode 100644 index c4e3c153..00000000 --- a/net/httpclient_listener.lua +++ /dev/null @@ -1,66 +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. --- - -local log = require "util.logger".init("httpclient_listener"); -local t_concat, t_insert = table.concat, table.insert; - -local connlisteners_register = require "net.connlisteners".register; - -local requests = {}; -- Open requests -local buffers = {}; -- Buffers of partial lines - -local httpclient = { default_port = 80, default_mode = "*a" }; - -function httpclient.onconnect(conn) - local req = requests[conn]; - -- Send the request - local request_line = { req.method or "GET", " ", req.path, " HTTP/1.1\r\n" }; - if req.query then - t_insert(request_line, 4, "?"..req.query); - end - - conn:write(t_concat(request_line)); - local t = { [2] = ": ", [4] = "\r\n" }; - for k, v in pairs(req.headers) do - t[1], t[3] = k, v; - conn:write(t_concat(t)); - end - conn:write("\r\n"); - - if req.body then - conn:write(req.body); - end -end - -function httpclient.onincoming(conn, data) - local request = requests[conn]; - - if not request then - log("warn", "Received response from connection %s with no request attached!", tostring(conn)); - return; - end - - if data and request.reader then - request:reader(data); - end -end - -function httpclient.ondisconnect(conn, err) - local request = requests[conn]; - if request and request.conn then - request:reader(nil); - end - requests[conn] = nil; -end - -function httpclient.register_request(conn, req) - log("debug", "Attaching request %s to connection %s", tostring(req.id or req), tostring(conn)); - requests[conn] = req; -end - -connlisteners_register("httpclient", httpclient); -- cgit v1.2.3 From 3a34c326b306321454bdae018b93bf3cf0d4875a Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Jan 2012 11:47:51 +0500 Subject: util.xmppstream: Remove some unnecessary code. --- util/xmppstream.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/util/xmppstream.lua b/util/xmppstream.lua index 0f80742d..adf4a359 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -151,8 +151,6 @@ function new_sax_handlers(session, stream_callbacks) end cb_error(session, "parse-error", "unexpected-element-close", name); end - stanza, chardata = nil, {}; - stack = {}; end end -- cgit v1.2.3 From ea7586b3634a22191995ff589a72b32eddc32e3b Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Jan 2012 11:49:27 +0500 Subject: util.xmppstream: Have faith in the XML parser matching start and end tags. --- util/xmppstream.lua | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/util/xmppstream.lua b/util/xmppstream.lua index adf4a359..09198f21 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -140,16 +140,8 @@ function new_sax_handlers(session, stream_callbacks) stanza = t_remove(stack); end else - if tagname == stream_tag then - if cb_streamclosed then - cb_streamclosed(session); - end - else - local curr_ns,name = tagname:match(ns_pattern); - if name == "" then - curr_ns, name = "", curr_ns; - end - cb_error(session, "parse-error", "unexpected-element-close", name); + if cb_streamclosed then + cb_streamclosed(session); end end end -- cgit v1.2.3 From 90e3f561fa6e02b26382d747155765753763a634 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Jan 2012 11:54:12 +0500 Subject: util.xmppstream: Optimize attribute processing. --- util/xmppstream.lua | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/util/xmppstream.lua b/util/xmppstream.lua index 09198f21..f1793b4f 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -25,8 +25,11 @@ module "xmppstream" local new_parser = lxp.new; -local ns_prefixes = { - ["http://www.w3.org/XML/1998/namespace"] = "xml"; +local xml_namespace = { + ["http://www.w3.org/XML/1998/namespace\1lang"] = "xml:lang"; + ["http://www.w3.org/XML/1998/namespace\1space"] = "xml:space"; + ["http://www.w3.org/XML/1998/namespace\1base"] = "xml:base"; + ["http://www.w3.org/XML/1998/namespace\1id"] = "xml:id"; }; local xmlns_streams = "http://etherx.jabber.org/streams"; @@ -73,17 +76,13 @@ function new_sax_handlers(session, stream_callbacks) non_streamns_depth = non_streamns_depth + 1; end - -- FIXME !!!!! for i=1,#attr do local k = attr[i]; attr[i] = nil; - local ns, nm = k:match(ns_pattern); - if nm ~= "" then - ns = ns_prefixes[ns]; - if ns then - attr[ns..":"..nm] = attr[k]; - attr[k] = nil; - end + local xmlk = xml_namespace[k]; + if xmlk then + attr[xmlk] = attr[k]; + attr[k] = nil; end end -- cgit v1.2.3 From cccbf349e4040d88501188515f0c4557795edd53 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 5 Feb 2012 00:06:20 +0500 Subject: util.template: Refactoring to make the string->stanza conversion code more generic. --- util/template.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/util/template.lua b/util/template.lua index ebd8be14..5e9b479e 100644 --- a/util/template.lua +++ b/util/template.lua @@ -7,6 +7,7 @@ local ipairs = ipairs; local error = error; local loadstring = loadstring; local debug = debug; +local t_remove = table.remove; module("template") @@ -42,7 +43,6 @@ local parse_xml = (function() stanza:tag(name, attr); end function handler:CharacterData(data) - data = data:gsub("^%s*", ""):gsub("%s*$", ""); stanza:text(data); end function handler:EndElement(tagname) @@ -60,6 +60,19 @@ local parse_xml = (function() end; end)(); +local function trim_xml(stanza) + for i=#stanza,1,-1 do + local child = stanza[i]; + if child.name then + trim_xml(child); + else + child = child:gsub("^%s*", ""):gsub("%s*$", ""); + stanza[i] = child; + if child == "" then t_remove(stanza, i); end + end + end +end + local function create_string_string(str) str = ("%q"):format(str); str = str:gsub("{([^}]*)}", function(s) @@ -118,6 +131,7 @@ local template_mt = { __tostring = function(t) return t.name end }; local function create_template(templates, text) local stanza, err = parse_xml(text); if not stanza then error(err); end + trim_xml(stanza); local info = debug.getinfo(3, "Sl"); info = info and ("template(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.currentline) or "template(unknown)"; -- cgit v1.2.3 From 86dbec26c8257844a032ac1b53188ef41452c1cb Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 5 Feb 2012 00:10:13 +0500 Subject: tools/ejabberdsql2prosody: Handle INSERT statement form where column list is specified (by skipping the column list). --- tools/ejabberdsql2prosody.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/ejabberdsql2prosody.lua b/tools/ejabberdsql2prosody.lua index 576f4436..c64faee0 100644 --- a/tools/ejabberdsql2prosody.lua +++ b/tools/ejabberdsql2prosody.lua @@ -129,7 +129,12 @@ local function readInsert() end end local tname = readTableName(); - for ch in ("` VALUES "):gmatch(".") do read(ch); end -- expect this + read("`"); read(" ") -- expect this + if peek() == "(" then -- skip column list + repeat until read() == ")"; + read(" "); + end + for ch in ("VALUES "):gmatch(".") do read(ch); end -- expect this local tuples = readTuples(); read(";"); read("\n"); return tname, tuples; -- cgit v1.2.3 From db7c1869ae79edcbd0ebacf61ef90c84bd9ab66c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 22 Feb 2012 23:12:57 +0100 Subject: mod_s2s: Add some missing imports --- plugins/s2s/mod_s2s.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 5be4d84b..219e6952 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -18,6 +18,9 @@ local new_xmpp_stream = require "util.xmppstream".new; local s2s_new_incoming = require "core.s2smanager".new_incoming; local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; +local nameprep = require "util.encodings".stringprep.nameprep; +local uuid_gen = require "util.uuid".generate; +local cert_verify_identity = require "util.x509".verify_identity; local s2sout = module:require("s2sout"); -- cgit v1.2.3 From bf57acbdca6f5d77ebe7eb32474d42c7ce5f85fb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 22 Feb 2012 23:14:21 +0100 Subject: mod_s2s: Fix typo --- plugins/s2s/mod_s2s.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 219e6952..7c0a99de 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -192,7 +192,7 @@ function stream_callbacks.streamopened(session, attr) if session.secure and not session.cert_chain_status then check_cert_status(session); end send(""); - send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', + send(st.stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, to=session.from_host, version=(session.version > 0 and "1.0" or nil) }):top_tag()); if session.version >= 1.0 then local features = st.stanza("stream:features"); -- cgit v1.2.3 From 4db93777bdce477c99228fd690f5d3058ab9b9e7 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 23 Feb 2012 23:03:28 +0100 Subject: mod_s2s: Initiate connections --- plugins/s2s/mod_s2s.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 7c0a99de..bb1a6dd7 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -92,6 +92,7 @@ function send_to_host(from_host, to_host, stanza) else log("debug", "opening a new outgoing connection for this stanza"); local host_session = s2s_new_outgoing(from_host, to_host); + s2sout.initiate_connection(host_session); -- Store in buffer host_session.bounce_sendq = bounce_sendq; -- cgit v1.2.3 From af7783a11409246b295bf56b298018e4ce73caa6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 23 Feb 2012 23:04:59 +0100 Subject: mod_s2s: Attach send function to session --- plugins/s2s/mod_s2s.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index bb1a6dd7..90bedce9 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -192,6 +192,9 @@ function stream_callbacks.streamopened(session, attr) if session.secure and not session.cert_chain_status then check_cert_status(session); end + function session.send(data) + return send_to_host(session.to_host, session.from_host, data); + end send(""); send(st.stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, to=session.from_host, version=(session.version > 0 and "1.0" or nil) }):top_tag()); -- cgit v1.2.3 From 05e087606f691330da8747c70233d22543524c5a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 23 Feb 2012 23:09:09 +0100 Subject: s2sout.lib: Import various util.* --- plugins/s2s/s2sout.lib.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index 094126e7..d84854a8 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -8,12 +8,18 @@ --- Module containing all the logic for connecting to a remote server +local wrapclient = require "net.server".wrapclient; local initialize_filters = require "util.filters".initialize; local idna_to_ascii = require "util.encodings".idna.to_ascii; local add_task = require "util.timer".add_task; +local new_ip = require "util.ip".new_ip; +local rfc3484_dest = require "util.rfc3484".destination; local socket = require "socket"; +local t_insert, t_sort = table.insert, table.sort; +local st = require "util.stanza"; local s2s_destroy_session = require "core.s2smanager".destroy_session; +local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2sout = {}; -- cgit v1.2.3 From afe54f2fbdd6494e198b238f795036211b1467dc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 23 Feb 2012 23:12:08 +0100 Subject: s2sout.lib: Add locals for source interfaces --- plugins/s2s/s2sout.lib.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index d84854a8..04a5c072 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -25,6 +25,9 @@ local s2sout = {}; local s2s_listener; +local cfg_sources = config.get("*", "core", "s2s_interfaces") or {"*"}; +local sources + function s2sout.set_listener(listener) s2s_listener = listener; end -- cgit v1.2.3 From f397b79717e372748b156e4874d0b0a483a2c134 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 23 Feb 2012 23:12:24 +0100 Subject: s2sout.lib: Fix wrong variable name --- plugins/s2s/s2sout.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index 04a5c072..64786862 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -45,7 +45,7 @@ end function s2sout.initiate_connection(host_session) initialize_filters(host_session); - session.open_stream = session_open_stream; + host_session.open_stream = session_open_stream; -- Kick the connection attempting machine into life if not s2sout.attempt_connection(host_session) then -- cgit v1.2.3 From 0699ab44aaa99853e7efb116ae041e83c00fdaba Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 24 Feb 2012 15:14:07 +0000 Subject: modulemanager: include mod_c2s and mod_s2s into autoloaded modules. --- core/modulemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index a192e637..0ca37105 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -34,7 +34,7 @@ end local array, set = require "util.array", require "util.set"; -local autoload_modules = {"presence", "message", "iq", "offline"}; +local autoload_modules = {"presence", "message", "iq", "offline", "c2s", "s2s"}; local component_inheritable_modules = {"tls", "dialback", "iq"}; -- We need this to let modules access the real global namespace -- cgit v1.2.3 From 0c2a6fec148390fc52308086e099f253b762310f Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 24 Feb 2012 15:15:43 +0000 Subject: s2smanager: remove send_to_host. --- core/s2smanager.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index e61aaccb..6877ee18 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -114,10 +114,7 @@ function mark_connected(session) local from, to = session.from_host, session.to_host; session.log("info", session.direction.." s2s connection "..from.."->"..to.." complete"); - - local send_to_host = send_to_host; - function session.send(data) return send_to_host(to, from, data); end - + local event_data = { session = session }; if session.type == "s2sout" then prosody.events.fire_event("s2sout-established", event_data); -- cgit v1.2.3 From 61564a3ca0e754fc10b343d92555818f57aa8769 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 24 Feb 2012 15:20:03 +0000 Subject: mod_dialback: import util.hashes and functionality once in s2smanager. --- plugins/mod_dialback.lua | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua index e27f8657..977e58c3 100644 --- a/plugins/mod_dialback.lua +++ b/plugins/mod_dialback.lua @@ -6,22 +6,37 @@ -- COPYING file in the source package for more information. -- +local format = string.format; local hosts = _G.hosts; local send_s2s = require "core.s2smanager".send_to_host; local s2s_make_authenticated = require "core.s2smanager".make_authenticated; -local s2s_initiate_dialback = require "core.s2smanager".initiate_dialback; -local s2s_verify_dialback = require "core.s2smanager".verify_dialback; local log = module._log; local st = require "util.stanza"; +local sha256_hash = require "util.hashes".sha256; local xmlns_stream = "http://etherx.jabber.org/streams"; local xmlns_dialback = "jabber:server:dialback"; local dialback_requests = setmetatable({}, { __mode = 'v' }); +function generate_dialback(id, to, from) + return sha256_hash(id..to..from..hosts[from].dialback_secret, true); +end + +function initiate_dialback(session) + -- generate dialback key + session.dialback_key = generate_dialback(session.streamid, session.to_host, session.from_host); + session.sends2s(format("%s", session.from_host, session.to_host, session.dialback_key)); + session.log("info", "sent dialback key on outgoing s2s stream"); +end + +function verify_dialback(id, to, from, key) + return key == generate_dialback(id, to, from); +end + module:hook("stanza/jabber:server:dialback:verify", function(event) local origin, stanza = event.origin, event.stanza; @@ -32,7 +47,7 @@ module:hook("stanza/jabber:server:dialback:verify", function(event) -- COMPAT: Grr, ejabberd breaks this one too?? it is black and white in XEP-220 example 34 --if attr.from ~= origin.to_host then error("invalid-from"); end local type; - if s2s_verify_dialback(attr.id, attr.from, attr.to, stanza[1]) then + if verify_dialback(attr.id, attr.from, attr.to, stanza[1]) then type = "valid" else type = "invalid" @@ -72,8 +87,8 @@ module:hook("stanza/jabber:server:dialback:result", function(event) end origin.log("debug", "asking %s if key %s belongs to them", attr.from, stanza[1]); - send_s2s(attr.to, attr.from, - st.stanza("db:verify", { from = attr.to, to = attr.from, id = origin.streamid }):text(stanza[1])); + --send_s2s(attr.to, attr.from, + origin.send(st.stanza("db:verify", { from = attr.to, to = attr.from, id = origin.streamid }):text(stanza[1])); return true; end end); @@ -84,6 +99,7 @@ module:hook("stanza/jabber:server:dialback:verify", function(event) if origin.type == "s2sout_unauthed" or origin.type == "s2sout" then local attr = stanza.attr; local dialback_verifying = dialback_requests[attr.from.."/"..(attr.id or "")]; + module:log("debug", tostring(dialback_verifying).." "..attr.from.." "..origin.to_host); if dialback_verifying and attr.from == origin.to_host then local valid; if attr.type == "valid" then @@ -134,14 +150,14 @@ end); module:hook_stanza("urn:ietf:params:xml:ns:xmpp-sasl", "failure", function (origin, stanza) if origin.external_auth == "failed" then module:log("debug", "SASL EXTERNAL failed, falling back to dialback"); - s2s_initiate_dialback(origin); + initiate_dialback(origin); return true; end end, 100); module:hook_stanza(xmlns_stream, "features", function (origin, stanza) if not origin.external_auth or origin.external_auth == "failed" then - s2s_initiate_dialback(origin); + initiate_dialback(origin); return true; end end, 100); -- cgit v1.2.3 From 48f64ab12c78907e4f85bb1c6dc02c297999cc55 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 24 Feb 2012 15:21:21 +0000 Subject: mod_s2s: port functionality once in s2smanager. --- plugins/s2s/mod_s2s.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 5be4d84b..0de821b2 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -14,10 +14,12 @@ local xpcall, traceback = xpcall, debug.traceback; local add_task = require "util.timer".add_task; local st = require "util.stanza"; local initialize_filters = require "util.filters".initialize; +local nameprep = require "util.encodings".stringprep.nameprep; local new_xmpp_stream = require "util.xmppstream".new; local s2s_new_incoming = require "core.s2smanager".new_incoming; local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; +local uuid_gen = require "util.uuid".generate; local s2sout = module:require("s2sout"); @@ -94,6 +96,7 @@ function send_to_host(from_host, to_host, stanza) 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); if not s2s_destroy_session(host_session, "Connection failed") then @@ -102,7 +105,6 @@ function send_to_host(from_host, to_host, stanza) end return false; end - s2sout.initiate_connection(host_session); end return true; end @@ -189,7 +191,7 @@ function stream_callbacks.streamopened(session, attr) if session.secure and not session.cert_chain_status then check_cert_status(session); end send(""); - send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', + send(stanza.stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, to=session.from_host, version=(session.version > 0 and "1.0" or nil) }):top_tag()); if session.version >= 1.0 then local features = st.stanza("stream:features"); @@ -235,6 +237,7 @@ function stream_callbacks.streamopened(session, attr) end end session.notopen = nil; + session.send = function(stanza) send_to_host(session.to_host, session.from_host, stanza); end; end function stream_callbacks.streamclosed(session) -- cgit v1.2.3 From 3552b31a4fac765a6b5bca81f869423079c2073f Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 24 Feb 2012 15:24:10 +0000 Subject: s2sout.lib: import utils/functionality once in s2smanager. --- plugins/s2s/s2sout.lib.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index 094126e7..12669d97 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -8,13 +8,24 @@ --- Module containing all the logic for connecting to a remote server +local t_insert = table.insert; +local t_sort = table.sort; +local ipairs = ipairs; + +local wrapclient = require "net.server".wrapclient; local initialize_filters = require "util.filters".initialize; local idna_to_ascii = require "util.encodings".idna.to_ascii; local add_task = require "util.timer".add_task; +local st = require "util.stanza"; +local new_ip = require "util.ip".new_ip; +local rfc3484_dest = require "util.rfc3484".destination; local socket = require "socket"; +local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; +local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses(); + local s2sout = {}; local s2s_listener; @@ -36,7 +47,7 @@ end function s2sout.initiate_connection(host_session) initialize_filters(host_session); - session.open_stream = session_open_stream; + host_session.open_stream = session_open_stream; -- Kick the connection attempting machine into life if not s2sout.attempt_connection(host_session) then @@ -147,6 +158,7 @@ function s2sout.try_next_ip(host_session) end function s2sout.try_connect(host_session, connect_host, connect_port, err) + local sources; host_session.connecting = true; if not err then @@ -156,7 +168,7 @@ function s2sout.try_connect(host_session, connect_host, connect_port, err) local has_other = false; if not sources then - sources = {}; + sources = {}; for i, source in ipairs(cfg_sources) do if source == "*" then sources[i] = new_ip("0.0.0.0", "IPv4"); -- cgit v1.2.3 From e69011927edb0b979b71b6649be70f0191655616 Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 24 Feb 2012 15:34:25 +0000 Subject: mod_s2s, s2sout.lib: import cert verify and add another fallback method in case socket.local_addresses isn't there. --- plugins/s2s/mod_s2s.lua | 1 + plugins/s2s/s2sout.lib.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 0de821b2..88e8cded 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -20,6 +20,7 @@ local s2s_new_incoming = require "core.s2smanager".new_incoming; local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; local uuid_gen = require "util.uuid".generate; +local cert_verify_identity = require "util.x509".verify_identity; local s2sout = module:require("s2sout"); diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index 12669d97..2856a15c 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -24,7 +24,7 @@ local socket = require "socket"; local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; -local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses(); +local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses and socket.local_addresses() or { "*" }; local s2sout = {}; -- cgit v1.2.3 From b6a8fd9f6b060324897dc9f4f6a80c706e4ba399 Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 24 Feb 2012 15:35:04 +0000 Subject: mod_admin_telnet: make service private. --- plugins/mod_admin_telnet.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 9e9065bb..d3f5543e 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -776,4 +776,5 @@ end require "core.portmanager".register_service("console", { listener = console_listener; default_port = 5582; + private = true; }); -- cgit v1.2.3 From 39a677bc8af8e6e97e896afb72e986ae7cbb9a7e Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 24 Feb 2012 15:36:36 +0000 Subject: mod_s2s: prevent attempting to reconnect when the stream is gracefully closed and fix TB by checking session.conn is set (racy racy?) --- plugins/s2s/mod_s2s.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 88e8cded..d1fdedb3 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -247,7 +247,7 @@ function stream_callbacks.streamclosed(session) end function stream_callbacks.streamdisconnected(session, err) - if err and err ~= "closed" then + if err and err ~= "stream closed" then (session.log or log)("debug", "s2s connection attempt failed: %s", err); if s2sout.attempt_connection(session, err) then (session.log or log)("debug", "...so we're going to try another target"); @@ -255,7 +255,7 @@ function stream_callbacks.streamdisconnected(session, err) end end (session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed")); - sessions[session.conn] = nil; + if session.con then sessions[session.conn] = nil; else (session.log or log)("debug", "stale session's connection already closed"); end s2s_destroy_session(session, err); end -- cgit v1.2.3 From dc4689b625060e77b2c555e71e353719b48d5401 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 24 Feb 2012 16:25:38 +0000 Subject: Backed out changeset aba47e6dff43 --- plugins/s2s/mod_s2s.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index d1fdedb3..88e8cded 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -247,7 +247,7 @@ function stream_callbacks.streamclosed(session) end function stream_callbacks.streamdisconnected(session, err) - if err and err ~= "stream closed" then + if err and err ~= "closed" then (session.log or log)("debug", "s2s connection attempt failed: %s", err); if s2sout.attempt_connection(session, err) then (session.log or log)("debug", "...so we're going to try another target"); @@ -255,7 +255,7 @@ function stream_callbacks.streamdisconnected(session, err) end end (session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed")); - if session.con then sessions[session.conn] = nil; else (session.log or log)("debug", "stale session's connection already closed"); end + sessions[session.conn] = nil; s2s_destroy_session(session, err); end -- cgit v1.2.3 From bbc4c94fc2ff017da68b672e6c9ff4cc628d64ce Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 24 Feb 2012 18:03:27 +0000 Subject: s2smanager, mod_s2s: clear up ip_hosts after s2s is marked as established, remove useless space from mod_s2s code --- core/s2smanager.lua | 1 + plugins/s2s/mod_s2s.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 6877ee18..c05f7229 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -134,6 +134,7 @@ function mark_connected(session) session.sendq = nil; end + session.ip_hosts = nil; session.srv_hosts = nil; end end diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 2573251f..9f522595 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -255,7 +255,7 @@ function stream_callbacks.streamdisconnected(session, err) end end (session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed")); - sessions[session.conn] = nil; + sessions[session.conn] = nil; s2s_destroy_session(session, err); end -- cgit v1.2.3 From d9b9c623c47e173d1194de5d35ab458b93445af6 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 25 Feb 2012 02:03:26 +0000 Subject: s2smanager: Fix traceback when socket.tcp6 isn't available --- core/s2smanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 822713fe..6c880a02 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -432,7 +432,7 @@ function make_connect(host_session, connect_host, connect_port) local conn, handler; if connect_host.proto == "IPv4" then conn, handler = socket.tcp(); - else + elseif socket.tcp6 then conn, handler = socket.tcp6(); end -- cgit v1.2.3 From dce98578a0bd920f0e94b396ba8536835d31c58c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 1 Mar 2012 00:14:53 +0100 Subject: mod_s2s: Add missing local table.insert --- plugins/s2s/mod_s2s.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 9f522595..ec01934d 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -9,6 +9,7 @@ module:set_global(); local tostring, type = tostring, type; +local t_insert = table.insert; local xpcall, traceback = xpcall, debug.traceback; local add_task = require "util.timer".add_task; -- cgit v1.2.3 From 0a43ddb0f8c91982fba926e2d2f4fe9828198e6e Mon Sep 17 00:00:00 2001 From: Thijs Alkemade Date: Fri, 2 Mar 2012 19:56:10 +0100 Subject: Fix make install for mod_s2s. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 0fa8b28e..d94cdf56 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ install: prosody.install prosodyctl.install prosody.cfg.lua.install util/encodin install -d $(SOURCE)/util/sasl install -m644 util/sasl/* $(SOURCE)/util/sasl install -m644 plugins/*.lua $(MODULES) + install -m644 plugins/s2s/*.lua $(MODULES) install -d $(MODULES)/muc install -m644 plugins/muc/* $(MODULES)/muc install -m644 certs/* $(CONFIG)/certs -- cgit v1.2.3 From fc74613190a83799ed7c80c98776f7ce8f65fbdb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 00:01:10 +0100 Subject: mod_dialback: Remove unused import of s2smanager.send_to_host() --- plugins/mod_dialback.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua index 977e58c3..eccfd3d6 100644 --- a/plugins/mod_dialback.lua +++ b/plugins/mod_dialback.lua @@ -9,7 +9,6 @@ local format = string.format; local hosts = _G.hosts; -local send_s2s = require "core.s2smanager".send_to_host; local s2s_make_authenticated = require "core.s2smanager".make_authenticated; local log = module._log; @@ -87,7 +86,6 @@ module:hook("stanza/jabber:server:dialback:result", function(event) end origin.log("debug", "asking %s if key %s belongs to them", attr.from, stanza[1]); - --send_s2s(attr.to, attr.from, origin.send(st.stanza("db:verify", { from = attr.to, to = attr.from, id = origin.streamid }):text(stanza[1])); return true; end -- cgit v1.2.3 From ee772304c50323d20d5ae4bd8b3ebe0e2badf69c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 00:03:06 +0100 Subject: mod_s2s: Split send_to_host() into two route/remote hooks, one for already exsisting sessions and one for non-existent. --- plugins/s2s/mod_s2s.lua | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index ec01934d..c00ff653 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -60,7 +60,8 @@ local function bounce_sendq(session, reason) session.sendq = nil; end -function send_to_host(from_host, to_host, stanza) +module:hook("route/remote", function (event) + local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza; if not hosts[from_host] then log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host); return false; @@ -70,7 +71,7 @@ function send_to_host(from_host, to_host, stanza) -- We have a connection to this host already if host.type == "s2sout_unauthed" and (stanza.name ~= "db:verify" or not host.dialback_key) then (host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host); - + -- Queue stanza until we are able to send it if host.sendq then t_insert(host.sendq, {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)}); else host.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} }; end @@ -90,30 +91,28 @@ function send_to_host(from_host, to_host, stanza) host.sends2s(stanza); host.log("debug", "stanza sent over "..host.type); end - else - log("debug", "opening a new outgoing connection for this stanza"); - local host_session = s2s_new_outgoing(from_host, to_host); - - -- 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); - if not s2s_destroy_session(host_session, "Connection failed") then - -- Already destroyed, we need to bounce our stanza - host_session:bounce_sendq(host_session.destruction_reason); - end - return false; - end end - return true; -end +end, 200); module:hook("route/remote", function (event) - return send_to_host(event.from_host, event.to_host, event.stanza); -end); + 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); + + -- 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); + if not s2s_destroy_session(host_session, "Connection failed") then + -- Already destroyed, we need to bounce our stanza + host_session:bounce_sendq(host_session.destruction_reason); + end + return false; + end +end, 100); --- Helper to check that a session peer's certificate is valid local function check_cert_status(session) @@ -239,7 +238,7 @@ function stream_callbacks.streamopened(session, attr) end end session.notopen = nil; - session.send = function(stanza) send_to_host(session.to_host, session.from_host, stanza); end; + session.send = function(stanza) prosody.events.fire_event("route/remote", { from_host = session.to_host, to_host = session.from_host, stanza = stanza}) end; end function stream_callbacks.streamclosed(session) -- cgit v1.2.3 From 8dd4570ed5e6b89821f66b062c57cbebf187238b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 00:14:48 +0100 Subject: mod_s2s: return true when we sent the stanza, or initiated a new s2sout --- plugins/s2s/mod_s2s.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index c00ff653..de3ad4e1 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -90,6 +90,7 @@ module:hook("route/remote", function (event) end host.sends2s(stanza); host.log("debug", "stanza sent over "..host.type); + return true; end end end, 200); @@ -112,6 +113,7 @@ module:hook("route/remote", function (event) end return false; end + return true; end, 100); --- Helper to check that a session peer's certificate is valid -- cgit v1.2.3 From b7e7de7a209082695416b51761e9bc34835594e1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 00:32:57 +0100 Subject: mod_admin_telnet: Import util.iterators properly --- plugins/mod_admin_telnet.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index d3f5543e..fd72787e 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -15,7 +15,8 @@ local hosts = prosody.hosts; local console_listener = { default_port = 5582; default_mode = "*l"; interface = "127.0.0.1" }; -require "util.iterators"; +local iterators = require "util.iterators"; +local keys, values = iterators.keys, iterators.values; local jid_bare = require "util.jid".bare; local set, array = require "util.set", require "util.array"; local cert_verify_identity = require "util.x509".verify_identity; -- cgit v1.2.3 From f886be3d2c699f58a84b37c2ec51cdd14ebf6fe7 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 00:54:19 +0100 Subject: core.portmanager: Make sure the private flag takes precedence over global interfaces --- core/portmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 353c4a89..3d6dada4 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -62,9 +62,9 @@ function activate_service(service_name) local bind_interfaces = set.new(config.get("*", service_name.."_interfaces") or config.get("*", service_name.."_interface") -- COMPAT w/pre-0.9 + or (service_info.private and default_local_interfaces) or config.get("*", "interfaces") or config.get("*", "interface") -- COMPAT w/pre-0.9 - or (service_info.private and default_local_interfaces) or service_info.default_interface -- COMPAT w/pre0.9 or default_interfaces); -- cgit v1.2.3 From 71528cdc3726be326d6a0afb76f490b7d3c4fcc6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 01:35:39 +0100 Subject: mod_s2s, mod_dialback: Event on pre-XMPP streams, so we can try dialback. --- plugins/mod_dialback.lua | 5 +++++ plugins/s2s/mod_s2s.lua | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua index eccfd3d6..5cb59fef 100644 --- a/plugins/mod_dialback.lua +++ b/plugins/mod_dialback.lua @@ -160,6 +160,11 @@ module:hook_stanza(xmlns_stream, "features", function (origin, stanza) end end, 100); +module:hook("s2s-no-stream-features", function (event) + initiate_dialback(event.origin); + return true; +end, 100); + -- Offer dialback to incoming hosts module:hook("s2s-stream-features", function (data) data.features:tag("dialback", { xmlns='urn:xmpp:features:dialback' }):up(); diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index de3ad4e1..9ab2ee0a 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -233,7 +233,7 @@ function stream_callbacks.streamopened(session, attr) if session.version < 1.0 then if not session.dialback_verifying then log("debug", "Initiating dialback..."); - initiate_dialback(session); + hosts[session.from_host].events.fire_event("s2s-no-stream-features", { origin = session }); else s2s_mark_connected(session); end -- cgit v1.2.3 From cb1eb75a470e9a101aee99a7c465964b79bedfbc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 13:28:33 +0100 Subject: mod_s2s, mod_dialback: Rename event to s2s-authenticate-legacy --- plugins/mod_dialback.lua | 2 +- plugins/s2s/mod_s2s.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua index 5cb59fef..2c2dc795 100644 --- a/plugins/mod_dialback.lua +++ b/plugins/mod_dialback.lua @@ -160,7 +160,7 @@ module:hook_stanza(xmlns_stream, "features", function (origin, stanza) end end, 100); -module:hook("s2s-no-stream-features", function (event) +module:hook("s2s-authenticate-legacy", function (event) initiate_dialback(event.origin); return true; end, 100); diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 9ab2ee0a..bebf1962 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -233,7 +233,7 @@ function stream_callbacks.streamopened(session, attr) if session.version < 1.0 then if not session.dialback_verifying then log("debug", "Initiating dialback..."); - hosts[session.from_host].events.fire_event("s2s-no-stream-features", { origin = session }); + hosts[session.from_host].events.fire_event("s2s-authenticate-legacy", { origin = session }); else s2s_mark_connected(session); end -- cgit v1.2.3 From 3ee4c78cd8116b29687c3b9bdfbb4600db974d8b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 16:45:34 +0100 Subject: mod_dialback, mod_s2s: Log initiation of dialback in mod_dialback --- plugins/mod_dialback.lua | 2 ++ plugins/s2s/mod_s2s.lua | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua index 2c2dc795..e578c412 100644 --- a/plugins/mod_dialback.lua +++ b/plugins/mod_dialback.lua @@ -155,12 +155,14 @@ end, 100); module:hook_stanza(xmlns_stream, "features", function (origin, stanza) if not origin.external_auth or origin.external_auth == "failed" then + module:log("debug", "Initiating dialback..."); initiate_dialback(origin); return true; end end, 100); module:hook("s2s-authenticate-legacy", function (event) + module:log("debug", "Initiating dialback..."); initiate_dialback(event.origin); return true; end, 100); diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index bebf1962..407a7e04 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -232,7 +232,6 @@ function stream_callbacks.streamopened(session, attr) -- If server is pre-1.0, don't wait for features, just do dialback if session.version < 1.0 then if not session.dialback_verifying then - log("debug", "Initiating dialback..."); hosts[session.from_host].events.fire_event("s2s-authenticate-legacy", { origin = session }); else s2s_mark_connected(session); -- cgit v1.2.3 From 5a5adc7aa92c0826d2f200d03e80b02ca75f9573 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 4 Mar 2012 17:39:19 +0100 Subject: mod_s2s: Log the entire stream header. --- plugins/s2s/mod_s2s.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 407a7e04..5e03e896 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -172,7 +172,7 @@ function stream_callbacks.streamopened(session, attr) session.from_host = attr.from and nameprep(attr.from); session.streamid = uuid_gen(); - (session.log or log)("debug", "Incoming s2s received "); + (session.log or log)("debug", "Incoming s2s received %s", st.stanza("stream:stream", attr):top_tag()); if session.to_host then if not hosts[session.to_host] then -- Attempting to connect to a host we don't serve -- cgit v1.2.3 From f340820df2e6828c7f25d072579b7ed1a213ae58 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 5 Mar 2012 11:07:10 +0000 Subject: mod_s2s: streamopened(): Tighter validation around stream 'to' and 'from' attributes, and only set to_host and from_host if they aren't set already and if the session hasn't already been authenticated --- plugins/s2s/mod_s2s.lua | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 407a7e04..fcdd9dd6 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -168,9 +168,33 @@ function stream_callbacks.streamopened(session, attr) if session.direction == "incoming" then -- Send a reply stream header - session.to_host = attr.to and nameprep(attr.to); - session.from_host = attr.from and nameprep(attr.from); - + + -- Validate to/from + local to, from = nameprep(attr.to), nameprep(attr.from); + if not to and attr.to then -- COMPAT: Some servers do not reliably set 'to' (especially on stream restarts) + session:close({ condition = "improper-addressing", text = "Invalid 'to' address" }); + return; + end + if not from and attr.from then -- COMPAT: Some servers do not reliably set 'from' (especially on stream restarts) + session:close({ condition = "improper-addressing", text = "Invalid 'from' address" }); + return; + end + + -- Set session.[from/to]_host if they have not been set already and if + -- this session isn't already authenticated + if session.type == "s2sin_unauthed" and from and not session.from_host then + session.from_host = from; + elseif from ~= session.from_host then + session:close({ condition = "improper-addressing", text = "New stream 'from' attribute does not match original" }); + return; + end + if session.type == "s2sin_unauthed" and to and not session.to_host then + session.to_host = to; + elseif to ~= session.to_host then + session:close({ condition = "improper-addressing", text = "New stream 'to' attribute does not match original" }); + return; + end + session.streamid = uuid_gen(); (session.log or log)("debug", "Incoming s2s received "); if session.to_host then -- cgit v1.2.3 From 3bb6cff7d45ce44efc0fc9fd6163f66026c0967c Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Mon, 5 Mar 2012 16:39:50 +0000 Subject: mod_s2s: remove conn sessions clearing redundancy and leave it only in listener.ondisconnect --- plugins/s2s/mod_s2s.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index fcdd9dd6..ab122f12 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -280,7 +280,6 @@ function stream_callbacks.streamdisconnected(session, err) end end (session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed")); - sessions[session.conn] = nil; s2s_destroy_session(session, err); end @@ -457,7 +456,7 @@ function listener.ondisconnect(conn, err) return; -- Connection lives, for now end end - sessions[conn] = nil; + sessions[conn] = nil; end function listener.register_outgoing(conn, session) -- cgit v1.2.3 From a4652f2478890ee6a86a28f5de5e3e8dcf10f9e7 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 7 Mar 2012 21:12:04 +0100 Subject: mod_admin_adhoc: Import util.iterators properly --- plugins/mod_admin_adhoc.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mod_admin_adhoc.lua b/plugins/mod_admin_adhoc.lua index d78c1aee..a594483c 100644 --- a/plugins/mod_admin_adhoc.lua +++ b/plugins/mod_admin_adhoc.lua @@ -10,7 +10,8 @@ local prosody = _G.prosody; local hosts = prosody.hosts; local t_concat = table.concat; -require "util.iterators"; +local iterators = require "util.iterators"; +local keys, values = iterators.keys, iterators.values; local usermanager_user_exists = require "core.usermanager".user_exists; local usermanager_create_user = require "core.usermanager".create_user; local usermanager_get_password = require "core.usermanager".get_password; -- cgit v1.2.3 From 65b0a00e7cf26917ed00df3c5b3aac8ed0e6fb6d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 7 Mar 2012 21:14:08 +0100 Subject: mod_admin_adhoc: Use module:depends() --- plugins/mod_admin_adhoc.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_admin_adhoc.lua b/plugins/mod_admin_adhoc.lua index a594483c..6f1357a9 100644 --- a/plugins/mod_admin_adhoc.lua +++ b/plugins/mod_admin_adhoc.lua @@ -24,6 +24,7 @@ local dataforms_new = require "util.dataforms".new; local array = require "util.array"; local modulemanager = require "modulemanager"; +module:depends"adhoc"; local adhoc_new = module:require "adhoc".new; function add_user_command_handler(self, data, state) -- cgit v1.2.3 From 91247839773b8514d2097e818271e4e84dfc4362 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 11 Mar 2012 12:40:32 +0000 Subject: portmanager: Add get_service() --- core/portmanager.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index 3d6dada4..f655659d 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -128,5 +128,8 @@ function register_service(service_name, service_info) return true; end +function get_service(service_name) + return services[service_name]; +end return _M; -- cgit v1.2.3 From 9ed71a359e09c1997955346837afbd9749d1e12d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 11 Mar 2012 19:14:28 +0100 Subject: s2smanager, mod_s2s: Move import of dns_max_depth to mod_s2s --- core/s2smanager.lua | 1 - plugins/s2s/s2sout.lib.lua | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index c05f7229..158b5461 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -37,7 +37,6 @@ local sha256_hash = require "util.hashes".sha256; local adns, dns = require "net.adns", require "net.dns"; local config = require "core.configmanager"; local dns_timeout = config.get("*", "core", "dns_timeout") or 15; -local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; local cfg_sources = config.get("*", "core", "s2s_interface") or config.get("*", "core", "interface"); local sources; diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index 808c7e74..4fcb094a 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -23,6 +23,8 @@ local s2s_destroy_session = require "core.s2smanager".destroy_session; local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses and socket.local_addresses() or { "*" }; +local max_dns_depth = module:get_option_number("dns_max_depth", 3); + local s2sout = {}; local s2s_listener; -- cgit v1.2.3 From e7eecd81b727f653c5e3f7ecf3da956dc94cf5eb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 11 Mar 2012 18:35:27 +0000 Subject: portmanager: Add get_active_services() --- core/portmanager.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index f655659d..103c5d44 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -132,4 +132,8 @@ function get_service(service_name) return services[service_name]; end +function get_active_services(...) + return active_services; +end + return _M; -- cgit v1.2.3 From 0348adf69e2c847200976b94e21ca5e63680f350 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Sun, 11 Mar 2012 20:15:42 +0100 Subject: mod_s2s: Remove obsolete default_interface --- plugins/s2s/mod_s2s.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index aa4d5d56..a496af57 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -320,7 +320,7 @@ function stream_callbacks.handlestanza(session, stanza) end end -local listener = { default_port = 5269, default_mode = "*a", default_interface = "*" }; +local listener = { default_port = 5269, default_mode = "*a" }; --- Session methods local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; -- cgit v1.2.3 From 818b91cdf5aea37839a40170551410a743d298b8 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Sun, 11 Mar 2012 20:16:57 +0100 Subject: mod_s2s: Collect s2s sources from portmanager and get local address if necessary --- plugins/s2s/s2sout.lib.lua | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index 4fcb094a..f9f9d362 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -8,6 +8,7 @@ --- Module containing all the logic for connecting to a remote server +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; @@ -21,7 +22,7 @@ local st = require "util.stanza"; local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; -local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses and socket.local_addresses() or { "*" }; +local sources = {}; local max_dns_depth = module:get_option_number("dns_max_depth", 3); @@ -158,7 +159,6 @@ function s2sout.try_next_ip(host_session) end function s2sout.try_connect(host_session, connect_host, connect_port, err) - local sources; host_session.connecting = true; if not err then @@ -167,17 +167,6 @@ function s2sout.try_connect(host_session, connect_host, connect_port, err) local handle4, handle6; local has_other = false; - if not sources then - sources = {}; - for i, source in ipairs(cfg_sources) do - if source == "*" then - sources[i] = new_ip("0.0.0.0", "IPv4"); - else - sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); - end - end - end - handle4 = adns.lookup(function (reply, err) handle4 = nil; @@ -323,4 +312,32 @@ function s2sout.make_connect(host_session, connect_host, connect_port) 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"); + + for source, _ in pairs(s2s_sources) do + if source == "*" or source == "0.0.0.0" then + if not socket.local_addresses then + sources[#sources + 1] = new_ip("0.0.0.0", "IPv4"); + else + for _, addr in ipairs(socket.local_addresses("ipv4", true)) do + sources[#sources + 1] = new_ip(addr, "IPv4"); + end + end + elseif source == "::" then + if not socket.local_addresses then + sources[#sources + 1] = new_ip("::", "IPv4"); + else + for _, addr in ipairs(socket.local_addresses("ipv6", true)) do + sources[#sources + 1] = new_ip(addr, "IPv6"); + end + end + else + sources[#sources + 1] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); + end + end +end); + return s2sout; -- cgit v1.2.3 From dce18041d04aa7a62a749a05341099932d8947f9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 11 Mar 2012 21:37:55 +0100 Subject: mod_s2s: Don't bounce sendq on failed connections since it's handled by s2smanager --- plugins/s2s/mod_s2s.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index a496af57..1fa8ae81 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -107,10 +107,7 @@ module:hook("route/remote", function (event) 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); - if not s2s_destroy_session(host_session, "Connection failed") then - -- Already destroyed, we need to bounce our stanza - host_session:bounce_sendq(host_session.destruction_reason); - end + s2s_destroy_session(host_session, "Connection failed"); return false; end return true; -- cgit v1.2.3 From a4a4365842fd701623fa72d38e9ea8068e0674ce Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 11 Mar 2012 20:56:09 +0000 Subject: mod_auth_internal_{plain,hashed}: Clarify log messages on initialization --- plugins/mod_auth_internal_hashed.lua | 2 +- plugins/mod_auth_internal_plain.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index ee810426..399044ad 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -54,7 +54,7 @@ local iteration_count = 4096; function new_hashpass_provider(host) local provider = { name = "internal_hashed" }; - log("debug", "initializing hashpass authentication provider for host '%s'", host); + log("debug", "initializing internal_hashed authentication provider for host '%s'", host); function provider.test_password(username, password) local credentials = datamanager.load(username, host, "accounts") or {}; diff --git a/plugins/mod_auth_internal_plain.lua b/plugins/mod_auth_internal_plain.lua index 784553ea..93b50351 100644 --- a/plugins/mod_auth_internal_plain.lua +++ b/plugins/mod_auth_internal_plain.lua @@ -23,7 +23,7 @@ local prosody = _G.prosody; function new_default_provider(host) local provider = { name = "internal_plain" }; - log("debug", "initializing default authentication provider for host '%s'", host); + log("debug", "initializing internal_plain authentication provider for host '%s'", host); function provider.test_password(username, password) log("debug", "test password '%s' for user %s at host %s", password, username, module.host); -- cgit v1.2.3 From aef2645ec8a90374fdb869aee40bff7c878d34d5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 17:03:48 +0000 Subject: modulemanager: Use appropriate events object for global modules when firing item-removed on unload --- core/modulemanager.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 0ca37105..328224ae 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -101,11 +101,12 @@ local function do_unload_module(host, name) end if mod.module.items then -- remove items + local events = (host == "*" and prosody.events) or hosts[host].events; 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 = mod.module, item = value}); + events.fire_event("item-removed/"..key, {source = mod.module, item = value}); end end end -- cgit v1.2.3 From 0b6925250291121264bc82a71a0970e2ddc2eab0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:33:15 +0000 Subject: moduleapi: Set module.global = true when module:set_global() is called --- core/moduleapi.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 7a4d1fa6..b11fd7b3 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -50,6 +50,7 @@ function api:set_global() local _log = logger.init("mod_"..self.name); self.log = function (self, ...) return _log(...); end; self._log = _log; + self.global = true; end function api:add_feature(xmlns) -- cgit v1.2.3 From e737bb93b75141942ac4106a622ceaff27684534 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:37:00 +0000 Subject: modulemanager: Some refactoring. Deprecate module.host = "*", modules should call module:set_global() (which has been around since forever) --- core/modulemanager.lua | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 328224ae..f9f3a8b8 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -123,6 +123,7 @@ local function do_load_module(host, module_name) if not modulemap[host] then modulemap[host] = {}; + hosts[host].modules = modulemap[host]; end if modulemap[host][module_name] then @@ -148,8 +149,6 @@ local function do_load_module(host, module_name) api_instance.environment = pluginenv; setfenv(mod, pluginenv); - hosts[host].modules = modulemap[host]; - modulemap[host][module_name] = pluginenv; local ok, err = pcall(mod); if ok then @@ -161,15 +160,18 @@ local function do_load_module(host, module_name) end end - -- Use modified host, if the module set one - if api_instance.host == "*" and host ~= "*" then - modulemap[host][module_name] = nil; - modulemap["*"][module_name] = pluginenv; - api_instance:set_global(); + modulemap[pluginenv.module.host][module_name] = pluginenv; + if pluginenv.module.host == "*" then + if not pluginenv.module.global then -- COMPAT w/pre-0.9 + log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); + api_instance:set_global(); + end + else + hosts[host].modules[module_name] = pluginenv; end - else + end + if not ok then log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); - do_unload_module(api_instance.host, module_name); -- Ignore error, module may be partially-loaded end return ok and pluginenv, err; end -- cgit v1.2.3 From 88ea07a5ef72ee7d65d8e35a40186c7cd169cd41 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:39:02 +0000 Subject: portmanager: Add unregister_service(), and allow multiple services with the same name (they get queued) --- core/portmanager.lua | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 103c5d44..30597628 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -13,8 +13,8 @@ end --- Private state --- service_name -> service_info -local services = {}; +-- service_name -> { service_info, ... } +local services = setmetatable({}, { __index = function (t, k) rawset(t, k, {}); return rawget(t, k); end }); -- service_name, interface (string), port (number) local active_services = multitable.new(); @@ -55,7 +55,7 @@ module("portmanager", package.seeall); --- Public API function activate_service(service_name) - local service_info = services[service_name]; + local service_info = services[service_name][1]; if not service_info then return nil, "Unknown service: "..service_name; end @@ -102,6 +102,7 @@ function activate_service(service_name) end end log("info", "Activated service '%s'", service_name); + return true; end function deactivate(service_name) @@ -118,16 +119,35 @@ function deactivate(service_name) end function register_service(service_name, service_info) - services[service_name] = service_info; + table.insert(services[service_name], service_info); - if not active_services[service_name] then - activate_service(service_name); + if not active_services:get(service_name) then + log("debug", "No active service for %s, activating...", service_name); + local ok, err = activate_service(service_name); + if not ok then + log("error", "Failed to activate service '%s': %s", service_name, err or "unknown error"); + end end fire_event("service-added", { name = service_name, service = service_info }); return true; end +function unregister_service(service_name, service_info) + local service_info_list = services[service_name]; + for i, service in ipairs(service_info_list) do + if service == service_info then + table.remove(service_info_list, i); + end + end + if active_services[service_name] == service_info then + deactivate(service_name); + if #service_info_list > 0 then -- Other services registered with this name + activate(service_name); -- Re-activate with the next available one + end + end +end + function get_service(service_name) return services[service_name]; end -- cgit v1.2.3 From 1ab1ddd3ee3f8a93b2d48d6a93840ea1ddaa6cb9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:39:45 +0000 Subject: portmanager: Support item-added/net-provider (global and shared modules only!) --- core/portmanager.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index 30597628..2870815c 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -52,6 +52,15 @@ end module("portmanager", package.seeall); +prosody.events.add_handler("item-added/net-provider", function (event) + local item = event.item; + register_service(item.name, item); +end); +prosody.events.add_handler("item-removed/net-provider", function (event) + local item = event.item; + unregister_service(item.name, item); +end); + --- Public API function activate_service(service_name) -- cgit v1.2.3 From a156128a90397ec050ca3c2296d6d23e7308f3e6 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:40:14 +0000 Subject: portmanager: Fix log message when multiple services are configured to use the same port --- core/portmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 2870815c..a7ba8a49 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -95,7 +95,7 @@ function activate_service(service_name) for interface in bind_interfaces do for port in bind_ports do if not service_info.multiplex and #active_services:search(nil, interface, port) > 0 then - log("error", "Multiple services configured to listen on the same port: %s, %s", table.concat(active_services:search(nil, interface, port), ", "), service_name); + 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 "", service_name or ""); else local handler, err = server.addserver(interface, port, listener, mode, ssl); if not handler then -- cgit v1.2.3 From 829f9e222487d456c88f9624f613bdd37a9f80dd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:42:08 +0000 Subject: mod_c2s: Use module:add_item() to add the net-provider for portmanager --- plugins/mod_c2s.lua | 6 ++++-- plugins/s2s/mod_s2s.lua | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 682c4e3b..e3557492 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -220,13 +220,15 @@ function listener.associate_session(conn, session) sessions[conn] = session; end -portmanager.register_service("c2s", { +module:add_item("net-provider", { + name = "c2s"; listener = listener; default_port = 5222; encryption = "starttls"; }); -portmanager.register_service("legacy_ssl", { +module:add_item("net-provider", { + name = "legacy_ssl"; listener = listener; encryption = "ssl"; }); diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index a496af57..194364cd 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -467,7 +467,8 @@ end s2sout.set_listener(listener); -require "core.portmanager".register_service("s2s", { +module:add_item("net-provider", { + name = "s2s"; listener = listener; default_port = 5269; encryption = "starttls"; -- cgit v1.2.3 From 16c3e94d0fecaeb4cc91cfe8c1c5cc0832116664 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:43:46 +0000 Subject: mod_s2s/s2sout.lib: Log message instead of traceback when s2s isn't configured for any ports --- plugins/s2s/s2sout.lib.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index f9f9d362..6ee1ca83 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -316,7 +316,10 @@ 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("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 if not socket.local_addresses then -- cgit v1.2.3 From 591bcc96e66cb00ac85d416899a61733d12cb6f0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 23:44:24 +0000 Subject: portmanager: Fire service-removed on unregister --- core/portmanager.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index a7ba8a49..605e1e30 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -155,6 +155,7 @@ function unregister_service(service_name, service_info) activate(service_name); -- Re-activate with the next available one end end + fire_event("service-removed", { name = service_name, service = service_info }); end function get_service(service_name) -- cgit v1.2.3 From 4090846d760a746e67f19eda59becf291608eff2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 02:52:31 +0000 Subject: moduleapi: Add module:provides(), a shortcut to add an item with the current module's name --- core/moduleapi.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index b11fd7b3..2177378f 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -292,4 +292,18 @@ function api:handle_items(type, added_cb, removed_cb, existing) end end +function api:provides(name, item) + if not item then item = self.environment; end + if not item.name then + local item_name = module.name; + -- Strip a provider prefix to find the item name + -- (e.g. "auth_foo" -> "foo" for an auth provider) + if item_name:find(name.."_", 1, true) == 1 then + item_name = item_name:sub(#name+2); + end + item.name = item_name; + end + self:add_item(name, item); +end + return api; -- cgit v1.2.3 From a0549526b82982e1e7ec4a612e3d895868c1dd47 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 02:53:05 +0000 Subject: moduleapi: Add module:send() as an alias for core_post_stanza() from the current host's origin --- core/moduleapi.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 2177378f..a577c07a 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -22,6 +22,7 @@ local tonumber, tostring = tonumber, tostring; local prosody = prosody; local hosts = prosody.hosts; +local core_post_stanza = prosody.core_post_stanza; -- Registry of shared module data local shared_data = setmetatable({}, { __mode = "v" }); @@ -306,4 +307,8 @@ function api:provides(name, item) self:add_item(name, item); end +function api:send(stanza) + return core_post_stanza(hosts[self.host], stanza); +end + return api; -- cgit v1.2.3 From 0ae4e4464d090e98bd7daedae45320222a456599 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 02:56:44 +0000 Subject: portmanager: Allow services to specify their config option prefix --- core/portmanager.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 605e1e30..a87e14eb 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -68,19 +68,23 @@ function activate_service(service_name) if not service_info then return nil, "Unknown service: "..service_name; end + + local config_prefix = (service_info.config_prefix or service_name).."_"; + if config_prefix == "_" then + config_prefix = ""; + end - local bind_interfaces = set.new(config.get("*", service_name.."_interfaces") - or config.get("*", service_name.."_interface") -- COMPAT w/pre-0.9 + local bind_interfaces = set.new(config.get("*", config_prefix.."interfaces") + or config.get("*", config_prefix.."interface") -- COMPAT w/pre-0.9 or (service_info.private and default_local_interfaces) or config.get("*", "interfaces") or config.get("*", "interface") -- COMPAT w/pre-0.9 or service_info.default_interface -- COMPAT w/pre0.9 or default_interfaces); - local bind_ports = set.new(config.get("*", service_name.."_ports") - or (service_info.multiplex and config.get("*", "ports")) or service_info.default_ports or {service_info.default_port}); + local bind_ports = set.new(config.get("*", config_prefix.."ports") local listener = service_info.listener; local mode = listener.default_mode or "*a"; -- cgit v1.2.3 From 917df51c8fd55aae373ddb7e6a6df40b7d336fe8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 03:02:09 +0000 Subject: portmanager: Fix pre-0.9 compatibility by taking default_interface and default_port from the listener instead of service table --- core/portmanager.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index a87e14eb..a7c3c44e 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -69,6 +69,8 @@ function activate_service(service_name) return nil, "Unknown service: "..service_name; end + local listener = service_info.listener; + local config_prefix = (service_info.config_prefix or service_name).."_"; if config_prefix == "_" then config_prefix = ""; @@ -79,14 +81,13 @@ function activate_service(service_name) or (service_info.private and default_local_interfaces) or config.get("*", "interfaces") or config.get("*", "interface") -- COMPAT w/pre-0.9 - or service_info.default_interface -- COMPAT w/pre0.9 + or listener.default_interface -- COMPAT w/pre0.9 or default_interfaces); - or service_info.default_ports - or {service_info.default_port}); local bind_ports = set.new(config.get("*", config_prefix.."ports") + or service_info.default_ports + or {listener.default_port}); -- COMPAT w/pre-0.9 - local listener = service_info.listener; local mode = listener.default_mode or "*a"; local ssl; if service_info.encryption == "ssl" then -- cgit v1.2.3 From 477f505f4861870fff596c3751d249ecd1132164 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 03:02:36 +0000 Subject: portmanager: Remove check for service_info.multiplex (now implemented in mod_net_multiplex) --- core/portmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index a7c3c44e..b4ceaef9 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -99,7 +99,7 @@ function activate_service(service_name) for interface in bind_interfaces do for port in bind_ports do - if not service_info.multiplex and #active_services:search(nil, interface, port) > 0 then + if #active_services:search(nil, interface, port) > 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 "", service_name or ""); else local handler, err = server.addserver(interface, port, listener, mode, ssl); -- cgit v1.2.3 From 9f7aa91f8f8bfa6c16a5d1e161fce292c190e103 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 03:02:51 +0000 Subject: portmanager: Add get_registered_services() to the public API --- core/portmanager.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index b4ceaef9..914a8e2f 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -171,4 +171,8 @@ function get_active_services(...) return active_services; end +function get_registered_services() + return services; +end + return _M; -- cgit v1.2.3 From e776782e7fd00b7d8d99b5ef831b420b8e6480f0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 03:05:02 +0000 Subject: mod_net_multiplex: Port multiplexing (run multiple different services on a the same port(s))... now pluggable for use with any net plugin --- plugins/mod_net_multiplex.lua | 70 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 plugins/mod_net_multiplex.lua diff --git a/plugins/mod_net_multiplex.lua b/plugins/mod_net_multiplex.lua new file mode 100644 index 00000000..44e1c1ee --- /dev/null +++ b/plugins/mod_net_multiplex.lua @@ -0,0 +1,70 @@ +module:set_global(); + +local max_buffer_len = module:get_option_number("multiplex_buffer_size", 1024); + +local portmanager = require "core.portmanager"; + +local available_services = {}; + +local function add_service(service) + local multiplex_pattern = service.multiplex and service.multiplex.pattern; + if multiplex_pattern then + module:log("debug", "Adding multiplex service %q with pattern %q", service.name, multiplex_pattern); + available_services[service] = multiplex_pattern; + else + module:log("debug", "Service %q is not multiplex-capable", service.name); + end +end +module:hook("service-added", function (event) add_service(event.service); end); +module:hook("service-removed", function (event) available_services[event.service] = nil; end); + +for service_name, services in pairs(portmanager.get_registered_services()) do + for i, service in ipairs(services) do + add_service(service); + end +end + +local buffers = {}; + +local listener = { default_mode = "*a" }; + +function listener.onconnect() +end + +function listener.onincoming(conn, data) + if not data then return; end + local buf = buffers[conn]; + buffers[conn] = nil; + buf = buf and buf..data or data; + for service, multiplex_pattern in pairs(available_services) do + if buf:match(multiplex_pattern) then + module:log("debug", "Routing incoming connection to %s", service.name); + local listener = service.listener; + conn:setlistener(listener); + local onconnect = listener.onconnect; + if onconnect then onconnect(conn) end + return listener.onincoming(conn, buf); + end + end + if #buf > max_buffer_len then -- Give up + conn:close(); + else + buffers[conn] = buf; + end +end + +function listener.ondisconnect(conn, err) + buffers[conn] = nil; -- warn if no buffer? +end + +module:add_item("net-provider", { + name = "multiplex"; + config_prefix = ""; + listener = listener; +}); + +module:provides("net", { + name = "multiplex_ssl"; + config_prefix = "ssl"; + listener = listener; +}); -- cgit v1.2.3 From 8496bd382a0b357eeefbb03e593104b0f11420f2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 03:05:24 +0000 Subject: mod_c2s, mod_s2s: Add multiplex support --- plugins/mod_c2s.lua | 3 +++ plugins/s2s/mod_s2s.lua | 3 +++ 2 files changed, 6 insertions(+) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index e3557492..67156f55 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -231,6 +231,9 @@ module:add_item("net-provider", { name = "legacy_ssl"; listener = listener; encryption = "ssl"; + multiplex = { + pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:client%1.*>"; + }; }); diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 194364cd..04b8ae04 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -472,5 +472,8 @@ module:add_item("net-provider", { listener = listener; default_port = 5269; encryption = "starttls"; + multiplex = { + pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:server%1.*>"; + }; }); -- cgit v1.2.3 From df424cfe1207f0f95b0f155a1893efa697865c3d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 15 Mar 2012 14:47:46 +0100 Subject: mod_posix, mod_bosh, mod_admin_telnet: Use module:set_global() --- plugins/mod_admin_telnet.lua | 2 +- plugins/mod_bosh.lua | 2 +- plugins/mod_posix.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index fd72787e..3799efc0 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -6,7 +6,7 @@ -- COPYING file in the source package for more information. -- -module.host = "*"; +module:set_global(); local _G = _G; diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 9de27b4b..20a6fa1f 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -6,7 +6,7 @@ -- COPYING file in the source package for more information. -- -module.host = "*" -- Global module +module:set_global(); -- Global module local hosts = _G.hosts; local new_xmpp_stream = require "util.xmppstream".new; diff --git a/plugins/mod_posix.lua b/plugins/mod_posix.lua index d229c1b8..b388fb9d 100644 --- a/plugins/mod_posix.lua +++ b/plugins/mod_posix.lua @@ -22,7 +22,7 @@ local stat = lfs.attributes; local prosody = _G.prosody; -module.host = "*"; -- we're a global module +module:set_global(); -- we're a global module local umask = module:get_option("umask") or "027"; pposix.umask(umask); -- cgit v1.2.3 From cd9f57bc12a5c99c86465c7ee209851081593161 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 16:29:30 +0000 Subject: portmanager: Support 'default_port' in service options --- core/portmanager.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 914a8e2f..c5bb936a 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -86,7 +86,9 @@ function activate_service(service_name) local bind_ports = set.new(config.get("*", config_prefix.."ports") or service_info.default_ports - or {listener.default_port}); -- COMPAT w/pre-0.9 + or {service_info.default_port + or listener.default_port -- COMPAT w/pre-0.9 + }); local mode = listener.default_mode or "*a"; local ssl; -- cgit v1.2.3 From fa8c54baebec2d023dae4a587aafde9f0ff49ac6 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 16:31:10 +0000 Subject: mod_c2s, mod_s2s: Drop default_port and default_mode from listener objects (default_port is deprecated, and default_mode already defaults to *a) --- plugins/mod_c2s.lua | 2 +- plugins/s2s/mod_s2s.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 67156f55..12d10b18 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -31,7 +31,7 @@ local opt_keepalives = module:get_option_boolean("tcp_keepalives", false); local sessions = module:shared("sessions"); local stream_callbacks = { default_ns = "jabber:client", handlestanza = core_process_stanza }; -local listener = { default_port = 5222, default_mode = "*a" }; +local listener = {}; --- Stream events handlers local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index ad7f4862..e1102528 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -317,7 +317,7 @@ function stream_callbacks.handlestanza(session, stanza) end end -local listener = { default_port = 5269, default_mode = "*a" }; +local listener = {}; --- Session methods local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; -- cgit v1.2.3 From 7fa01014cb684cc6421ecf9246b7559be795bf1e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 15 Mar 2012 17:37:07 +0100 Subject: mod_c2s: Add missing multiplexed service discovery pattern. --- plugins/mod_c2s.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 12d10b18..743fe3d2 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -225,6 +225,9 @@ module:add_item("net-provider", { listener = listener; default_port = 5222; encryption = "starttls"; + multiplex = { + pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:client%1.*>"; + }; }); module:add_item("net-provider", { -- cgit v1.2.3 From 7f64044ee8292c9a548446fbdf9ea1416ed5ecc5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 19:09:24 +0000 Subject: loggingmanager, util.logger: Remove name sinks and the ability to filter logs by source name (lots of code, hardly used if at all, and possibly broken) --- core/loggingmanager.lua | 34 ++++++---------------------------- util/logger.lua | 31 +------------------------------ 2 files changed, 7 insertions(+), 58 deletions(-) diff --git a/core/loggingmanager.lua b/core/loggingmanager.lua index 426425c1..56a3ee2c 100644 --- a/core/loggingmanager.lua +++ b/core/loggingmanager.lua @@ -48,34 +48,12 @@ local logging_levels = { "debug", "info", "warn", "error" } local function add_rule(sink_config) local sink_maker = log_sink_types[sink_config.to]; if sink_maker then - if sink_config.levels and not sink_config.source then - -- Create sink - local sink = sink_maker(sink_config); - - -- Set sink for all chosen levels - for level in pairs(get_levels(sink_config.levels)) do - logger.add_level_sink(level, sink); - end - elseif sink_config.source and not sink_config.levels then - logger.add_name_sink(sink_config.source, sink_maker(sink_config)); - elseif sink_config.source and sink_config.levels then - local levels = get_levels(sink_config.levels); - local sink = sink_maker(sink_config); - logger.add_name_sink(sink_config.source, - function (name, level, ...) - if levels[level] then - return sink(name, level, ...); - end - end); - else - -- All sources - -- Create sink - local sink = sink_maker(sink_config); - - -- Set sink for all levels - for _, level in pairs(logging_levels) do - logger.add_level_sink(level, sink); - end + -- Create sink + local sink = sink_maker(sink_config); + + -- Set sink for all chosen levels + for level in pairs(get_levels(sink_config.levels or logging_levels)) do + logger.add_level_sink(level, sink); end else -- No such sink type diff --git a/util/logger.lua b/util/logger.lua index c3bf3992..4fadb4b9 100644 --- a/util/logger.lua +++ b/util/logger.lua @@ -13,8 +13,7 @@ local ipairs, pairs, setmetatable = ipairs, pairs, setmetatable; module "logger" -local name_sinks, level_sinks = {}, {}; -local name_patterns = {}; +local level_sinks = {}; local make_logger; @@ -46,17 +45,7 @@ function make_logger(source_name, level) level_sinks[level] = level_handlers; end - local source_handlers = name_sinks[source_name]; - local logger = function (message, ...) - if source_handlers then - for i = 1,#source_handlers do - if source_handlers[i](source_name, level, message, ...) == false then - return; - end - end - end - for i = 1,#level_handlers do level_handlers[i](source_name, level, message, ...); end @@ -66,14 +55,12 @@ function make_logger(source_name, level) end function reset() - for k in pairs(name_sinks) do name_sinks[k] = nil; end for level, handler_list in pairs(level_sinks) do -- Clear all handlers for this level for i = 1, #handler_list do handler_list[i] = nil; end end - for k in pairs(name_patterns) do name_patterns[k] = nil; end end function add_level_sink(level, sink_function) @@ -84,22 +71,6 @@ function add_level_sink(level, sink_function) end end -function add_name_sink(name, sink_function, exclusive) - if not name_sinks[name] then - name_sinks[name] = { sink_function }; - else - name_sinks[name][#name_sinks[name] + 1] = sink_function; - end -end - -function add_name_pattern_sink(name_pattern, sink_function, exclusive) - if not name_patterns[name_pattern] then - name_patterns[name_pattern] = { sink_function }; - else - name_patterns[name_pattern][#name_patterns[name_pattern] + 1] = sink_function; - end -end - _M.new = make_logger; return _M; -- cgit v1.2.3 From a55ad8478e8c326dc48797f89b93a9f291fa2be9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 20 Mar 2012 12:50:53 +0100 Subject: Makefile: Use cp instead of install, and make sure the correct umask is set --- Makefile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Makefile b/Makefile index d94cdf56..356563f2 100644 --- a/Makefile +++ b/Makefile @@ -32,13 +32,8 @@ install: prosody.install prosodyctl.install prosody.cfg.lua.install util/encodin install -m644 util/*.so $(SOURCE)/util install -d $(SOURCE)/util/sasl install -m644 util/sasl/* $(SOURCE)/util/sasl - install -m644 plugins/*.lua $(MODULES) - install -m644 plugins/s2s/*.lua $(MODULES) - install -d $(MODULES)/muc - install -m644 plugins/muc/* $(MODULES)/muc + umask 0022 && cp -r plugins/* $(MODULES) install -m644 certs/* $(CONFIG)/certs - install -d $(MODULES)/adhoc - install -m644 plugins/adhoc/*.lua $(MODULES)/adhoc install -m644 man/prosodyctl.man $(MAN)/man1/prosodyctl.1 test -e $(CONFIG)/prosody.cfg.lua || install -m644 prosody.cfg.lua.install $(CONFIG)/prosody.cfg.lua test -e prosody.version && install prosody.version $(SOURCE)/prosody.version || true -- cgit v1.2.3 From 451abcc73f8315134c31478450b53494f1bfd455 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 22 Mar 2012 15:07:27 +0000 Subject: util.debug: Attempt to get debug.traceback() parameter handling correct (again) --- util/debug.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util/debug.lua b/util/debug.lua index 2170a6d1..f70e20dd 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -95,7 +95,11 @@ function debug.traceback(thread, message, level) if level and type(message) ~= "string" then return nil, "invalid message"; elseif not level then - level = message or 2; + if type(message) == "number" then + level, message = message, nil; + else + level = 2; + end end message = message and (message.."\n") or ""; -- cgit v1.2.3 From 553666c3f2d7da937658e5f695ca4d29a4c1851d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 22 Mar 2012 15:08:21 +0000 Subject: util.debug: Add catch for an "error in error handling" --- util/debug.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/util/debug.lua b/util/debug.lua index f70e20dd..9fd0f9dd 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -88,7 +88,15 @@ function get_traceback_table(thread, start_level) return levels; end -function debug.traceback(thread, message, level) +function debug.traceback(...) + local ok, ret = pcall(debug._traceback, ...); + if not ok then + return "Error in error handling: "..ret; + end + return ret; +end + +function debug._traceback(thread, message, level) if type(thread) ~= "thread" then thread, message, level = coroutine.running(), thread, message; end -- cgit v1.2.3 From 4c94b7dbf1148040551c000f0fa5cf31713f1fc2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 22 Mar 2012 15:09:37 +0000 Subject: util.debug: Add markers in the output when crossing source file boundaries --- util/debug.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/util/debug.lua b/util/debug.lua index 9fd0f9dd..6df249c0 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -114,11 +114,14 @@ function debug._traceback(thread, message, level) local levels = get_traceback_table(thread, level+2); + local last_source_desc; + local lines = {}; for nlevel, level in ipairs(levels) do local info = level.info; local line = "..."; local func_type = info.namewhat.." "; + local source_desc = (info.short_src == "[C]" and "C code") or info.short_src or "Unknown"; if func_type == " " then func_type = ""; end; if info.short_src == "[C]" then line = "[ C ] "..func_type.."C function "..(info.name and ("%q"):format(info.name) or "(unknown name)") @@ -134,6 +137,13 @@ function debug._traceback(thread, message, level) end line = "[Lua] "..info.short_src.." line "..info.currentline.." in "..func_type..name.." defined on line "..info.linedefined; end + if source_desc ~= last_source_desc then -- Venturing into a new source, add marker for previous + if last_source_desc then + local padding = string.rep("-", math.floor(((65 - 6) - #last_source_desc)/2)); + table.insert(lines, "\t ^"..padding.." "..last_source_desc.." "..padding..(#last_source_desc%2==0 and "-^" or "^ ")); + end + last_source_desc = source_desc; + end nlevel = nlevel-1; table.insert(lines, "\t"..(nlevel==0 and ">" or " ").."("..nlevel..") "..line); local npadding = (" "):rep(#tostring(nlevel)); @@ -146,5 +156,9 @@ function debug._traceback(thread, message, level) table.insert(lines, "\t "..npadding.."Upvals: "..upvalues_str); end end + + local padding = string.rep("-", math.floor(((65 - 6) - #last_source_desc) / 2)); + table.insert(lines, "\t ^"..padding.." "..last_source_desc.." "..padding..(#last_source_desc%2==0 and "-^" or "^ ")); + return message.."stack traceback:\n"..table.concat(lines, "\n"); end -- cgit v1.2.3 From 059b6456a7e5dab6cacb002b6ddddb2338f79438 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 22 Mar 2012 15:10:38 +0000 Subject: util.debug: Move optimal line length (default 65) into a variable --- util/debug.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/util/debug.lua b/util/debug.lua index 6df249c0..9b1c4853 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -7,6 +7,8 @@ local censored_names = { pass = true; pwd = true; }; +local optimal_line_length = 65; + local function get_locals_table(level) level = level + 1; -- Skip this function itself @@ -139,7 +141,7 @@ function debug._traceback(thread, message, level) end if source_desc ~= last_source_desc then -- Venturing into a new source, add marker for previous if last_source_desc then - local padding = string.rep("-", math.floor(((65 - 6) - #last_source_desc)/2)); + local padding = string.rep("-", math.floor(((optimal_line_length - 6) - #last_source_desc)/2)); table.insert(lines, "\t ^"..padding.." "..last_source_desc.." "..padding..(#last_source_desc%2==0 and "-^" or "^ ")); end last_source_desc = source_desc; @@ -147,17 +149,17 @@ function debug._traceback(thread, message, level) nlevel = nlevel-1; table.insert(lines, "\t"..(nlevel==0 and ">" or " ").."("..nlevel..") "..line); local npadding = (" "):rep(#tostring(nlevel)); - local locals_str = string_from_var_table(level.locals, 65, "\t "..npadding); + local locals_str = string_from_var_table(level.locals, optimal_line_length, "\t "..npadding); if locals_str then table.insert(lines, "\t "..npadding.."Locals: "..locals_str); end - local upvalues_str = string_from_var_table(level.upvalues, 65, "\t "..npadding); + local upvalues_str = string_from_var_table(level.upvalues, optimal_line_length, "\t "..npadding); if upvalues_str then table.insert(lines, "\t "..npadding.."Upvals: "..upvalues_str); end end - local padding = string.rep("-", math.floor(((65 - 6) - #last_source_desc) / 2)); + local padding = string.rep("-", math.floor(((optimal_line_length - 6) - #last_source_desc) / 2)); table.insert(lines, "\t ^"..padding.." "..last_source_desc.." "..padding..(#last_source_desc%2==0 and "-^" or "^ ")); return message.."stack traceback:\n"..table.concat(lines, "\n"); -- cgit v1.2.3 From 5cb235a17d4886a1853e368dafe5557fe1df522f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 22 Mar 2012 16:07:57 +0000 Subject: util.debug: Add a bit of colour --- util/debug.lua | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/util/debug.lua b/util/debug.lua index 9b1c4853..9d76c795 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -9,6 +9,18 @@ local censored_names = { }; local optimal_line_length = 65; +local termcolours = require "util.termcolours"; +local getstring = termcolours.getstring; +local styles; +do + _ = termcolours.getstyle; + styles = { + boundary_padding = _("bright", "white"); + filename = _("bright", "blue"); + level_num = _("green"); + funcname = _("yellow"); + }; +end local function get_locals_table(level) level = level + 1; -- Skip this function itself @@ -98,6 +110,11 @@ function debug.traceback(...) return ret; end +local function build_source_boundary_marker(last_source_desc) + local padding = string.rep("-", math.floor(((optimal_line_length - 6) - #last_source_desc)/2)); + return getstring(styles.boundary_padding, "^"..padding).." "..getstring(styles.filename, last_source_desc).." "..getstring(styles.boundary_padding, padding..(#last_source_desc%2==0 and "-^" or "^ ")); +end + function debug._traceback(thread, message, level) if type(thread) ~= "thread" then thread, message, level = coroutine.running(), thread, message; @@ -137,17 +154,16 @@ function debug._traceback(thread, message, level) if func_type == "global " or func_type == "local " then func_type = func_type.."function "; end - line = "[Lua] "..info.short_src.." line "..info.currentline.." in "..func_type..name.." defined on line "..info.linedefined; + line = "[Lua] "..info.short_src.." line "..info.currentline.." in "..func_type..getstring(styles.funcname, name).." defined on line "..info.linedefined; end if source_desc ~= last_source_desc then -- Venturing into a new source, add marker for previous if last_source_desc then - local padding = string.rep("-", math.floor(((optimal_line_length - 6) - #last_source_desc)/2)); - table.insert(lines, "\t ^"..padding.." "..last_source_desc.." "..padding..(#last_source_desc%2==0 and "-^" or "^ ")); + table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); end last_source_desc = source_desc; end nlevel = nlevel-1; - table.insert(lines, "\t"..(nlevel==0 and ">" or " ").."("..nlevel..") "..line); + table.insert(lines, "\t"..(nlevel==0 and ">" or " ")..getstring(styles.level_num, "("..nlevel..") ")..line); local npadding = (" "):rep(#tostring(nlevel)); local locals_str = string_from_var_table(level.locals, optimal_line_length, "\t "..npadding); if locals_str then @@ -159,8 +175,7 @@ function debug._traceback(thread, message, level) end end - local padding = string.rep("-", math.floor(((optimal_line_length - 6) - #last_source_desc) / 2)); - table.insert(lines, "\t ^"..padding.." "..last_source_desc.." "..padding..(#last_source_desc%2==0 and "-^" or "^ ")); + table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); return message.."stack traceback:\n"..table.concat(lines, "\n"); end -- cgit v1.2.3 From 9794c49ceae42113b1b80e53b57745c19ffc66bf Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 22 Mar 2012 16:39:28 +0000 Subject: util.debug: Add a touch of colour to source locations --- util/debug.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util/debug.lua b/util/debug.lua index 9d76c795..d63dce1b 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -19,6 +19,7 @@ do filename = _("bright", "blue"); level_num = _("green"); funcname = _("yellow"); + location = _("yellow"); }; end @@ -143,9 +144,9 @@ function debug._traceback(thread, message, level) local source_desc = (info.short_src == "[C]" and "C code") or info.short_src or "Unknown"; if func_type == " " then func_type = ""; end; if info.short_src == "[C]" then - line = "[ C ] "..func_type.."C function "..(info.name and ("%q"):format(info.name) or "(unknown name)") + line = "[ C ] "..func_type.."C function "..getstring(styles.location, (info.name and ("%q"):format(info.name) or "(unknown name)")); elseif info.what == "main" then - line = "[Lua] "..info.short_src.." line "..info.currentline; + line = "[Lua] "..getstring(styles.location, info.short_src.." line "..info.currentline); else local name = info.name or " "; if name ~= " " then @@ -154,7 +155,7 @@ function debug._traceback(thread, message, level) if func_type == "global " or func_type == "local " then func_type = func_type.."function "; end - line = "[Lua] "..info.short_src.." line "..info.currentline.." in "..func_type..getstring(styles.funcname, name).." defined on line "..info.linedefined; + line = "[Lua] "..getstring(styles.location, info.short_src.." line "..info.currentline).." in "..func_type..getstring(styles.funcname, name).." (defined on line "..info.linedefined..")"; end if source_desc ~= last_source_desc then -- Venturing into a new source, add marker for previous if last_source_desc then -- cgit v1.2.3 From a1b3f76c8c2d381950fc51b1ee89dd059898766f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 22 Mar 2012 16:51:39 +0000 Subject: util.debug: Move boundary markers to top of relevant sections of the stack trace (easier to follow) --- util/debug.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/util/debug.lua b/util/debug.lua index d63dce1b..3736dd34 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -113,7 +113,7 @@ end local function build_source_boundary_marker(last_source_desc) local padding = string.rep("-", math.floor(((optimal_line_length - 6) - #last_source_desc)/2)); - return getstring(styles.boundary_padding, "^"..padding).." "..getstring(styles.filename, last_source_desc).." "..getstring(styles.boundary_padding, padding..(#last_source_desc%2==0 and "-^" or "^ ")); + return getstring(styles.boundary_padding, "v"..padding).." "..getstring(styles.filename, last_source_desc).." "..getstring(styles.boundary_padding, padding..(#last_source_desc%2==0 and "-v" or "v ")); end function debug._traceback(thread, message, level) @@ -158,10 +158,8 @@ function debug._traceback(thread, message, level) line = "[Lua] "..getstring(styles.location, info.short_src.." line "..info.currentline).." in "..func_type..getstring(styles.funcname, name).." (defined on line "..info.linedefined..")"; end if source_desc ~= last_source_desc then -- Venturing into a new source, add marker for previous - if last_source_desc then - table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); - end last_source_desc = source_desc; + table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); end nlevel = nlevel-1; table.insert(lines, "\t"..(nlevel==0 and ">" or " ")..getstring(styles.level_num, "("..nlevel..") ")..line); @@ -176,7 +174,7 @@ function debug._traceback(thread, message, level) end end - table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); +-- table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); return message.."stack traceback:\n"..table.concat(lines, "\n"); end -- cgit v1.2.3 From 3028a7d45da8d282f77f0395e026f9b77f006f9b Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Mon, 26 Mar 2012 18:23:49 +0200 Subject: mod_s2s: Add "::" as a IPv6 interface (thanks darkrain) --- plugins/s2s/s2sout.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index 6ee1ca83..af55b273 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -331,7 +331,7 @@ module:hook_global("service-added", function (event) end elseif source == "::" then if not socket.local_addresses then - sources[#sources + 1] = new_ip("::", "IPv4"); + sources[#sources + 1] = new_ip("::", "IPv6"); else for _, addr in ipairs(socket.local_addresses("ipv6", true)) do sources[#sources + 1] = new_ip(addr, "IPv6"); -- cgit v1.2.3 From 7f2d31106e94a0e680586ce2ae39b397304256a3 Mon Sep 17 00:00:00 2001 From: Paul Aurich Date: Mon, 26 Mar 2012 19:17:09 -0700 Subject: mod_s2s: Queuing a stanza constitutes handling it. --- plugins/s2s/mod_s2s.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index e1102528..b0bd5b40 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -76,6 +76,7 @@ module:hook("route/remote", function (event) if host.sendq then t_insert(host.sendq, {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)}); else host.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} }; end host.log("debug", "stanza [%s] queued ", stanza.name); + return true; elseif host.type == "local" or host.type == "component" then log("error", "Trying to send a stanza to ourselves??") log("error", "Traceback: %s", get_traceback()); -- cgit v1.2.3 From d95a61cf4e19f386bd06d988905ef7a8de96be5f Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 8 Apr 2012 04:09:33 +0500 Subject: net.http.{server|codes|parser}: Initial commit. --- net/http/codes.lua | 66 ++++++++++++++++ net/http/parser.lua | 116 +++++++++++++++++++++++++++ net/http/server.lua | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 405 insertions(+) create mode 100644 net/http/codes.lua create mode 100644 net/http/parser.lua create mode 100644 net/http/server.lua diff --git a/net/http/codes.lua b/net/http/codes.lua new file mode 100644 index 00000000..2e701027 --- /dev/null +++ b/net/http/codes.lua @@ -0,0 +1,66 @@ + +local response_codes = { + -- Source: http://www.iana.org/assignments/http-status-codes + -- s/^\(\d*\)\s*\(.*\S\)\s*\[RFC.*\]\s*$/^I["\1"] = "\2"; + [100] = "Continue"; + [101] = "Switching Protocols"; + [102] = "Processing"; + + [200] = "OK"; + [201] = "Created"; + [202] = "Accepted"; + [203] = "Non-Authoritative Information"; + [204] = "No Content"; + [205] = "Reset Content"; + [206] = "Partial Content"; + [207] = "Multi-Status"; + [208] = "Already Reported"; + [226] = "IM Used"; + + [300] = "Multiple Choices"; + [301] = "Moved Permanently"; + [302] = "Found"; + [303] = "See Other"; + [304] = "Not Modified"; + [305] = "Use Proxy"; + -- The 306 status code was used in a previous version of [RFC2616], is no longer used, and the code is reserved. + [307] = "Temporary Redirect"; + + [400] = "Bad Request"; + [401] = "Unauthorized"; + [402] = "Payment Required"; + [403] = "Forbidden"; + [404] = "Not Found"; + [405] = "Method Not Allowed"; + [406] = "Not Acceptable"; + [407] = "Proxy Authentication Required"; + [408] = "Request Timeout"; + [409] = "Conflict"; + [410] = "Gone"; + [411] = "Length Required"; + [412] = "Precondition Failed"; + [413] = "Request Entity Too Large"; + [414] = "Request-URI Too Long"; + [415] = "Unsupported Media Type"; + [416] = "Requested Range Not Satisfiable"; + [417] = "Expectation Failed"; + [422] = "Unprocessable Entity"; + [423] = "Locked"; + [424] = "Failed Dependency"; + -- The 425 status code is reserved for the WebDAV advanced collections expired proposal [RFC2817] + [426] = "Upgrade Required"; + + [500] = "Internal Server Error"; + [501] = "Not Implemented"; + [502] = "Bad Gateway"; + [503] = "Service Unavailable"; + [504] = "Gateway Timeout"; + [505] = "HTTP Version Not Supported"; + [506] = "Variant Also Negotiates"; -- Experimental + [507] = "Insufficient Storage"; + [508] = "Loop Detected"; + [510] = "Not Extended"; +}; + +for k,v in pairs(response_codes) do response_codes[k] = k.." "..v; end +return setmetatable(response_codes, { __index = function(t, k) return k.." Unassigned"; end }) diff --git a/net/http/parser.lua b/net/http/parser.lua new file mode 100644 index 00000000..c98c75af --- /dev/null +++ b/net/http/parser.lua @@ -0,0 +1,116 @@ + +local tonumber = tonumber; +local assert = assert; + +local httpstream = {}; + +function httpstream.new(success_cb, error_cb, parser_type, options_cb) + local client = true; + if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end + local buf = ""; + local chunked; + local state = nil; + local packet; + local len; + local have_body; + local error; + return { + feed = function(self, data) + if error then return nil, "parse has failed"; end + if not data then -- EOF + if state and client and not len then -- reading client body until EOF + packet.body = buf; + success_cb(packet); + elseif buf ~= "" then -- unexpected EOF + error = true; return error_cb(); + end + return; + end + buf = buf..data; + while #buf > 0 do + if state == nil then -- read request + local index = buf:find("\r\n\r\n", nil, true); + if not index then return; end -- not enough data + local method, path, httpversion, status_code, reason_phrase; + local first_line; + local headers = {}; + for line in buf:sub(1,index+1):gmatch("([^\r\n]+)\r\n") do -- parse request + if first_line then + local key, val = line:match("^([^%s:]+): *(.*)$"); + if not key then error = true; return error_cb("invalid-header-line"); end -- TODO handle multi-line and invalid headers + key = key:lower(); + headers[key] = headers[key] and headers[key]..","..val or val; + else + first_line = line; + if client then + httpversion, status_code, reason_phrase = line:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$"); + if not status_code then error = true; return error_cb("invalid-status-line"); end + have_body = not + ( (options_cb and options_cb().method == "HEAD") + or (status_code == 204 or status_code == 304 or status_code == 301) + or (status_code >= 100 and status_code < 200) ); + chunked = have_body and headers["transfer-encoding"] == "chunked"; + else + method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$"); + if not method then error = true; return error_cb("invalid-status-line"); end + path = path:gsub("^//+", "/"); -- TODO parse url more + end + end + end + len = tonumber(headers["content-length"]); -- TODO check for invalid len + if client then + -- FIXME handle '100 Continue' response (by skipping it) + if not have_body then len = 0; end + packet = { + code = status_code; + httpversion = httpversion; + headers = headers; + body = have_body and "" or nil; + -- COMPAT the properties below are deprecated + responseversion = httpversion; + responseheaders = headers; + }; + else + len = len or 0; + packet = { + method = method; + path = path; + httpversion = httpversion; + headers = headers; + body = nil; + }; + end + buf = buf:sub(index + 4); + state = true; + end + if state then -- read body + if client then + if chunked then + local index = buf:find("\r\n", nil, true); + if not index then return; end -- not enough data + local chunk_size = buf:match("^%x+"); + if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end + chunk_size = tonumber(chunk_size, 16); + index = index + 2; + if chunk_size == 0 then + state = nil; success_cb(packet); + elseif #buf - index + 1 >= chunk_size then -- we have a chunk + packet.body = packet.body..buf:sub(index, index + chunk_size - 1); + buf = buf:sub(index + chunk_size); + end + error("trailers"); -- FIXME MUST read trailers + elseif len and #buf >= len then + packet.body, buf = buf:sub(1, len), buf:sub(len + 1); + state = nil; success_cb(packet); + end + elseif #buf >= len then + packet.body, buf = buf:sub(1, len), buf:sub(len + 1); + state = nil; success_cb(packet); + end + end + end + end; + }; +end + +return httpstream; diff --git a/net/http/server.lua b/net/http/server.lua new file mode 100644 index 00000000..788f046b --- /dev/null +++ b/net/http/server.lua @@ -0,0 +1,223 @@ + +local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; +local parser_new = require "net.http.parser".new; +local events = require "util.events".new(); +local addserver = require "net.server".addserver; +local log = require "util.logger".init("http.server"); +local os_date = os.date; +local pairs = pairs; +local s_upper = string.upper; +local setmetatable = setmetatable; +local xpcall = xpcall; +local debug = debug; +local tostring = tostring; +local codes = require "net.http.codes"; +local _G = _G; + +local _M = {}; + +local sessions = {}; +local handlers = {}; + +local listener = {}; + +local handle_request; +local _1, _2, _3; +local function _handle_request() return handle_request(_1, _2, _3); end +local function _traceback_handler(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end + +function listener.onconnect(conn) + local secure = conn:ssl() and true or nil; + local pending = {}; + local waiting = false; + local function process_next(last_response) + --if waiting then log("debug", "can't process_next, waiting"); return; end + if sessions[conn] and #pending > 0 then + local request = t_remove(pending); + --log("debug", "process_next: %s", request.path); + waiting = true; + --handle_request(conn, request, process_next); + _1, _2, _3 = conn, request, process_next; + if not xpcall(_handle_request, _traceback_handler) then + conn:write("HTTP/1.0 503 Internal Server Error\r\n\r\nAn error occured during the processing of this request."); + conn:close(); + end + else + --log("debug", "ready for more"); + waiting = false; + end + end + local function success_cb(request) + --log("debug", "success_cb: %s", request.path); + request.secure = secure; + t_insert(pending, request); + if not waiting then + process_next(); + end + end + local function error_cb(err) + log("debug", "error_cb: %s", err or ""); + -- FIXME don't close immediately, wait until we process current stuff + -- FIXME if err, send off a bad-request response + sessions[conn] = nil; + conn:close(); + end + sessions[conn] = parser_new(success_cb, error_cb); +end + +function listener.ondisconnect(conn) + sessions[conn] = nil; +end + +function listener.onincoming(conn, data) + sessions[conn]:feed(data); +end + +local headerfix = setmetatable({}, { + __index = function(t, k) + local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": "; + t[k] = v; + return v; + end +}); + +function _M.hijack_response(response, listener) + error("TODO"); +end +function handle_request(conn, request, finish_cb) + --log("debug", "handler: %s", request.path); + local headers = {}; + for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end + request.headers = headers; + request.conn = conn; + + local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use + local conn_header = request.headers.connection; + local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close"); + + local response = { + request = request; + status_code = 200; + headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") }; + conn = conn; + send = _M.send_response; + finish_cb = finish_cb; + }; + + if not request.headers.host then + response.status_code = 400; + response.headers.content_type = "text/html"; + response:send("400 Bad Request400 Bad Request: No Host header."); + else + -- TODO call handler + --response.headers.content_type = "text/plain"; + --response:send("host="..(request.headers.host or "").."\npath="..request.path.."\n"..(request.body or "")); + local host = request.headers.host; + if host then + host = host:match("[^:]*"):lower(); + local event = request.method.." "..host..request.path:match("[^?]*"); + local payload = { request = request, response = response }; + --[[repeat + if events.fire_event(event, payload) ~= nil then return; end + event = (event:sub(-1) == "/") and event:sub(1, -1) or event:gsub("[^/]*$", ""); + if event:sub(-1) == "/" then + event = event:sub(1, -1); + else + event = event:gsub("[^/]*$", ""); + end + until not event:find("/", 1, true);]] + --log("debug", "Event: %s", event); + if events.fire_event(event, payload) ~= nil then return; end + -- TODO try adding/stripping / at the end, but this needs to work via an HTTP redirect + end + + -- if handler not called, fallback to legacy httpserver handlers + _M.legacy_handler(request, response); + end +end +function _M.send_response(response, body) + local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); + local headers = response.headers; + body = body or ""; + headers.content_length = #body; + + local output = { status_line }; + for k,v in pairs(headers) do + t_insert(output, headerfix[k]..v); + end + t_insert(output, "\r\n\r\n"); + t_insert(output, body); + + response.conn:write(t_concat(output)); + if headers.connection == "Keep-Alive" then + response:finish_cb(); + else + response.conn:close(); + end +end +function _M.legacy_handler(request, response) + log("debug", "Invoking legacy handler"); + local base = request.path:match("^/([^/?]+)"); + local legacy_server = _G.httpserver and _G.httpserver.new.http_servers[5280]; + local handler = legacy_server and legacy_server.handlers[base]; + if not handler then handler = _G.httpserver and _G.httpserver.set_default_handler.default_handler; end + if handler then + -- add legacy properties to request object + request.url = { path = request.path }; + request.handler = response.conn; + request.id = tostring{}:match("%x+$"); + local headers = {}; + for k,v in pairs(request.headers) do + headers[k:gsub("_", "-")] = v; + end + request.headers = headers; + function request:send(resp) + if self.destroyed then return; end + if resp.body or resp.headers then + if resp.headers then + for k,v in pairs(resp.headers) do response.headers[k] = v; end + end + response:send(resp.body) + else + response:send(resp) + end + self.sent = true; + self:destroy(); + end + function request:destroy() + if self.destroyed then return; end + if not self.sent then return self:send(""); end + self.destroyed = true; + if self.on_destroy then + log("debug", "Request has destroy callback"); + self:on_destroy(); + else + log("debug", "Request has no destroy callback"); + end + end + local r = handler(request.method, request.body, request); + if r ~= true then + request:send(r); + end + else + log("debug", "No handler found"); + response.status_code = 404; + response.headers.content_type = "text/html"; + response:send("404 Not Found404 Not Found: No such page."); + end +end + +function _M.add_handler(event, handler, priority) + events.add_handler(event, handler, priority); +end +function _M.remove_handler(event, handler) + events.remove_handler(event, handler); +end + +function _M.listen_on(port, interface, ssl) + addserver(interface or "*", port, listener, "*a", ssl); +end + +_M.listener = listener; +_M.codes = codes; +return _M; -- cgit v1.2.3 From d710b38313d0bab66d0c10153fbe9062b1f3c373 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 13 Apr 2012 00:39:00 +0200 Subject: mod_adhoc: Always allow at least the "complete" action --- plugins/adhoc/adhoc.lib.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/adhoc/adhoc.lib.lua b/plugins/adhoc/adhoc.lib.lua index 0cb4efe1..57059b3f 100644 --- a/plugins/adhoc/adhoc.lib.lua +++ b/plugins/adhoc/adhoc.lib.lua @@ -25,6 +25,7 @@ function _M.new(name, node, handler, permission) end function _M.handle_cmd(command, origin, stanza) + local cmdtag, actions; local sessionid = stanza.tags[1].attr.sessionid or uuid.generate(); local dataIn = {}; dataIn.to = stanza.attr.to; @@ -58,7 +59,7 @@ function _M.handle_cmd(command, origin, stanza) elseif name == "error" then cmdtag:tag("note", {type="error"}):text(content.message):up(); elseif name =="actions" then - local actions = st.stanza("actions"); + actions = st.stanza("actions"); for _, action in ipairs(content) do if (action == "prev") or (action == "next") or (action == "complete") then actions:tag(action):up(); @@ -67,7 +68,6 @@ function _M.handle_cmd(command, origin, stanza) '" at node "'..command.node..'" provided an invalid action "'..action..'"'); end end - cmdtag:add_child(actions); elseif name == "form" then cmdtag:add_child((content.layout or content):form(content.values)); elseif name == "result" then @@ -76,6 +76,13 @@ function _M.handle_cmd(command, origin, stanza) cmdtag:add_child(content); end end + + if not actions then + actions = st.stanza("actions"); + actions:tag("complete"):up(); + end + cmdtag:add_child(actions); + stanza:add_child(cmdtag); origin.send(stanza); -- cgit v1.2.3 From 66d1101545dc3b6981ad2a7f45e6b719829d030e Mon Sep 17 00:00:00 2001 From: Markus K?tter Date: Fri, 13 Apr 2012 21:23:26 +0200 Subject: muc - implement per channel history limits - allow configuration via channel settings - store the settings for permanent channels - honor muc max_history_messages from the config as upper limit - only broadcast_message with historic = true if history_length is > 0 --- plugins/muc/mod_muc.lua | 8 ++++---- plugins/muc/muc.lib.lua | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 8ef7a5b3..43b8423d 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -69,10 +69,10 @@ for jid in pairs(persistent_rooms) do local node = jid_split(jid); local data = datamanager.load(node, muc_host, "config") or {}; local room = muc_new_room(jid, { - history_length = max_history_messages; + max_history_length = max_history_messages; }); room._data = data._data; - room._data.history_length = max_history_messages; --TODO: Need to allow per-room with a global limit + room._data.max_history_length = max_history_messages; -- Overwrite old max_history_length in data with current settings room._affiliations = data._affiliations; room.route_stanza = room_route_stanza; room.save = room_save; @@ -80,7 +80,7 @@ for jid in pairs(persistent_rooms) do end local host_room = muc_new_room(muc_host, { - history_length = max_history_messages; + max_history_length = max_history_messages; }); host_room.route_stanza = room_route_stanza; host_room.save = room_save; @@ -131,7 +131,7 @@ function stanza_handler(event) (restrict_room_creation == "admin" and is_admin(stanza.attr.from)) or (restrict_room_creation == "local" and select(2, jid_split(stanza.attr.from)) == module.host:gsub("^[^%.]+%.", "")) then room = muc_new_room(bare, { - history_length = max_history_messages; + max_history_length = max_history_messages; }); room.route_stanza = room_route_stanza; room.save = room_save; diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 731f9e37..286ad70c 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -138,7 +138,7 @@ function room_mt:broadcast_message(stanza, historic) stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) local entry = { stanza = stanza, stamp = stamp }; t_insert(history, entry); - while #history > (self._data.history_length or default_history_length) do t_remove(history, 1) end + while #history > self._data.history_length do t_remove(history, 1) end end end function room_mt:broadcast_except_nick(stanza, nick) @@ -339,6 +339,21 @@ end function room_mt:get_changesubject() return self._data.changesubject; end +function room_mt:get_historylength() + return self._data.history_length +end +function room_mt:set_historylength(length) + if tonumber(length) == nil then + return + end + length = tonumber(length); + log("debug", "max_history_length %s", self._data.max_history_length or "nil"); + if self._data.max_history_length and length > self._data.max_history_length then + length = self._data.max_history_length + end + self._data.history_length = length; +end + function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc local from, to = stanza.attr.from, stanza.attr.to; @@ -608,6 +623,12 @@ function room_mt:get_form_layout() type = 'boolean', label = 'Make Room Members-Only?', value = self:is_members_only() + }, + { + name = 'muc#roomconfig_historylength', + type = 'text-single', + label = 'Maximum Number of History Messages Returned by Room', + value = tostring(self:get_historylength()) } }); end @@ -659,6 +680,11 @@ function room_mt:process_form(origin, stanza) dirty = dirty or (self:get_changesubject() ~= (not changesubject and true or nil)) module:log('debug', 'changesubject=%s', changesubject and "true" or "false") + local historylength = fields['muc#roomconfig_historylength']; + dirty = dirty or (self:get_historylength() ~= (historylength and true or nil)) + module:log('debug', 'historylength=%s', historylength) + + local whois = fields['muc#roomconfig_whois']; if not valid_whois[whois] then origin.send(st.error_reply(stanza, 'cancel', 'bad-request', "Invalid value for 'whois'")); @@ -677,6 +703,7 @@ function room_mt:process_form(origin, stanza) self:set_persistent(persistent); self:set_hidden(not public); self:set_changesubject(changesubject); + self:set_historylength(historylength); if self.save then self:save(true); end origin.send(st.reply(stanza)); @@ -848,7 +875,7 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha origin.send(st.error_reply(stanza, "cancel", "forbidden")); end else - self:broadcast_message(stanza, true); + self:broadcast_message(stanza, self:get_historylength() > 0); end stanza.attr.from = from; end @@ -1102,7 +1129,8 @@ function _M.new_room(jid, config) _occupants = {}; _data = { whois = 'moderators'; - history_length = (config and config.history_length); + history_length = (config and config.max_history_length) or default_history_length; + max_history_length = (config and config.max_history_length) or default_history_length; }; _affiliations = {}; }, room_mt); -- cgit v1.2.3 From 4c43038f7494c0800c27ff198a931b92e63c02d1 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sat, 21 Apr 2012 17:37:00 +0500 Subject: net.http.server: Missing in 404 Not Found response. --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 788f046b..feb8f766 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -203,7 +203,7 @@ function _M.legacy_handler(request, response) log("debug", "No handler found"); response.status_code = 404; response.headers.content_type = "text/html"; - response:send("<html><head>404 Not Found</head><body>404 Not Found: No such page.</body></html>"); + response:send("<html><head><title>404 Not Found404 Not Found: No such page."); end end -- cgit v1.2.3 From 7f2c53a45dd7583d4cf560988c81e0ff59d007e1 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sat, 21 Apr 2012 17:38:01 +0500 Subject: net.http.server: Fire global HTTP event when no specific handlers available. --- net/http/server.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/http/server.lua b/net/http/server.lua index feb8f766..1f61c7b9 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -129,6 +129,7 @@ function handle_request(conn, request, finish_cb) --log("debug", "Event: %s", event); if events.fire_event(event, payload) ~= nil then return; end -- TODO try adding/stripping / at the end, but this needs to work via an HTTP redirect + if events.fire_event("*", payload) ~= nil then return; end end -- if handler not called, fallback to legacy httpserver handlers -- cgit v1.2.3 From 10a43c8ff2c3b1e7b9092a88886dd24105527cda Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sat, 21 Apr 2012 17:38:48 +0500 Subject: mod_http: Provide HTTP service. --- plugins/mod_http.lua | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 plugins/mod_http.lua diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua new file mode 100644 index 00000000..4713cf13 --- /dev/null +++ b/plugins/mod_http.lua @@ -0,0 +1,69 @@ +-- 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:set_global(); + +--local sessions = module:shared("sessions"); + +--[[function listener.associate_session(conn, session) + sessions[conn] = session; +end]] + +local handlers; + +function build_handlers() + handlers = {}; + for _, item in ipairs(module:get_host_items("http-handler")) do + local previous = handlers[item.path]; + if not previous and item.path then + handlers[item.path] = item; + end + end +end +function clear_handlers() + handlers = nil; +end +module:handle_items("http-handler", clear_handlers, clear_handlers, false); + +function http_handler(event) + local request, response = event.request, event.response; + + if not handlers then build_handlers(); end + local item = handlers[request.path:match("[^?]*")]; + local handler = item and item.handler; + if handler then + handler(request, response); + return true; + end +end + +local server = require "net.http.server"; +local listener = server.listener; +server.add_handler("*", http_handler); +function module.unload() + server.remove_handler("*", http_handler); +end +--require "net.http.server".listen_on(8080); + +module:add_item("net-provider", { + name = "http"; + listener = listener; + default_port = 5280; + multiplex = { + pattern = "^[A-Z]"; + }; +}); + +module:add_item("net-provider", { + name = "https"; + listener = listener; + encryption = "ssl"; + multiplex = { + pattern = "^[A-Z]"; + }; +}); -- cgit v1.2.3 From 668d1d442a158223979c1510841298dfd4535525 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sat, 21 Apr 2012 18:23:44 +0500 Subject: mod_http: Include handlers of non-global modules. --- plugins/mod_http.lua | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 4713cf13..24ef8ab3 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -14,28 +14,44 @@ module:set_global(); sessions[conn] = session; end]] -local handlers; +local NULL = {}; +local handlers = {}; -function build_handlers() - handlers = {}; - for _, item in ipairs(module:get_host_items("http-handler")) do - local previous = handlers[item.path]; - if not previous and item.path then - handlers[item.path] = item; +function build_handlers(host) + if not hosts[host] then return; end + local h = {}; + handlers[host] = h; + + for mod_name, module in pairs(modulemanager.get_modules(host)) do + module = module.module; + if module.items then + for _, item in ipairs(module.items["http-handler"] or NULL) do + local previous = handlers[item.path]; + if not previous and item.path then + h[item.path] = item; + end + end end end + + return h; +end +function clear_handlers(event) + handlers[event.source.host] = nil; end -function clear_handlers() - handlers = nil; +function get_handler(host, path) + local h = handlers[host] or build_handlers(host); + if h then + local item = h[path]; + return item and item.handler; + end end module:handle_items("http-handler", clear_handlers, clear_handlers, false); function http_handler(event) local request, response = event.request, event.response; - if not handlers then build_handlers(); end - local item = handlers[request.path:match("[^?]*")]; - local handler = item and item.handler; + local handler = get_handler(request.headers.host:match("[^:]*"):lower(), request.path:match("[^?]*")); if handler then handler(request, response); return true; -- cgit v1.2.3 From 40ba334367d45d21d57dea128c429cce1ca874f7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 16:27:42 +0100 Subject: net.connlisteners: Be gone! --- net/connlisteners.lua | 81 --------------------------------------------------- 1 file changed, 81 deletions(-) delete mode 100644 net/connlisteners.lua diff --git a/net/connlisteners.lua b/net/connlisteners.lua deleted file mode 100644 index 6a227c9d..00000000 --- a/net/connlisteners.lua +++ /dev/null @@ -1,81 +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. --- - - - -local listeners_dir = (CFG_SOURCEDIR or ".").."/net/"; -local server = require "net.server"; -local log = require "util.logger".init("connlisteners"); -local tostring = tostring; -local type = type -local ipairs = ipairs - -local dofile, xpcall, error = - dofile, xpcall, error - -local debug_traceback = debug.traceback; - -module "connlisteners" - -local listeners = {}; - -function register(name, listener) - if listeners[name] and listeners[name] ~= listener then - log("debug", "Listener %s is already registered, not registering any more", name); - return false; - end - listeners[name] = listener; - log("debug", "Registered connection listener %s", name); - return true; -end - -function deregister(name) - listeners[name] = nil; -end - -function get(name) - local h = listeners[name]; - if not h then - local ok, ret = xpcall(function() dofile(listeners_dir..name:gsub("[^%w%-]", "_").."_listener.lua") end, debug_traceback); - if not ok then - log("error", "Error while loading listener '%s': %s", tostring(name), tostring(ret)); - return nil, ret; - end - h = listeners[name]; - end - return h; -end - -function start(name, udata) - local h, err = get(name); - if not h then - error("No such connection module: "..name.. (err and (" ("..err..")") or ""), 0); - end - - local interfaces = (udata and udata.interface) or h.default_interface or "*"; - if type(interfaces) == "string" then interfaces = {interfaces}; end - local port = (udata and udata.port) or h.default_port or error("Can't start listener "..name.." because no port was specified, and it has no default port", 0); - local mode = (udata and udata.mode) or h.default_mode or 1; - local ssl = (udata and udata.ssl) or nil; - local autossl = udata and udata.type == "ssl"; - - if autossl and not ssl then - return nil, "no ssl context"; - end - - ok, err = true, {}; - for _, interface in ipairs(interfaces) do - local handler - handler, err[interface] = server.addserver(interface, port, h, mode, autossl and ssl or nil); - ok = ok and handler; - end - - return ok, err; -end - -return _M; -- cgit v1.2.3 From 28cb1b2a943e79eebccd1e724585c06e6ffbf420 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:00:30 +0100 Subject: modulemanager: Allow loading a module onto "*" (part-fixes #228) --- core/modulemanager.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f9f3a8b8..f00532a0 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -117,13 +117,15 @@ end local function do_load_module(host, module_name) if not (host and module_name) then return nil, "insufficient-parameters"; - elseif not hosts[host] then + elseif not hosts[host] and host ~= "*"then return nil, "unknown-host"; end if not modulemap[host] then modulemap[host] = {}; - hosts[host].modules = modulemap[host]; + if host ~= "*" then + hosts[host].modules = modulemap[host]; + end end if modulemap[host][module_name] then -- cgit v1.2.3 From 06c05a1916d99acc3fc79f0baa28fc9dd71167d7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:01:40 +0100 Subject: modulemanager: Use api_instance rather than pluginenv.module (same thing) --- core/modulemanager.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f00532a0..f5173f00 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -162,9 +162,9 @@ local function do_load_module(host, module_name) end end - modulemap[pluginenv.module.host][module_name] = pluginenv; - if pluginenv.module.host == "*" then - if not pluginenv.module.global then -- COMPAT w/pre-0.9 + modulemap[api_instance.host][module_name] = pluginenv; + if api_instance.host == "*" then + if not api_instance.global then -- COMPAT w/pre-0.9 log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); api_instance:set_global(); end -- cgit v1.2.3 From 4664423a7b50fc7146c7e798af2d31e88ca5093e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:02:45 +0100 Subject: modulemanager: Use modulemap rather than hosts[host] (fix for when host == "*") --- core/modulemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f5173f00..f7594cd9 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -169,7 +169,7 @@ local function do_load_module(host, module_name) api_instance:set_global(); end else - hosts[host].modules[module_name] = pluginenv; + modulemap[host][module_name] = pluginenv; end end if not ok then -- cgit v1.2.3 From 800fd50ce0934689db0afaf6966ec6ba78f06eec Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:04:07 +0100 Subject: modulemanager: Make module_has_method and module_call_method use rawget() --- core/modulemanager.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f7594cd9..769041f9 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -261,12 +261,12 @@ function is_loaded(host, name) end function module_has_method(module, method) - return type(module.module[method]) == "function"; + return type(rawget(module.module, method)) == "function"; end function call_module_method(module, method, ...) - if module_has_method(module, method) then - local f = module.module[method]; + local f = rawget(module.module, method); + if type(f) == "function" then return pcall(f, ...); else return false, "no-such-method"; -- cgit v1.2.3 From 1c88e91a1990b795c0d358f5b6d38c3d28288ea5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:09:03 +0100 Subject: modulemanager: Support for shared modules - function module.add_host(host_module) in a global module --- core/modulemanager.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 769041f9..e76f2624 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -132,6 +132,21 @@ local function do_load_module(host, module_name) log("warn", "%s is already loaded for %s, so not loading again", module_name, host); return nil, "module-already-loaded"; elseif modulemap["*"][module_name] then + local mod = modulemap["*"][module_name]; + if module_has_method(mod, "add_host") then + local _log = logger.init(host..":"..module_name); + local host_module_api = setmetatable({ + host = host, event_handlers = {}, items = {}; + _log = _log, log = function (self, ...) return _log(...); end; + },{ + __index = modulemap["*"][module_name].module; + }); + local ok, result, module_err = call_module_method(mod, "add_host", host_module_api); + if not ok or result == false then return nil, ok and module_err or result; end + local host_module = setmetatable({ module = host_module_api }, { __index = mod }); + modulemap[host][module_name] = host_module; + return host_module; + end return nil, "global-module-already-loaded"; end -- cgit v1.2.3 From 0c6799c9368f41c38f2effceb29423e62b1693d2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:37:06 +0100 Subject: modulemanager: When a shared module becomes global, ensure it still gets loaded onto the original target host --- core/modulemanager.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index e76f2624..f263816b 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -183,8 +183,10 @@ local function do_load_module(host, module_name) log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); api_instance:set_global(); end - else - modulemap[host][module_name] = pluginenv; + if host ~= api_instance.host and module_has_method(pluginenv, "add_host") then + -- Now load the module again onto the host it was originally being loaded on + do_load_module(host, module_name); + end end end if not ok then -- cgit v1.2.3 From e774a4c4ba1f57888f40b5dc5cf93b19ad2b81d2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:38:27 +0100 Subject: mod_admin_telnet: get_host_set(): Include '*' in the set if no specific hosts are specified and the module is loaded there --- plugins/mod_admin_telnet.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 3799efc0..e60a2245 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -277,8 +277,12 @@ local function get_hosts_set(hosts, module) return set.new { hosts }; elseif hosts == nil then local mm = require "modulemanager"; - return set.new(array.collect(keys(prosody.hosts))) + local hosts_set = set.new(array.collect(keys(prosody.hosts))) / function (host) return prosody.hosts[host].type == "local" or module and mm.is_loaded(host, module); end; + if module and mm.get_module("*", module) then + hosts_set:add("*"); + end + return hosts_set; end end -- cgit v1.2.3 From 15b81453cd9c57a787c2db9fd28fbf04a0a98334 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:39:21 +0100 Subject: mod_admin_telnet: module:reload(): If module is loaded on *, reload it there first (ensuring shared module code is reloaded before per-host children of that module) --- plugins/mod_admin_telnet.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index e60a2245..2f2e057b 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -334,11 +334,15 @@ end function def_env.module:reload(name, hosts) local mm = require "modulemanager"; - hosts = get_hosts_set(hosts, name); - + hosts = array.collect(get_hosts_set(hosts, name)):sort(function (a, b) + if a == "*" then return true + elseif b == "*" then return false + else return a < b; end + end); + -- Reload the module for each host local ok, err, count = true, nil, 0; - for host in hosts do + for _, host in ipairs(hosts) do if mm.is_loaded(host, name) then ok, err = mm.reload(host, name); if not ok then -- cgit v1.2.3 From 76749e9e8c28633ef73cd5024f4353e4274462e7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:39:52 +0100 Subject: mod_admin_telnet: module:list(): List global modules (part-fixes #228) --- plugins/mod_admin_telnet.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 2f2e057b..412e1e62 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -363,6 +363,7 @@ end function def_env.module:list(hosts) if hosts == nil then hosts = array.collect(keys(prosody.hosts)); + table.insert(hosts, 1, "*"); end if type(hosts) == "string" then hosts = { hosts }; @@ -373,8 +374,8 @@ function def_env.module:list(hosts) local print = self.session.print; for _, host in ipairs(hosts) do - print(host..":"); - local modules = array.collect(keys(prosody.hosts[host] and prosody.hosts[host].modules or {})):sort(); + print((host == "*" and "Global" or host)..":"); + local modules = array.collect(keys(modulemanager.get_modules(host) or {})):sort(); if #modules == 0 then if prosody.hosts[host] then print(" No modules loaded"); -- cgit v1.2.3 From 2b4e2b8620c3cf5d6bfcf2450de08bd25ca77bfa Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:52:12 +0100 Subject: mod_admin_telnet: module:load(): Fix 'global-module-already-loaded' errors when successfully loading a global module (fixes #228) --- plugins/mod_admin_telnet.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 412e1e62..cb6302c7 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -292,16 +292,22 @@ function def_env.module:load(name, hosts, config) hosts = get_hosts_set(hosts); -- Load the module for each host - local ok, err, count = true, nil, 0; + local ok, err, count, mod = true, nil, 0, nil; for host in hosts do if (not mm.is_loaded(host, name)) then - ok, err = mm.load(host, name, config); - if not ok then + mod, err = mm.load(host, name, config); + if not mod then ok = false; + if err == "global-module-already-loaded" then + if count > 0 then + ok, err, count = true, nil, 1; + end + break; + end self.session.print(err or "Unknown error loading module"); else count = count + 1; - self.session.print("Loaded for "..host); + self.session.print("Loaded for "..mod.module.host); end end end -- cgit v1.2.3 From 3bf2b6904e529659f8a76e6648de0d9caaea91ed Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 21:03:57 +0100 Subject: prosody: Don't require net.httpserver now --- prosody | 2 -- 1 file changed, 2 deletions(-) diff --git a/prosody b/prosody index f79910b7..2995f0f1 100755 --- a/prosody +++ b/prosody @@ -306,8 +306,6 @@ function load_secondary_libraries() if remdebug then remdebug.engine.start() end ]] - require "net.httpserver"; - require "util.stanza" require "util.jid" end -- cgit v1.2.3 From 8af73c6e8ac4013229e599be6692447dbd53337d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 21:16:53 +0100 Subject: mod_motd: Send only to resource coming online, not the user's bare JID (fixes #282) --- plugins/mod_motd.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/mod_motd.lua b/plugins/mod_motd.lua index d567288b..646bf8cd 100644 --- a/plugins/mod_motd.lua +++ b/plugins/mod_motd.lua @@ -13,15 +13,15 @@ local motd_jid = module:get_option_string("motd_jid", host); if not motd_text then return; end +local jid_join = require "util.jid".join; local st = require "util.stanza"; motd_text = motd_text:gsub("^%s*(.-)%s*$", "%1"):gsub("\n%s+", "\n"); -- Strip indentation from the config -module:hook("resource-bind", - function (event) +module:hook("resource-bind", function (event) local session = event.session; local motd_stanza = - st.message({ to = session.username..'@'..session.host, from = motd_jid }) + st.message({ to = jid_join(session.username, session.host, session.resource), from = motd_jid }) :tag("body"):text(motd_text); core_route_stanza(hosts[host], motd_stanza); module:log("debug", "MOTD send to user %s@%s", session.username, session.host); -- cgit v1.2.3 From c8279932bfefb1bb8f5a251f59cdbd0230454335 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 21 Apr 2012 23:11:59 +0200 Subject: core.certmanager: Log a message when a password is required but not supplied. fixes #214 --- core/certmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/certmanager.lua b/core/certmanager.lua index 8b82ac47..cccf3098 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -35,7 +35,7 @@ function create_context(host, mode, user_ssl_config) mode = mode; protocol = user_ssl_config.protocol or "sslv23"; key = resolve_path(config_path, user_ssl_config.key); - password = user_ssl_config.password; + password = user_ssl_config.password or function() log("error", "Encrypted certificate for %s requires 'ssl' 'password' to be set in config", host); end; certificate = resolve_path(config_path, user_ssl_config.certificate); capath = resolve_path(config_path, user_ssl_config.capath or default_capath); cafile = resolve_path(config_path, user_ssl_config.cafile); -- cgit v1.2.3 From 4ff0a4613db3b11d7a9dd4c89c020cec3e89bad4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 22:50:57 +0100 Subject: mod_component: Make a shared module, and move the xmppcomponent_listener into it ('port'ing over to portmanager). Ha ha. --- net/xmppcomponent_listener.lua | 218 --------------------------- plugins/mod_component.lua | 333 +++++++++++++++++++++++++++++++++-------- 2 files changed, 268 insertions(+), 283 deletions(-) delete mode 100644 net/xmppcomponent_listener.lua diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua deleted file mode 100644 index dd7b2b91..00000000 --- a/net/xmppcomponent_listener.lua +++ /dev/null @@ -1,218 +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. --- - - -local t_concat = table.concat; -local tostring = tostring; -local type = type; -local pairs = pairs; - -local lxp = require "lxp"; -local logger = require "util.logger"; -local config = require "core.configmanager"; -local connlisteners = require "net.connlisteners"; -local uuid_gen = require "util.uuid".generate; -local jid_split = require "util.jid".split; -local sha1 = require "util.hashes".sha1; -local st = require "util.stanza"; -local new_xmpp_stream = require "util.xmppstream".new; - -local sessions = {}; - -local log = logger.init("componentlistener"); - -local component_listener = { default_port = 5347; default_mode = "*a"; default_interface = config.get("*", "core", "component_interface") or "127.0.0.1" }; - -local xmlns_component = 'jabber:component:accept'; - ---- Callbacks/data for xmppstream to handle streams for us --- - -local stream_callbacks = { default_ns = xmlns_component }; - -local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; - -function stream_callbacks.error(session, error, data, data2) - if session.destroyed then return; end - log("warn", "Error processing component stream: "..tostring(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:close("not-well-formed"); - elseif error == "stream-error" then - local condition, text = "undefined-condition"; - for child in data:children() do - if child.attr.xmlns == xmlns_xmpp_streams then - if child.name ~= "text" then - condition = child.name; - else - text = child:get_text(); - end - if condition ~= "undefined-condition" and text then - break; - end - end - end - text = condition .. (text and (" ("..text..")") or ""); - session.log("info", "Session closed by remote with error: %s", text); - session:close(nil, text); - end -end - -function stream_callbacks.streamopened(session, attr) - if config.get(attr.to, "core", "component_module") ~= "component" then - -- Trying to act as a component domain which - -- hasn't been configured - session:close{ condition = "host-unknown", text = tostring(attr.to).." does not match any configured external components" }; - return; - end - - -- Note that we don't create the internal component - -- until after the external component auths successfully - - session.host = attr.to; - session.streamid = uuid_gen(); - session.notopen = nil; - - session.send(st.stanza("stream:stream", { xmlns=xmlns_component, - ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.host }):top_tag()); - -end - -function stream_callbacks.streamclosed(session) - session.log("debug", "Received "); - session:close(); -end - -local core_process_stanza = core_process_stanza; - -function stream_callbacks.handlestanza(session, stanza) - -- Namespaces are icky. - if not stanza.attr.xmlns and stanza.name == "handshake" then - stanza.attr.xmlns = xmlns_component; - end - if not stanza.attr.xmlns or stanza.attr.xmlns == "jabber:client" then - local from = stanza.attr.from; - if from then - if session.component_validate_from then - local _, domain = jid_split(stanza.attr.from); - if domain ~= session.host then - -- Return error - session.log("warn", "Component sent stanza with missing or invalid 'from' address"); - session:close{ - condition = "invalid-from"; - text = "Component tried to send from address <"..tostring(from) - .."> which is not in domain <"..tostring(session.host)..">"; - }; - return; - end - end - else - stanza.attr.from = session.host; - end - if not stanza.attr.to then - session.log("warn", "Rejecting stanza with no 'to' address"); - session.send(st.error_reply(stanza, "modify", "bad-request", "Components MUST specify a 'to' address on stanzas")); - return; - end - end - return core_process_stanza(session, stanza); -end - ---- Closing a component connection -local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; -local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; -local function session_close(session, reason) - if session.destroyed then return; end - local log = session.log or log; - if session.conn then - if session.notopen then - session.send(""); - session.send(st.stanza("stream:stream", default_stream_attr):top_tag()); - end - if reason then - if type(reason) == "string" then -- assume stream error - log("info", "Disconnecting component, is: %s", reason); - session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' })); - elseif type(reason) == "table" then - if reason.condition then - local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up(); - if reason.text then - stanza:tag("text", stream_xmlns_attr):text(reason.text):up(); - end - if reason.extra then - stanza:add_child(reason.extra); - end - log("info", "Disconnecting component, is: %s", tostring(stanza)); - session.send(stanza); - elseif reason.name then -- a stanza - log("info", "Disconnecting component, is: %s", tostring(reason)); - session.send(reason); - end - end - end - session.send(""); - session.conn:close(); - component_listener.ondisconnect(session.conn, "stream error"); - end -end - ---- Component connlistener -function component_listener.onconnect(conn) - local _send = conn.write; - local session = { type = "component", conn = conn, send = function (data) return _send(conn, tostring(data)); end }; - - -- Logging functions -- - local conn_name = "jcp"..tostring(conn):match("[a-f0-9]+$"); - session.log = logger.init(conn_name); - session.close = session_close; - - session.log("info", "Incoming Jabber component connection"); - - local stream = new_xmpp_stream(session, stream_callbacks); - session.stream = stream; - - session.notopen = true; - - function session.reset_stream() - session.notopen = true; - session.stream:reset(); - end - - function session.data(conn, data) - local ok, err = stream:feed(data); - if ok then return; end - log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); - session:close("not-well-formed"); - end - - session.dispatch_stanza = stream_callbacks.handlestanza; - - sessions[conn] = session; -end -function component_listener.onincoming(conn, data) - local session = sessions[conn]; - session.data(conn, data); -end -function component_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)); - if session.on_destroy then session:on_destroy(err); end - sessions[conn] = nil; - for k in pairs(session) do - if k ~= "log" and k ~= "close" then - session[k] = nil; - end - end - session.destroyed = true; - session = nil; - end -end - -connlisteners.register('xmppcomponent', component_listener); diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua index f7d09930..96d55e40 100644 --- a/plugins/mod_component.lua +++ b/plugins/mod_component.lua @@ -6,95 +6,298 @@ -- COPYING file in the source package for more information. -- -if module:get_host_type() ~= "component" then - error("Don't load mod_component manually, it should be for a component, please see http://prosody.im/doc/components", 0); -end +module:set_global(); local t_concat = table.concat; +local logger = require "util.logger"; local sha1 = require "util.hashes".sha1; local st = require "util.stanza"; +local jid_split = require "util.jid".split; +local new_xmpp_stream = require "util.xmppstream".new; +local uuid_gen = require "util.uuid".generate; + + local log = module._log; -local main_session, send; +local sessions = module:shared("sessions"); -local function on_destroy(session, err) - if main_session == session then - connected = false; - main_session = nil; +function module.add_host(module) + if module:get_host_type() ~= "component" then + error("Don't load mod_component manually, it should be for a component, please see http://prosody.im/doc/components", 0); + end + + local env = module.environment; + env.connected = false; + + local send; + + local function on_destroy(session, err) + env.connected = false; send = nil; session.on_destroy = nil; end + + -- Handle authentication attempts by component + local function handle_component_auth(event) + local session, stanza = event.origin, event.stanza; + + if session.type ~= "component" then return; end + + if (not session.host) or #stanza.tags > 0 then + (session.log or log)("warn", "Invalid component handshake for host: %s", session.host); + session:close("not-authorized"); + return true; + end + + local secret = module:get_option("component_secret"); + if not secret then + (session.log or log)("warn", "Component attempted to identify as %s, but component_secret is not set", session.host); + session:close("not-authorized"); + return true; + end + + local supplied_token = t_concat(stanza); + local calculated_token = sha1(session.streamid..secret, true); + if supplied_token:lower() ~= calculated_token:lower() then + module:log("info", "Component authentication failed for %s", session.host); + session:close{ condition = "not-authorized", text = "Given token does not match calculated token" }; + return true; + end + + if env.connected then + module:log("error", "Second component attempted to connect, denying connection"); + session:close{ condition = "conflict", text = "Component already connected" }; + end + + env.connected = true; + send = session.send; + session.on_destroy = on_destroy; + session.component_validate_from = module:get_option_boolean("validate_from_addresses", true); + module:log("info", "External component successfully authenticated"); + session.send(st.stanza("handshake")); + + return true; + end + module:hook("stanza/jabber:component:accept:handshake", handle_component_auth); + + -- Handle stanzas addressed to this component + local function handle_stanza(event) + local stanza = event.stanza; + if send then + stanza.attr.xmlns = nil; + send(stanza); + else + module:log("warn", "Component not connected, bouncing error for: %s", stanza:top_tag()); + if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then + event.origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable")); + end + end + return true; + end + + module:hook("iq/bare", handle_stanza, -1); + module:hook("message/bare", handle_stanza, -1); + module:hook("presence/bare", handle_stanza, -1); + module:hook("iq/full", handle_stanza, -1); + module:hook("message/full", handle_stanza, -1); + module:hook("presence/full", handle_stanza, -1); + module:hook("iq/host", handle_stanza, -1); + module:hook("message/host", handle_stanza, -1); + module:hook("presence/host", handle_stanza, -1); end -local function handle_stanza(event) - local stanza = event.stanza; - if send then - stanza.attr.xmlns = nil; - send(stanza); - else - log("warn", "Component not connected, bouncing error for: %s", stanza:top_tag()); - if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then - event.origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable")); +--- Network and stream part --- + +local xmlns_component = 'jabber:component:accept'; + +local listener = {}; + +--- Callbacks/data for xmppstream to handle streams for us --- + +local stream_callbacks = { default_ns = xmlns_component }; + +local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; + +function stream_callbacks.error(session, error, data, data2) + if session.destroyed then return; end + module:log("warn", "Error processing component stream: "..tostring(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:close("not-well-formed"); + elseif error == "stream-error" then + local condition, text = "undefined-condition"; + for child in data:children() do + if child.attr.xmlns == xmlns_xmpp_streams then + if child.name ~= "text" then + condition = child.name; + else + text = child:get_text(); + end + if condition ~= "undefined-condition" and text then + break; + end + end end + text = condition .. (text and (" ("..text..")") or ""); + session.log("info", "Session closed by remote with error: %s", text); + session:close(nil, text); end - return true; end -module:hook("iq/bare", handle_stanza, -1); -module:hook("message/bare", handle_stanza, -1); -module:hook("presence/bare", handle_stanza, -1); -module:hook("iq/full", handle_stanza, -1); -module:hook("message/full", handle_stanza, -1); -module:hook("presence/full", handle_stanza, -1); -module:hook("iq/host", handle_stanza, -1); -module:hook("message/host", handle_stanza, -1); -module:hook("presence/host", handle_stanza, -1); - ---- Handle authentication attempts by components -function handle_component_auth(event) - local session, stanza = event.origin, event.stanza; - - if session.type ~= "component" then return; end - if main_session == session then return; end +function stream_callbacks.streamopened(session, attr) + if not hosts[attr.to].modules.component then + session:close{ condition = "host-unknown", text = tostring(attr.to).." does not match any configured external components" }; + return; + end + session.host = attr.to; + session.streamid = uuid_gen(); + session.notopen = nil; + -- Return stream header + session.send(st.stanza("stream:stream", { xmlns=xmlns_component, + ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.host }):top_tag()); +end - if (not session.host) or #stanza.tags > 0 then - (session.log or log)("warn", "Invalid component handshake for host: %s", session.host); - session:close("not-authorized"); - return true; +function stream_callbacks.streamclosed(session) + session.log("debug", "Received "); + session:close(); +end + +local core_process_stanza = core_process_stanza; + +function stream_callbacks.handlestanza(session, stanza) + -- Namespaces are icky. + if not stanza.attr.xmlns and stanza.name == "handshake" then + stanza.attr.xmlns = xmlns_component; end - - local secret = module:get_option("component_secret"); - if not secret then - (session.log or log)("warn", "Component attempted to identify as %s, but component_secret is not set", session.host); - session:close("not-authorized"); - return true; + if not stanza.attr.xmlns or stanza.attr.xmlns == "jabber:client" then + local from = stanza.attr.from; + if from then + if session.component_validate_from then + local _, domain = jid_split(stanza.attr.from); + if domain ~= session.host then + -- Return error + session.log("warn", "Component sent stanza with missing or invalid 'from' address"); + session:close{ + condition = "invalid-from"; + text = "Component tried to send from address <"..tostring(from) + .."> which is not in domain <"..tostring(session.host)..">"; + }; + return; + end + end + else + stanza.attr.from = session.host; -- COMPAT: Strictly we shouldn't allow this + end + if not stanza.attr.to then + session.log("warn", "Rejecting stanza with no 'to' address"); + session.send(st.error_reply(stanza, "modify", "bad-request", "Components MUST specify a 'to' address on stanzas")); + return; + end end - - local supplied_token = t_concat(stanza); - local calculated_token = sha1(session.streamid..secret, true); - if supplied_token:lower() ~= calculated_token:lower() then - log("info", "Component authentication failed for %s", session.host); - session:close{ condition = "not-authorized", text = "Given token does not match calculated token" }; - return true; + return core_process_stanza(session, stanza); +end + +--- Closing a component connection +local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; +local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; +local function session_close(session, reason) + if session.destroyed then return; end + local log = session.log or log; + if session.conn then + if session.notopen then + session.send(""); + session.send(st.stanza("stream:stream", default_stream_attr):top_tag()); + end + if reason then + if type(reason) == "string" then -- assume stream error + module:log("info", "Disconnecting component, is: %s", reason); + session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' })); + elseif type(reason) == "table" then + if reason.condition then + local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up(); + if reason.text then + stanza:tag("text", stream_xmlns_attr):text(reason.text):up(); + end + if reason.extra then + stanza:add_child(reason.extra); + end + module:log("info", "Disconnecting component, is: %s", tostring(stanza)); + session.send(stanza); + elseif reason.name then -- a stanza + module:log("info", "Disconnecting component, is: %s", tostring(reason)); + session.send(reason); + end + end + end + session.send(""); + session.conn:close(); + listener.ondisconnect(session.conn, "stream error"); end +end + +--- Component connlistener + +function listener.onconnect(conn) + local _send = conn.write; + local session = { type = "component", conn = conn, send = function (data) return _send(conn, tostring(data)); end }; + + -- Logging functions -- + local conn_name = "jcp"..tostring(conn):match("[a-f0-9]+$"); + session.log = logger.init(conn_name); + session.close = session_close; - -- If component not already created for this host, create one now - if not main_session then - connected = true; - send = session.send; - main_session = session; - session.on_destroy = on_destroy; - session.component_validate_from = module:get_option_boolean("validate_from_addresses", true); - log("info", "Component successfully authenticated: %s", session.host); - session.send(st.stanza("handshake")); - else -- TODO: Implement stanza distribution - log("error", "Multiple components bound to the same address, first one wins: %s", session.host); - session:close{ condition = "conflict", text = "Component already connected" }; + session.log("info", "Incoming Jabber component connection"); + + local stream = new_xmpp_stream(session, stream_callbacks); + session.stream = stream; + + session.notopen = true; + + function session.reset_stream() + session.notopen = true; + session.stream:reset(); + end + + function session.data(conn, data) + local ok, err = stream:feed(data); + if ok then return; end + module:log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); + session:close("not-well-formed"); end - return true; + session.dispatch_stanza = stream_callbacks.handlestanza; + + sessions[conn] = session; +end +function listener.onincoming(conn, data) + local session = sessions[conn]; + session.data(conn, data); +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)); + if session.on_destroy then session:on_destroy(err); end + sessions[conn] = nil; + for k in pairs(session) do + if k ~= "log" and k ~= "close" then + session[k] = nil; + end + end + session.destroyed = true; + session = nil; + end end -module:hook("stanza/jabber:component:accept:handshake", handle_component_auth); +module:add_item("net-provider", { + name = "component"; + listener = listener; + default_port = 5347; + multiplex = { + pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:component%1.*>"; + }; +}); -- cgit v1.2.3 From e0a85aed90d0d4a5a6bcf8220cef38f15ec3844b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 22:52:15 +0100 Subject: moduleapi: Have modules internally store a reference to shared tables they use, to ensure they don't get collected while any module that had access to that table is still loaded (thanks Zash) --- core/moduleapi.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index a577c07a..7a3bd1c8 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -135,6 +135,7 @@ end -- Intentionally does not allow the table at a path to be _set_, it -- is auto-created if it does not exist. function api:shared(...) + if not self.shared_data then self.shared_data = {}; end local paths = { n = select("#", ...), ... }; local data_array = {}; local default_path_components = { self.host, self.name }; @@ -150,6 +151,7 @@ function api:shared(...) shared_data[path] = shared; end t_insert(data_array, shared); + self.shared_data[path] = shared; end return unpack(data_array); end -- cgit v1.2.3 From 60b60d9c30833600635e3a695362f903c812cd36 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 22:53:22 +0100 Subject: modulemanager: For children of shared modules, set module.environment to the empty environment for that module (useful to expose data or APIs at host.modules[module]) --- core/modulemanager.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f263816b..aca314c0 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -144,6 +144,7 @@ local function do_load_module(host, module_name) local ok, result, module_err = call_module_method(mod, "add_host", host_module_api); if not ok or result == false then return nil, ok and module_err or result; end local host_module = setmetatable({ module = host_module_api }, { __index = mod }); + host_module.module.environment = host_module; modulemap[host][module_name] = host_module; return host_module; end -- cgit v1.2.3 From 938276d5ca8fe272ce7c1c6fafe7afc91cc0596b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 22:54:55 +0100 Subject: multiplex_listener: Remove (already ported to mod_net_multiplex) --- net/multiplex_listener.lua | 50 ---------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 net/multiplex_listener.lua diff --git a/net/multiplex_listener.lua b/net/multiplex_listener.lua deleted file mode 100644 index b515ccce..00000000 --- a/net/multiplex_listener.lua +++ /dev/null @@ -1,50 +0,0 @@ - -local connlisteners_register = require "net.connlisteners".register; -local connlisteners_get = require "net.connlisteners".get; - -local httpserver_listener = connlisteners_get("httpserver"); -local xmppserver_listener = connlisteners_get("xmppserver"); -local xmppclient_listener = connlisteners_get("xmppclient"); -local xmppcomponent_listener = connlisteners_get("xmppcomponent"); - -local server = { default_mode = "*a" }; - -local buffer = {}; - -function server.onincoming(conn, data) - if not data then return; end - local buf = buffer[conn]; - buffer[conn] = nil; - buf = buf and buf..data or data; - if buf:match("^[a-zA-Z]") then - local listener = httpserver_listener; - conn:setlistener(listener); - local onconnect = listener.onconnect; - if onconnect then onconnect(conn) end - listener.onincoming(conn, buf); - elseif buf:match(">") then - local listener; - local xmlns = buf:match("%sxmlns%s*=%s*['\"]([^'\"]*)"); - if xmlns == "jabber:server" then - listener = xmppserver_listener; - elseif xmlns == "jabber:component:accept" then - listener = xmppcomponent_listener; - else - listener = xmppclient_listener; - end - conn:setlistener(listener); - local onconnect = listener.onconnect; - if onconnect then onconnect(conn) end - listener.onincoming(conn, buf); - elseif #buf > 1024 then - conn:close(); - else - buffer[conn] = buf; - end -end - -function server.ondisconnect(conn, err) - buffer[conn] = nil; -- warn if no buffer? -end - -connlisteners_register("multiplex", server); -- cgit v1.2.3 From d74cf643a6dc8f0eb12a8af4eba82f944c41aaf8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Apr 2012 14:54:36 +0100 Subject: mod_motd: Use presence/bare to catch a client's initial presence and send the MOTD then (fixes #282) --- plugins/mod_motd.lua | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/mod_motd.lua b/plugins/mod_motd.lua index 646bf8cd..39b74de9 100644 --- a/plugins/mod_motd.lua +++ b/plugins/mod_motd.lua @@ -18,12 +18,13 @@ local st = require "util.stanza"; motd_text = motd_text:gsub("^%s*(.-)%s*$", "%1"):gsub("\n%s+", "\n"); -- Strip indentation from the config -module:hook("resource-bind", function (event) - local session = event.session; - local motd_stanza = - st.message({ to = jid_join(session.username, session.host, session.resource), from = motd_jid }) - :tag("body"):text(motd_text); - core_route_stanza(hosts[host], motd_stanza); - module:log("debug", "MOTD send to user %s@%s", session.username, session.host); - -end); +module:hook("presence/bare", function (event) + local session, stanza = event.origin, event.stanza; + if not session.presence and not stanza.attr.type then + local motd_stanza = + st.message({ to = session.full_jid, from = motd_jid }) + :tag("body"):text(motd_text); + core_route_stanza(hosts[host], motd_stanza); + module:log("debug", "MOTD send to user %s", session.full_jid); + end +end, 1); -- cgit v1.2.3 From 67f1c12639abc5053bda02077abba5bf02ca0c2f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Apr 2012 16:35:26 +0100 Subject: mod_component: Handle component connecting to non-existent host --- plugins/mod_component.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua index 96d55e40..5b1eefc7 100644 --- a/plugins/mod_component.lua +++ b/plugins/mod_component.lua @@ -149,7 +149,7 @@ function stream_callbacks.error(session, error, data, data2) end function stream_callbacks.streamopened(session, attr) - if not hosts[attr.to].modules.component then + if not hosts[attr.to] or not hosts[attr.to].modules.component then session:close{ condition = "host-unknown", text = tostring(attr.to).." does not match any configured external components" }; return; end -- cgit v1.2.3 From e5b647c77cc4360aa843b7d81e70a3d84ddbfb95 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 22 Apr 2012 23:43:34 +0500 Subject: net.httpserver: Comment dependency on connlisteners. Further cleanup to follow. --- net/httpserver.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/httpserver.lua b/net/httpserver.lua index 44e8e24d..0f5a4186 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -10,8 +10,8 @@ local url_parse = require "socket.url".parse; local httpstream_new = require "util.httpstream".new; -local connlisteners_start = require "net.connlisteners".start; -local connlisteners_get = require "net.connlisteners".get; +--local connlisteners_start = require "net.connlisteners".start; +--local connlisteners_get = require "net.connlisteners".get; local listener; local t_insert, t_concat = table.insert, table.concat; @@ -164,7 +164,7 @@ end function destroy_request(request) log("debug", "Destroying request %s", request.id); - listener = listener or connlisteners_get("httpserver"); + --listener = listener or connlisteners_get("httpserver"); if not request.destroyed then request.destroyed = true; if request.on_destroy then @@ -186,7 +186,7 @@ function new(params) http_server = { handlers = {} }; http_servers[params.port] = http_server; -- We weren't already listening on this port, so start now - connlisteners_start("httpserver", params); + --connlisteners_start("httpserver", params); end if params.base then http_server.handlers[params.base] = params.handler; -- cgit v1.2.3 From 608d074ba071a82cb409eb5a8b7aa5b5c4bd4eb4 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 22 Apr 2012 23:44:21 +0500 Subject: net.http.server: Fix legacy net.httpserver fallback (httpserver is no longer a global). --- net/http/server.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index 1f61c7b9..3a0cb53a 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -13,6 +13,7 @@ local debug = debug; local tostring = tostring; local codes = require "net.http.codes"; local _G = _G; +local legacy_httpserver = require "net.httpserver"; local _M = {}; @@ -159,9 +160,9 @@ end function _M.legacy_handler(request, response) log("debug", "Invoking legacy handler"); local base = request.path:match("^/([^/?]+)"); - local legacy_server = _G.httpserver and _G.httpserver.new.http_servers[5280]; + local legacy_server = legacy_httpserver and legacy_httpserver.new.http_servers[5280]; local handler = legacy_server and legacy_server.handlers[base]; - if not handler then handler = _G.httpserver and _G.httpserver.set_default_handler.default_handler; end + if not handler then handler = legacy_httpserver and legacy_httpserver.set_default_handler.default_handler; end if handler then -- add legacy properties to request object request.url = { path = request.path }; -- cgit v1.2.3 From 5f18da1e08389a8159b8ce826aab67b035b00559 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:09:35 +0100 Subject: moduleapi: module:provides(): Fix usage of wrong table --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 7a3bd1c8..61926017 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -298,7 +298,7 @@ end function api:provides(name, item) if not item then item = self.environment; end if not item.name then - local item_name = module.name; + local item_name = self.name; -- Strip a provider prefix to find the item name -- (e.g. "auth_foo" -> "foo" for an auth provider) if item_name:find(name.."_", 1, true) == 1 then -- cgit v1.2.3 From f5b07374469842b3b800bf34c4ba4e85dcbc4ad0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:10:04 +0100 Subject: moduleapi: module:provides(): Add "-provider" onto the key name --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 61926017..2223f4d0 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -306,7 +306,7 @@ function api:provides(name, item) end item.name = item_name; end - self:add_item(name, item); + self:add_item(name.."-provider", item); end function api:send(stanza) -- cgit v1.2.3 From c4ff9b7e1086e93be9320dd0cdd8aebf7df2b4ad Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:11:10 +0100 Subject: modulemanager: Report errors that happen when loading a shared module onto its original host --- core/modulemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index aca314c0..f0648667 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -186,7 +186,7 @@ local function do_load_module(host, module_name) end if host ~= api_instance.host and module_has_method(pluginenv, "add_host") then -- Now load the module again onto the host it was originally being loaded on - do_load_module(host, module_name); + ok, err = do_load_module(host, module_name); end end end -- cgit v1.2.3 From 1e7d11c689a927e9730a101f2f9050b84eb5216e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:15:07 +0100 Subject: moduleapi: module:depends(): Load shared modules onto the current host even if they are loaded globally already --- core/moduleapi.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 2223f4d0..e680f615 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -120,6 +120,9 @@ function api:depends(name) end); end local mod = modulemanager.get_module(self.host, name) or modulemanager.get_module("*", name); + if mod and mod.module.host == "*" and modulemanager.module_has_method(mod, "add_host") then + mod = nil; -- This is a shared module, so we still want to load it on our host + end if not mod then local err; mod, err = modulemanager.load(self.host, name); -- cgit v1.2.3 From 46e69d7017eb793885f52a5fd259a85cfdbc78ed Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:16:59 +0100 Subject: mod_http: Revamp module for new API and config --- plugins/mod_http.lua | 106 ++++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 24ef8ab3..d8e1e10a 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -1,6 +1,6 @@ -- Prosody IM --- Copyright (C) 2008-2010 Matthew Wild --- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2008-2012 Matthew Wild +-- Copyright (C) 2008-2012 Waqas Hussain -- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. @@ -8,67 +8,68 @@ module:set_global(); ---local sessions = module:shared("sessions"); +local parse_url = require "socket.url".parse; +local server = require "net.http.server"; ---[[function listener.associate_session(conn, session) - sessions[conn] = session; -end]] +local function normalize_path(path) + if path:sub(1,1) ~= "/" then path = "/"..path; end + if path:sub(-1,-1) == "/" then path = path:sub(1, -2); end + return path; +end -local NULL = {}; -local handlers = {}; +local function get_http_event(http_module, app_name, key) + local method, path = key:match("^(%S+)%s+(.+)$"); + if not method and key:sub(1,1) == "/" then + method, path = "GET", key; + else + return nil; + end + path = normalize_path(path); + local app_path = normalize_path(http_module:get_option_string(app_name.."_http_path", "/"..app_name)); + return method:upper().." "..http_module.host..app_path..path; +end -function build_handlers(host) - if not hosts[host] then return; end - local h = {}; - handlers[host] = h; - - for mod_name, module in pairs(modulemanager.get_modules(host)) do - module = module.module; - if module.items then - for _, item in ipairs(module.items["http-handler"] or NULL) do - local previous = handlers[item.path]; - if not previous and item.path then - h[item.path] = item; +function module.add_host(module) + local apps = {}; + module.environment.apps = apps; + local function http_app_added(event) + local app_name = event.item.name; + if not app_name then + -- TODO: Link to docs + module:log("error", "HTTP app has no 'name', add one or use module:provides('http', app)"); + return; + end + apps[app_name] = apps[app_name] or {}; + local app_handlers = apps[app_name]; + for key, handler in pairs(event.item.route or {}) do + local event_name = get_http_event(module, app_name, key); + if event_name then + if not app_handlers[event_name] then + app_handlers[event_name] = handler; + server.add_handler(event_name, handler); + else + module:log("warn", "App %s added handler twice for '%s', ignoring", app_name, event_name); end + else + module:log("error", "Invalid route in %s: %q", app_name, key); end end end - - return h; -end -function clear_handlers(event) - handlers[event.source.host] = nil; -end -function get_handler(host, path) - local h = handlers[host] or build_handlers(host); - if h then - local item = h[path]; - return item and item.handler; - end -end -module:handle_items("http-handler", clear_handlers, clear_handlers, false); - -function http_handler(event) - local request, response = event.request, event.response; - - local handler = get_handler(request.headers.host:match("[^:]*"):lower(), request.path:match("[^?]*")); - if handler then - handler(request, response); - return true; + + local function http_app_removed(event) + local app_handlers = apps[event.item.name]; + apps[event.item.name] = nil; + for event, handler in pairs(app_handlers) do + server.remove_handler(event, handler); + end end + + module:handle_items("http-provider", http_app_added, http_app_removed); end -local server = require "net.http.server"; -local listener = server.listener; -server.add_handler("*", http_handler); -function module.unload() - server.remove_handler("*", http_handler); -end ---require "net.http.server".listen_on(8080); - module:add_item("net-provider", { name = "http"; - listener = listener; + listener = server.listener; default_port = 5280; multiplex = { pattern = "^[A-Z]"; @@ -77,7 +78,8 @@ module:add_item("net-provider", { module:add_item("net-provider", { name = "https"; - listener = listener; + listener = server.listener; + default_port = 5281; encryption = "ssl"; multiplex = { pattern = "^[A-Z]"; -- cgit v1.2.3 From 566fc32a4e01ed5c85f30d09e12bb3a56f7d6081 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 15:38:36 +0100 Subject: modulemanager: Set module.loaded = false on unload --- core/modulemanager.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f0648667..bf1e1924 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -110,6 +110,7 @@ local function do_unload_module(host, name) end end end + mod.module.loaded = false; modulemap[host][name] = nil; return true; end -- cgit v1.2.3 From 78051fedaca3cff787f530aa9cab17d719393a79 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 15:38:40 +0100 Subject: moduleapi: Add module:add_timer(delay, callback) - automatically halts the timer on module unload --- core/moduleapi.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index e680f615..d16ee410 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -316,4 +316,11 @@ function api:send(stanza) return core_post_stanza(hosts[self.host], stanza); end +function api:add_timer(delay, callback) + return timer.add_task(delay, function (t) + if self.loaded == false then return; end + return callback(t); + end); +end + return api; -- cgit v1.2.3 From ead2ecb05d1867fc2f4e7a5d76ba4052205a6a23 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 18:23:49 +0100 Subject: mod_http: Support for default_path in apps --- plugins/mod_http.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index d8e1e10a..cd1984c0 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -17,7 +17,7 @@ local function normalize_path(path) return path; end -local function get_http_event(http_module, app_name, key) +local function get_http_event(host, app_path, key) local method, path = key:match("^(%S+)%s+(.+)$"); if not method and key:sub(1,1) == "/" then method, path = "GET", key; @@ -25,15 +25,17 @@ local function get_http_event(http_module, app_name, key) return nil; end path = normalize_path(path); - local app_path = normalize_path(http_module:get_option_string(app_name.."_http_path", "/"..app_name)); - return method:upper().." "..http_module.host..app_path..path; + return method:upper().." "..host..app_path..path; end function module.add_host(module) + local host = module.host; local apps = {}; module.environment.apps = apps; local function http_app_added(event) local app_name = event.item.name; + local default_app_path = event.item.default_path or "/"..app_name; + local app_path = normalize_path(module:get_option_string(app_name.."_http_path", default_app_path)); if not app_name then -- TODO: Link to docs module:log("error", "HTTP app has no 'name', add one or use module:provides('http', app)"); @@ -42,7 +44,7 @@ function module.add_host(module) apps[app_name] = apps[app_name] or {}; local app_handlers = apps[app_name]; for key, handler in pairs(event.item.route or {}) do - local event_name = get_http_event(module, app_name, key); + local event_name = get_http_event(host, app_path, key); if event_name then if not app_handlers[event_name] then app_handlers[event_name] = handler; -- cgit v1.2.3 From b19b3bb07d6d7da9ed4562945db3038214aa9baf Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 21:29:18 +0100 Subject: net.http.server: Support for wildcard events (events that end with '/*') --- net/http/server.lua | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/net/http/server.lua b/net/http/server.lua index 3a0cb53a..94487e5e 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -22,6 +22,57 @@ local handlers = {}; local listener = {}; +local function is_wildcard_event(event) + return event:sub(-2, -1) == "/*"; +end +local function is_wildcard_match(wildcard_event, event) + log("debug", "comparing %q with %q", wildcard_event:sub(1, -2), event:sub(1, #wildcard_event-1)); + return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1); +end + +local event_map = events._event_map; +setmetatable(events._handlers, { + __index = function (handlers, curr_event) + if is_wildcard_event(curr_event) then return; end -- Wildcard events cannot be fired + -- Find all handlers that could match this event, sort them + -- and then put the array into handlers[event] + local matching_handlers_set = {}; + local handlers_array = {}; + for event, handlers_set in pairs(event_map) do + if event == curr_event or + is_wildcard_event(event) and is_wildcard_match(event, curr_event) then + for handler, priority in pairs(handlers_set) do + matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), priority }; + table.insert(handlers_array, handler); + end + end + end + if #handlers_array == 0 then return; end + table.sort(handlers_array, function(b, a) + local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; + for i = 1, #a_score do + if a ~= b then -- If equal, compare next score value + return a_score[i] < b_score[i]; + end + end + return false; + end); + handlers[curr_event] = handlers_array; + return handlers_array; + end; + __newindex = function (handlers, curr_event, handlers_array) + if handlers_array == nil + and is_wildcard_event(curr_event) then + -- Invalidate all matching + for event in pairs(handlers) do + if is_wildcard_match(curr_event, event) then + handlers[event] = nil; + end + end + end + end; +}); + local handle_request; local _1, _2, _3; local function _handle_request() return handle_request(_1, _2, _3); end -- cgit v1.2.3 From a2e8ab35556fa6fd6591c3bf64f2f9e873b2a4f4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 21:34:05 +0100 Subject: mod_http: Pass portion of path that matched wildcard to wildcard handlers, as a second parameter --- plugins/mod_http.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index cd1984c0..3dcb14d7 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -46,6 +46,14 @@ function module.add_host(module) for key, handler in pairs(event.item.route or {}) do local event_name = get_http_event(host, app_path, key); if event_name then + if event_name:sub(-2, -1) == "/*" then + local base_path = event_name:match("/(.+)/*$"); + local _handler = handler; + handler = function (event) + local path = event.request.path:sub(#base_path+1); + return _handler(event, path); + end; + end if not app_handlers[event_name] then app_handlers[event_name] = handler; server.add_handler(event_name, handler); -- cgit v1.2.3 From e34e6c0a91c97f30c09a4b2b55596c68eea28863 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Apr 2012 23:36:50 +0200 Subject: mod_httpserver: Adapt to use the new HTTP API --- plugins/mod_httpserver.lua | 67 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/plugins/mod_httpserver.lua b/plugins/mod_httpserver.lua index 654aff06..bc9cbcd3 100644 --- a/plugins/mod_httpserver.lua +++ b/plugins/mod_httpserver.lua @@ -6,19 +6,17 @@ -- COPYING file in the source package for more information. -- - -local httpserver = require "net.httpserver"; +module:depends("http"); local lfs = require "lfs"; local open = io.open; -local t_concat = table.concat; local stat = lfs.attributes; -local http_base = config.get("*", "core", "http_path") or "www_files"; +local http_base = module:get_option_string("http_path", "www_files"); -local response_400 = { status = "400 Bad Request", body = "

Bad Request

Sorry, we didn't understand your request :(" }; -local response_403 = { status = "403 Forbidden", body = "

Forbidden

You don't have permission to view the contents of this directory :(" }; -local response_404 = { status = "404 Not Found", body = "

Page Not Found

Sorry, we couldn't find what you were looking for :(" }; +local response_400 = "

Bad Request

Sorry, we didn't understand your request :("; +local response_403 = "

Forbidden

You don't have permission to view the contents of this directory :("; +local response_404 = "

Page Not Found

Sorry, we couldn't find what you were looking for :("; -- TODO: Should we read this from /etc/mime.types if it exists? (startup time...?) local mime_map = { @@ -49,49 +47,40 @@ local function preprocess_path(path) return path; end -function serve_file(path) +function serve_file(request, path) + local response = request.response; + path = path and preprocess_path(path); + if not path then + response.status = 400; + return response:send(response_400); + end local full_path = http_base..path; if stat(full_path, "mode") == "directory" then if stat(full_path.."/index.html", "mode") == "file" then - return serve_file(path.."/index.html"); + return serve_file(request, path.."/index.html"); end - return response_403; + response.status = 403; + return response:send(response_403); end local f, err = open(full_path, "rb"); - if not f then return response_404; end + if not f then + response.status = 404; + return response:send(response_404.."
"..tostring(err)); + end local data = f:read("*a"); f:close(); if not data then - return response_403; + response.status = 403; + return response:send(response_403); end local ext = path:match("%.([^.]*)$"); - local mime = mime_map[ext]; -- Content-Type should be nil when not known - return { - headers = { ["Content-Type"] = mime; }; - body = data; - }; -end - -local function handle_file_request(method, body, request) - local path = preprocess_path(request.url.path); - if not path then return response_400; end - path = path:gsub("^/[^/]+", ""); -- Strip /files/ - return serve_file(path); + response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known + return response:send(data); end -local function handle_default_request(method, body, request) - local path = preprocess_path(request.url.path); - if not path then return response_400; end - return serve_file(path); -end +module:provides("http", { + route = { + ["/*"] = serve_file; + }; +}); -local function setup() - local ports = config.get(module.host, "core", "http_ports") or { 5280 }; - httpserver.set_default_handler(handle_default_request); - httpserver.new_from_config(ports, handle_file_request, { base = "files" }); -end -if prosody.start_time then -- already started - setup(); -else - prosody.events.add_handler("server-started", setup); -end -- cgit v1.2.3 From 7ca291e03f0485ce227715ce00b787e1586098c9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Apr 2012 23:37:43 +0200 Subject: mod_httpserver: Rename to mod_http_files --- plugins/mod_http_files.lua | 86 ++++++++++++++++++++++++++++++++++++++++++++++ plugins/mod_httpserver.lua | 86 ---------------------------------------------- 2 files changed, 86 insertions(+), 86 deletions(-) create mode 100644 plugins/mod_http_files.lua delete mode 100644 plugins/mod_httpserver.lua diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua new file mode 100644 index 00000000..bc9cbcd3 --- /dev/null +++ b/plugins/mod_http_files.lua @@ -0,0 +1,86 @@ +-- 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:depends("http"); +local lfs = require "lfs"; + +local open = io.open; +local stat = lfs.attributes; + +local http_base = module:get_option_string("http_path", "www_files"); + +local response_400 = "

Bad Request

Sorry, we didn't understand your request :("; +local response_403 = "

Forbidden

You don't have permission to view the contents of this directory :("; +local response_404 = "

Page Not Found

Sorry, we couldn't find what you were looking for :("; + +-- TODO: Should we read this from /etc/mime.types if it exists? (startup time...?) +local mime_map = { + html = "text/html"; + htm = "text/html"; + xml = "text/xml"; + xsl = "text/xml"; + txt = "text/plain; charset=utf-8"; + js = "text/javascript"; + css = "text/css"; +}; + +local function preprocess_path(path) + if path:sub(1,1) ~= "/" then + path = "/"..path; + end + local level = 0; + for component in path:gmatch("([^/]+)/") do + if component == ".." then + level = level - 1; + elseif component ~= "." then + level = level + 1; + end + if level < 0 then + return nil; + end + end + return path; +end + +function serve_file(request, path) + local response = request.response; + path = path and preprocess_path(path); + if not path then + response.status = 400; + return response:send(response_400); + end + local full_path = http_base..path; + if stat(full_path, "mode") == "directory" then + if stat(full_path.."/index.html", "mode") == "file" then + return serve_file(request, path.."/index.html"); + end + response.status = 403; + return response:send(response_403); + end + local f, err = open(full_path, "rb"); + if not f then + response.status = 404; + return response:send(response_404.."
"..tostring(err)); + end + local data = f:read("*a"); + f:close(); + if not data then + response.status = 403; + return response:send(response_403); + end + local ext = path:match("%.([^.]*)$"); + response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known + return response:send(data); +end + +module:provides("http", { + route = { + ["/*"] = serve_file; + }; +}); + diff --git a/plugins/mod_httpserver.lua b/plugins/mod_httpserver.lua deleted file mode 100644 index bc9cbcd3..00000000 --- a/plugins/mod_httpserver.lua +++ /dev/null @@ -1,86 +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:depends("http"); -local lfs = require "lfs"; - -local open = io.open; -local stat = lfs.attributes; - -local http_base = module:get_option_string("http_path", "www_files"); - -local response_400 = "

Bad Request

Sorry, we didn't understand your request :("; -local response_403 = "

Forbidden

You don't have permission to view the contents of this directory :("; -local response_404 = "

Page Not Found

Sorry, we couldn't find what you were looking for :("; - --- TODO: Should we read this from /etc/mime.types if it exists? (startup time...?) -local mime_map = { - html = "text/html"; - htm = "text/html"; - xml = "text/xml"; - xsl = "text/xml"; - txt = "text/plain; charset=utf-8"; - js = "text/javascript"; - css = "text/css"; -}; - -local function preprocess_path(path) - if path:sub(1,1) ~= "/" then - path = "/"..path; - end - local level = 0; - for component in path:gmatch("([^/]+)/") do - if component == ".." then - level = level - 1; - elseif component ~= "." then - level = level + 1; - end - if level < 0 then - return nil; - end - end - return path; -end - -function serve_file(request, path) - local response = request.response; - path = path and preprocess_path(path); - if not path then - response.status = 400; - return response:send(response_400); - end - local full_path = http_base..path; - if stat(full_path, "mode") == "directory" then - if stat(full_path.."/index.html", "mode") == "file" then - return serve_file(request, path.."/index.html"); - end - response.status = 403; - return response:send(response_403); - end - local f, err = open(full_path, "rb"); - if not f then - response.status = 404; - return response:send(response_404.."
"..tostring(err)); - end - local data = f:read("*a"); - f:close(); - if not data then - response.status = 403; - return response:send(response_403); - end - local ext = path:match("%.([^.]*)$"); - response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known - return response:send(data); -end - -module:provides("http", { - route = { - ["/*"] = serve_file; - }; -}); - -- cgit v1.2.3 From 3fd98627c00a102fc7cbb4657be7e872f953b869 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Apr 2012 00:17:15 +0200 Subject: mod_http_files: Rename argument to reflect what it actually is --- plugins/mod_http_files.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index bc9cbcd3..437633e7 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -47,8 +47,8 @@ local function preprocess_path(path) return path; end -function serve_file(request, path) - local response = request.response; +function serve_file(event, path) + local response = event.response; path = path and preprocess_path(path); if not path then response.status = 400; @@ -57,7 +57,7 @@ function serve_file(request, path) local full_path = http_base..path; if stat(full_path, "mode") == "directory" then if stat(full_path.."/index.html", "mode") == "file" then - return serve_file(request, path.."/index.html"); + return serve_file(event, path.."/index.html"); end response.status = 403; return response:send(response_403); -- cgit v1.2.3 From edb8a196dc0350a656fad5c1905a5627a3b25ccd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 13:37:35 +0100 Subject: prosody: Load portmanager at startup, to ensure it is ready to receive item-added events when modules are loaded --- prosody | 1 + 1 file changed, 1 insertion(+) diff --git a/prosody b/prosody index 2995f0f1..64021fd3 100755 --- a/prosody +++ b/prosody @@ -280,6 +280,7 @@ function load_secondary_libraries() require "util.xmppstream" require "core.rostermanager" require "core.hostmanager" + require "core.portmanager" require "core.modulemanager" require "core.usermanager" require "core.sessionmanager" -- cgit v1.2.3 From 112de122fd296ec8471b070ac26759b871444dbb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 15:40:00 +0100 Subject: mod_admin_telnet: Add initial port:list() and port:close() commands --- plugins/mod_admin_telnet.lua | 48 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index cb6302c7..aa65b05b 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -759,6 +759,51 @@ function def_env.host:list() return true, i.." hosts"; end +def_env.port = {}; + +function def_env.port:list() + local print = self.session.print; + local services = portmanager.get_active_services().data; + local ordered_services, n_ports = {}, 0; + for service, interfaces in pairs(services) do + table.insert(ordered_services, service); + end + table.sort(ordered_services); + for _, service in ipairs(ordered_services) do + local ports_list = {}; + for interface, ports in pairs(services[service]) do + for port in pairs(ports) do + table.insert(ports_list, "["..interface.."]:"..port); + end + end + n_ports = n_ports + #ports_list; + print(service..": "..table.concat(ports_list, ", ")); + end + return true, #ordered_services.." services listening on "..n_ports.." ports"; +end + +function def_env.port:close(close_port, close_interface) + close_port = assert(tonumber(close_port), "Invalid port number"); + local n_closed = 0; + local services = portmanager.get_active_services().data; + for service, interfaces in pairs(services) do + for interface, ports in pairs(interfaces) do + if not close_interface or close_interface == interface then + if ports[close_port] then + self.session.print("Closing ["..interface.."]:"..close_port.."..."); + local ok, err = portmanager.close(interface, close_port) + if not ok then + self.session.print("Failed to close "..interface.." "..port..": "..err); + else + n_closed = n_closed + 1; + end + end + end + end + end + return true, "Closed "..n_closed.." ports"; +end + ------------- function printbanner(session) @@ -789,7 +834,8 @@ if option and option ~= "short" and option ~= "full" and option ~= "graphic" the end end -require "core.portmanager".register_service("console", { +module:add_item("net-provider", { + name = "console"; listener = console_listener; default_port = 5582; private = true; -- cgit v1.2.3 From ea7212be3be670deda10d80f0d99abcb9d171f7a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 15:42:35 +0100 Subject: net.server_select: Remove server from _server table when closing --- net/server_select.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/server_select.lua b/net/server_select.lua index 8802f620..d6cfc1f8 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -202,6 +202,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxco socket:close( ) _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _server[ip..":"..serverport] = nil; _socketlist[ socket ] = nil handler = nil socket = nil -- cgit v1.2.3 From dcdfe0e0ceb454cbb8ecb8461fd4648f5e560830 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 15:52:30 +0100 Subject: net.server_event: Do not automatically close clients when closing a server port --- net/server_event.lua | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index dbf5161f..8d6f5597 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -343,24 +343,11 @@ do return nil, "writebuffer not empty, waiting" end else - debug( "try to close server with id:", self.id, "args:", now ) + debug( "try to close server with id:", tostring(self.id), "args:", tostring(now) ) self.fatalerror = "server to close" self:_lock( true ) - local count = 0 - for _, item in ipairs( interfacelist( ) ) do - if ( item.type ~= "server" ) and ( item._server == self ) then -- client/server match - if item:close( now ) then -- writebuffer was empty - count = count + 1 - end - end - end - local timeout = 0 -- dont wait for unfinished writebuffers of clients... - if not now then - timeout = cfg.WRITE_TIMEOUT -- ...or wait for it - end - self:_close( timeout ) -- add new event to remove the server interface - debug( "seconds remained until server is closed:", timeout ) - return count -- returns finished clients with empty writebuffer + self:_close( 0 ) -- add new event to remove the server interface + return true end end -- cgit v1.2.3 From 1dea1062ea7a5a446bd72b82bab5ce373294e9d8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 16:00:20 +0100 Subject: portmanager: Add get_service_at(interface, port) and close(interface, port) --- core/portmanager.lua | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index c5bb936a..da238dba 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -126,9 +126,7 @@ function deactivate(service_name) if not active then return; end for interface, ports in pairs(active) do for port, active_service in pairs(ports) do - active_service:close(); - active_services:remove(service_name, interface, port, active_service); - log("debug", "Removed listening service %s from [%s]:%d", service_name, interface, port); + close(interface, port); end end log("info", "Deactivated service '%s'", service_name); @@ -165,6 +163,22 @@ function unregister_service(service_name, service_info) fire_event("service-removed", { name = service_name, service = service_info }); end +function close(interface, port) + local service, server = get_service_at(interface, port); + if not service then + return false, "port-not-open"; + end + server:close(); + active_services:remove(service.name, interface, port); + log("debug", "Removed listening service %s from [%s]:%d", service.name, interface, port); + return true; +end + +function get_service_at(interface, port) + local data = active_services:search(nil, interface, port)[1][1]; + return data.service, data.server; +end + function get_service(service_name) return services[service_name]; end -- cgit v1.2.3 From 3e2df1d2fe330c599df89109ac95251bf6ff8450 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 16:02:30 +0100 Subject: mod_http: Fix specifying method in app route keys --- plugins/mod_http.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 3dcb14d7..6da4db24 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -19,10 +19,11 @@ end local function get_http_event(host, app_path, key) local method, path = key:match("^(%S+)%s+(.+)$"); - if not method and key:sub(1,1) == "/" then + if not method then + if key:sub(1,1) ~= "/" then + return nil; + end method, path = "GET", key; - else - return nil; end path = normalize_path(path); return method:upper().." "..host..app_path..path; -- cgit v1.2.3 From 370f28850450e1fac7f548511fdf5178538c4c52 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 18:50:22 +0100 Subject: mod_proxy65: Port to portmanager, make a shared module --- plugins/mod_proxy65.lua | 184 ++++++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 98 deletions(-) diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua index d02f3b58..155cb60d 100644 --- a/plugins/mod_proxy65.lua +++ b/plugins/mod_proxy65.lua @@ -6,35 +6,21 @@ -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- ---[[ -* to restart the proxy in the console: e.g. -module:unload("proxy65"); -> server.removeserver(); -module:load("proxy65", ); -]]-- +module:set_global(); -local module = module; -local tostring = tostring; local jid_compare, jid_prep = require "util.jid".compare, require "util.jid".prep; local st = require "util.stanza"; -local connlisteners = require "net.connlisteners"; local sha1 = require "util.hashes".sha1; -local server = require "net.server"; local b64 = require "util.encodings".base64.encode; +local server = require "net.server"; -local host, name = module:get_host(), "SOCKS5 Bytestreams Service"; -local sessions, transfers = {}, {}; - -local proxy_port = module:get_option("proxy65_port") or 5000; -local proxy_interface = module:get_option("proxy65_interface") or "*"; -local proxy_address = module:get_option("proxy65_address") or (proxy_interface ~= "*" and proxy_interface) or host; -local proxy_acl = module:get_option("proxy65_acl"); +local sessions, transfers = module:shared("sessions", "transfers"); local max_buffer_size = 4096; -local connlistener = { default_port = proxy_port, default_interface = proxy_interface, default_mode = "*a" }; +local listener = {}; -function connlistener.onincoming(conn, data) +function listener.onincoming(conn, data) local session = sessions[conn] or {}; local transfer = transfers[session.sha]; @@ -84,7 +70,7 @@ function connlistener.onincoming(conn, data) end end -function connlistener.ondisconnect(conn, err) +function listener.ondisconnect(conn, err) local session = sessions[conn]; if session then if transfers[session.sha] then @@ -101,88 +87,90 @@ function connlistener.ondisconnect(conn, err) end end -module:add_identity("proxy", "bytestreams", name); -module:add_feature("http://jabber.org/protocol/bytestreams"); - -module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function(event) - local origin, stanza = event.origin, event.stanza; - origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#info") - :tag("identity", {category='proxy', type='bytestreams', name=name}):up() - :tag("feature", {var="http://jabber.org/protocol/bytestreams"}) ); - return true; -end, -1); - -module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function(event) - local origin, stanza = event.origin, event.stanza; - origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#items")); - return true; -end, -1); +function module.add_host(module) + local host, name = module:get_host(), module:get_option_string("name", "SOCKS5 Bytestreams Service"); + + local proxy_address = module:get_option("proxy65_address", host); + local proxy_port = module:get_option_number("proxy65_port", next(portmanager.get_active_services():search("proxy65", nil)[1])); + local proxy_acl = module:get_option("proxy65_acl"); -module:hook("iq-get/host/http://jabber.org/protocol/bytestreams:query", function(event) - local origin, stanza = event.origin, event.stanza; + module:add_identity("proxy", "bytestreams", name); + module:add_feature("http://jabber.org/protocol/bytestreams"); + + module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function(event) + local origin, stanza = event.origin, event.stanza; + origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#info") + :tag("identity", {category='proxy', type='bytestreams', name=name}):up() + :tag("feature", {var="http://jabber.org/protocol/bytestreams"}) ); + return true; + end, -1); + + module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function(event) + local origin, stanza = event.origin, event.stanza; + origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#items")); + return true; + end, -1); - -- check ACL - while proxy_acl and #proxy_acl > 0 do -- using 'while' instead of 'if' so we can break out of it - local jid = stanza.attr.from; - for _, acl in ipairs(proxy_acl) do - if jid_compare(jid, acl) then break; end + module:hook("iq-get/host/http://jabber.org/protocol/bytestreams:query", function(event) + local origin, stanza = event.origin, event.stanza; + + -- check ACL + while proxy_acl and #proxy_acl > 0 do -- using 'while' instead of 'if' so we can break out of it + local jid = stanza.attr.from; + for _, acl in ipairs(proxy_acl) do + if jid_compare(jid, acl) then break; end + end + module:log("warn", "Denying use of proxy for %s", tostring(stanza.attr.from)); + origin.send(st.error_reply(stanza, "auth", "forbidden")); + return true; end - module:log("warn", "Denying use of proxy for %s", tostring(stanza.attr.from)); - origin.send(st.error_reply(stanza, "auth", "forbidden")); + + local sid = stanza.tags[1].attr.sid; + origin.send(st.reply(stanza):tag("query", {xmlns="http://jabber.org/protocol/bytestreams", sid=sid}) + :tag("streamhost", {jid=host, host=proxy_address, port=proxy_port})); return true; - end - - local sid = stanza.tags[1].attr.sid; - origin.send(st.reply(stanza):tag("query", {xmlns="http://jabber.org/protocol/bytestreams", sid=sid}) - :tag("streamhost", {jid=host, host=proxy_address, port=proxy_port})); - return true; -end); - -module.unload = function() - connlisteners.deregister(module.host .. ':proxy65'); -end - -module:hook("iq-set/host/http://jabber.org/protocol/bytestreams:query", function(event) - local origin, stanza = event.origin, event.stanza; - - local query = stanza.tags[1]; - local sid = query.attr.sid; - local from = stanza.attr.from; - local to = query:get_child_text("activate"); - local prepped_to = jid_prep(to); - - local info = "sid: "..tostring(sid)..", initiator: "..tostring(from)..", target: "..tostring(prepped_to or to); - if prepped_to and sid then - local sha = sha1(sid .. from .. prepped_to, true); - if not transfers[sha] then - module:log("debug", "Activation request has unknown session id; activation failed (%s)", info); - origin.send(st.error_reply(stanza, "modify", "item-not-found")); - elseif not transfers[sha].initiator then - module:log("debug", "The sender was not connected to the proxy; activation failed (%s)", info); - origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The sender (you) is not connected to the proxy")); - --elseif not transfers[sha].target then -- can't happen, as target is set when a transfer object is created - -- module:log("debug", "The recipient was not connected to the proxy; activation failed (%s)", info); - -- origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The recipient is not connected to the proxy")); - else -- if transfers[sha].initiator ~= nil and transfers[sha].target ~= nil then - module:log("debug", "Transfer activated (%s)", info); - transfers[sha].activated = true; - transfers[sha].target:resume(); - transfers[sha].initiator:resume(); - origin.send(st.reply(stanza)); + end); + + module:hook("iq-set/host/http://jabber.org/protocol/bytestreams:query", function(event) + local origin, stanza = event.origin, event.stanza; + + local query = stanza.tags[1]; + local sid = query.attr.sid; + local from = stanza.attr.from; + local to = query:get_child_text("activate"); + local prepped_to = jid_prep(to); + + local info = "sid: "..tostring(sid)..", initiator: "..tostring(from)..", target: "..tostring(prepped_to or to); + if prepped_to and sid then + local sha = sha1(sid .. from .. prepped_to, true); + if not transfers[sha] then + module:log("debug", "Activation request has unknown session id; activation failed (%s)", info); + origin.send(st.error_reply(stanza, "modify", "item-not-found")); + elseif not transfers[sha].initiator then + module:log("debug", "The sender was not connected to the proxy; activation failed (%s)", info); + origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The sender (you) is not connected to the proxy")); + --elseif not transfers[sha].target then -- can't happen, as target is set when a transfer object is created + -- module:log("debug", "The recipient was not connected to the proxy; activation failed (%s)", info); + -- origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The recipient is not connected to the proxy")); + else -- if transfers[sha].initiator ~= nil and transfers[sha].target ~= nil then + module:log("debug", "Transfer activated (%s)", info); + transfers[sha].activated = true; + transfers[sha].target:resume(); + transfers[sha].initiator:resume(); + origin.send(st.reply(stanza)); + end + elseif to and sid then + module:log("debug", "Malformed activation jid; activation failed (%s)", info); + origin.send(st.error_reply(stanza, "modify", "jid-malformed")); + else + module:log("debug", "Bad request; activation failed (%s)", info); + origin.send(st.error_reply(stanza, "modify", "bad-request")); end - elseif to and sid then - module:log("debug", "Malformed activation jid; activation failed (%s)", info); - origin.send(st.error_reply(stanza, "modify", "jid-malformed")); - else - module:log("debug", "Bad request; activation failed (%s)", info); - origin.send(st.error_reply(stanza, "modify", "bad-request")); - end - return true; -end); - -if not connlisteners.register(module.host .. ':proxy65', connlistener) then - module:log("error", "mod_proxy65: Could not establish a connection listener. Check your configuration please."); - module:log("error", "Possibly two proxy65 components are configured to share the same port."); + return true; + end); end -connlisteners.start(module.host .. ':proxy65'); +module:provides("net", { + default_port = 5000; + listener = listener; +}); -- cgit v1.2.3 From e6f0711e573d82e4a84cf0711f1320df16836949 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 18:53:50 +0100 Subject: util.debug: Turn into a real-ish module ('debugx'), and require you call use() to override debug.traceback() --- util/debug.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/util/debug.lua b/util/debug.lua index 2170a6d1..aeb710d7 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -8,7 +8,9 @@ local censored_names = { pwd = true; }; -local function get_locals_table(level) +module("debugx", package.seeall); + +function get_locals_table(level) level = level + 1; -- Skip this function itself local locals = {}; for local_num = 1, math.huge do @@ -19,7 +21,7 @@ local function get_locals_table(level) return locals; end -local function get_upvalues_table(func) +function get_upvalues_table(func) local upvalues = {}; if func then for upvalue_num = 1, math.huge do @@ -31,7 +33,7 @@ local function get_upvalues_table(func) return upvalues; end -local function string_from_var_table(var_table, max_line_len, indent_str) +function string_from_var_table(var_table, max_line_len, indent_str) local var_string = {}; local col_pos = 0; max_line_len = max_line_len or math.huge; @@ -88,7 +90,7 @@ function get_traceback_table(thread, start_level) return levels; end -function debug.traceback(thread, message, level) +function traceback(thread, message, level) if type(thread) ~= "thread" then thread, message, level = coroutine.running(), thread, message; end @@ -136,3 +138,9 @@ function debug.traceback(thread, message, level) end return message.."stack traceback:\n"..table.concat(lines, "\n"); end + +function use() + debug.traceback = traceback; +end + +return _M; -- cgit v1.2.3 From de1fd4aaf27ca216bcbfeeeb197845078311aa13 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 18:54:34 +0100 Subject: util.helpers: Add show_events(), to show the events and handlers in a util.events object --- util/helpers.lua | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/util/helpers.lua b/util/helpers.lua index a8c8c612..ad23dd79 100644 --- a/util/helpers.lua +++ b/util/helpers.lua @@ -6,6 +6,8 @@ -- COPYING file in the source package for more information. -- +local debug = require "util.debug"; + module("helpers", package.seeall); -- Helper functions for debugging @@ -31,6 +33,33 @@ function revert_log_events(events) events.fire_event, events[events.fire_event] = events[events.fire_event], nil; -- :)) end +function show_events(events) + local event_handlers = events._handlers; + local events_array = {}; + local event_handler_arrays = {}; + for event in pairs(events._event_map) do + local handlers = event_handlers[event]; + table.insert(events_array, event); + local handler_strings = {}; + for i, handler in ipairs(handlers) do + local upvals = debug.string_from_var_table(debug.get_upvalues_table(handler)); + handler_strings[i] = " "..i..": "..tostring(handler)..(upvals and ("\n "..upvals) or ""); + end + event_handler_arrays[event] = handler_strings; + end + table.sort(events_array); + local i = 1; + repeat + local handlers = event_handler_arrays[events_array[i]]; + for j=#handlers, 1, -1 do + table.insert(events_array, i+1, handlers[j]); + end + if i > 1 then events_array[i] = "\n"..events_array[i]; end + i = i + #handlers + 1 + until i == #events_array; + return table.concat(events_array, "\n"); +end + function get_upvalue(f, get_name) local i, name, value = 0; repeat -- cgit v1.2.3 From 58896e197237213df9061f5e141530387887f814 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 19:05:45 +0100 Subject: net.http.server: Remove debug message --- net/http/server.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 94487e5e..50a5c5a1 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -26,7 +26,6 @@ local function is_wildcard_event(event) return event:sub(-2, -1) == "/*"; end local function is_wildcard_match(wildcard_event, event) - log("debug", "comparing %q with %q", wildcard_event:sub(1, -2), event:sub(1, #wildcard_event-1)); return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1); end -- cgit v1.2.3 From b23e6a2ef012c2d2568766ef0f41aaadf3cac826 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 19:07:12 +0100 Subject: net.http.server: Handle results returned by handlers, and send as a response. Also removes explicit firing of '*', which can now be done via wildcard events. --- net/http/server.lua | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index 50a5c5a1..185ac9a0 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -168,19 +168,27 @@ function handle_request(conn, request, finish_cb) host = host:match("[^:]*"):lower(); local event = request.method.." "..host..request.path:match("[^?]*"); local payload = { request = request, response = response }; - --[[repeat - if events.fire_event(event, payload) ~= nil then return; end - event = (event:sub(-1) == "/") and event:sub(1, -1) or event:gsub("[^/]*$", ""); - if event:sub(-1) == "/" then - event = event:sub(1, -1); - else - event = event:gsub("[^/]*$", ""); + --log("debug", "Firing event: %s", event); + local result = events.fire_event(event, payload); + if result ~= nil then + if result ~= true then + local code, body = 200, ""; + local result_type = type(result); + if result_type == "number" then + response.status_code = result; + elseif result_type == "string" then + body = result; + elseif result_type == "table" then + body = result.body; + result.body = nil; + for k, v in pairs(result) do + response[k] = v; + end + end + response:send(body); end - until not event:find("/", 1, true);]] - --log("debug", "Event: %s", event); - if events.fire_event(event, payload) ~= nil then return; end - -- TODO try adding/stripping / at the end, but this needs to work via an HTTP redirect - if events.fire_event("*", payload) ~= nil then return; end + return; + end end -- if handler not called, fallback to legacy httpserver handlers -- cgit v1.2.3 From 6cfc7eb6bb7df52eb28ac4d99914631d89a67510 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Apr 2012 07:55:13 +0500 Subject: mod_proxy65: Add multiplex pattern. --- plugins/mod_proxy65.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua index 155cb60d..a0dd7233 100644 --- a/plugins/mod_proxy65.lua +++ b/plugins/mod_proxy65.lua @@ -173,4 +173,7 @@ end module:provides("net", { default_port = 5000; listener = listener; + multiplex = { + pattern = "^\5"; + }; }); -- cgit v1.2.3 From 34eff6d74ca04ed10cb8c10d368d36c30d85bf16 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 03:56:26 +0100 Subject: Makefile: Fix for net/http --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 356563f2..1acdd051 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,9 @@ install: prosody.install prosodyctl.install prosody.cfg.lua.install util/encodin install -m755 ./prosody.install $(BIN)/prosody install -m755 ./prosodyctl.install $(BIN)/prosodyctl install -m644 core/* $(SOURCE)/core - install -m644 net/* $(SOURCE)/net + install -m644 net/*.lua $(SOURCE)/net + install -d $(SOURCE)/net/http + install -m644 net/http/*.lua $(SOURCE)/net/http install -m644 util/*.lua $(SOURCE)/util install -m644 util/*.so $(SOURCE)/util install -d $(SOURCE)/util/sasl -- cgit v1.2.3 From 8d878575e6834a32a3170618c66655b6c5c1ab16 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 25 Apr 2012 14:53:54 +0200 Subject: portmanager: Fix selecting bind_interfaces from pre-0.9 config options. --- core/portmanager.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index da238dba..e2994308 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -76,13 +76,14 @@ function activate_service(service_name) config_prefix = ""; end - local bind_interfaces = set.new(config.get("*", config_prefix.."interfaces") + local bind_interfaces = config.get("*", config_prefix.."interfaces") or config.get("*", config_prefix.."interface") -- COMPAT w/pre-0.9 or (service_info.private and default_local_interfaces) or config.get("*", "interfaces") or config.get("*", "interface") -- COMPAT w/pre-0.9 or listener.default_interface -- COMPAT w/pre0.9 - or default_interfaces); + or default_interfaces + bind_interfaces = set.new(type(bind_interfaces)~="table" and {bind_interfaces} or bind_interfaces); local bind_ports = set.new(config.get("*", config_prefix.."ports") or service_info.default_ports -- cgit v1.2.3 From e6f8d445c0ee25e310dd64258fe43a472c4308bd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 14:58:10 +0100 Subject: net.http.server: Remove legacy compatibility --- net/http/server.lua | 61 ++++------------------------------------------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index 185ac9a0..00571bce 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -160,9 +160,6 @@ function handle_request(conn, request, finish_cb) response.headers.content_type = "text/html"; response:send("400 Bad Request400 Bad Request: No Host header."); else - -- TODO call handler - --response.headers.content_type = "text/plain"; - --response:send("host="..(request.headers.host or "").."\npath="..request.path.."\n"..(request.body or "")); local host = request.headers.host; if host then host = host:match("[^:]*"):lower(); @@ -191,8 +188,10 @@ function handle_request(conn, request, finish_cb) end end - -- if handler not called, fallback to legacy httpserver handlers - _M.legacy_handler(request, response); + -- if handler not called, return 404 + response.status_code = 404; + response.headers.content_type = "text/html"; + response:send("404 Not Found404 Not Found: No such page."); end end function _M.send_response(response, body) @@ -215,58 +214,6 @@ function _M.send_response(response, body) response.conn:close(); end end -function _M.legacy_handler(request, response) - log("debug", "Invoking legacy handler"); - local base = request.path:match("^/([^/?]+)"); - local legacy_server = legacy_httpserver and legacy_httpserver.new.http_servers[5280]; - local handler = legacy_server and legacy_server.handlers[base]; - if not handler then handler = legacy_httpserver and legacy_httpserver.set_default_handler.default_handler; end - if handler then - -- add legacy properties to request object - request.url = { path = request.path }; - request.handler = response.conn; - request.id = tostring{}:match("%x+$"); - local headers = {}; - for k,v in pairs(request.headers) do - headers[k:gsub("_", "-")] = v; - end - request.headers = headers; - function request:send(resp) - if self.destroyed then return; end - if resp.body or resp.headers then - if resp.headers then - for k,v in pairs(resp.headers) do response.headers[k] = v; end - end - response:send(resp.body) - else - response:send(resp) - end - self.sent = true; - self:destroy(); - end - function request:destroy() - if self.destroyed then return; end - if not self.sent then return self:send(""); end - self.destroyed = true; - if self.on_destroy then - log("debug", "Request has destroy callback"); - self:on_destroy(); - else - log("debug", "Request has no destroy callback"); - end - end - local r = handler(request.method, request.body, request); - if r ~= true then - request:send(r); - end - else - log("debug", "No handler found"); - response.status_code = 404; - response.headers.content_type = "text/html"; - response:send("404 Not Found404 Not Found: No such page."); - end -end - function _M.add_handler(event, handler, priority) events.add_handler(event, handler, priority); end -- cgit v1.2.3 From 0558bb4bb6fb21dbeb1e1e209286b88f38bee2c8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 15:02:27 +0100 Subject: net.http.server: Small fix to comment --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 00571bce..dc7c5a06 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -34,7 +34,7 @@ setmetatable(events._handlers, { __index = function (handlers, curr_event) if is_wildcard_event(curr_event) then return; end -- Wildcard events cannot be fired -- Find all handlers that could match this event, sort them - -- and then put the array into handlers[event] + -- and then put the array into handlers[curr_event] (and return it) local matching_handlers_set = {}; local handlers_array = {}; for event, handlers_set in pairs(event_map) do -- cgit v1.2.3 From a8e1a8b7de3f9b9863855d85b8e0fe4954948ebe Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 19:57:46 +0100 Subject: mod_bosh: Optimisation, store reply_before value as waiting_requests value (saves a lookup) --- plugins/mod_bosh.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index c5576004..599e4522 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -165,8 +165,7 @@ function handle_request(method, body, request) -- We're keeping this request open, to respond later log("debug", "Have nothing to say, so leaving request unanswered for now"); if session.bosh_wait then - request.reply_before = os_time() + session.bosh_wait; - waiting_requests[request] = true; + waiting_requests[request] = os_time() + session.bosh_wait; end end @@ -399,8 +398,8 @@ function on_timer() -- log("debug", "Checking for requests soon to timeout..."); -- Identify requests timing out within the next few seconds local now = os_time() + 3; - for request in pairs(waiting_requests) do - if request.reply_before <= now then + for request, reply_before in pairs(waiting_requests) do + if reply_before <= now then log("debug", "%s was soon to timeout, sending empty response", request.id); -- Send empty response to let the -- client know we're still here -- cgit v1.2.3 From 7e60956c9e072083e1aa33fce1cbd7f1d80a4b2e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 23:08:33 +0100 Subject: net.http.server: Support for on_destroy callback on response objects, and a 'finished' flag to say when they are destroyed (responded to or connection closed) --- net/http/server.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/http/server.lua b/net/http/server.lua index dc7c5a06..c108a398 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -117,6 +117,11 @@ function listener.onconnect(conn) end function listener.ondisconnect(conn) + local open_response = conn._http_open_response; + if open_response and open_response.on_destroy then + open_response.finished = true; + open_response:on_destroy(); + end sessions[conn] = nil; end @@ -154,6 +159,7 @@ function handle_request(conn, request, finish_cb) send = _M.send_response; finish_cb = finish_cb; }; + conn._http_open_response = response; if not request.headers.host then response.status_code = 400; @@ -195,6 +201,10 @@ function handle_request(conn, request, finish_cb) end end function _M.send_response(response, body) + if response.finished then return; end + response.finished = true; + response.conn._http_open_response = nil; + local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); local headers = response.headers; body = body or ""; @@ -208,6 +218,10 @@ function _M.send_response(response, body) t_insert(output, body); response.conn:write(t_concat(output)); + if response.on_destroy then + response:on_destroy(); + response.on_destroy = nil; + end if headers.connection == "Keep-Alive" then response:finish_cb(); else -- cgit v1.2.3 From 1ca477397d55ede0dc9ab8759f8d56cfb44ea056 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 23:10:32 +0100 Subject: mod_bosh: Large commit to update to mod_http/net.http.server APIs. Becomes a shared module. --- plugins/mod_bosh.lua | 168 +++++++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 85 deletions(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 599e4522..4432bd03 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -79,11 +79,12 @@ local inactive_sessions = {}; -- Sessions which have no open requests -- Used to respond to idle sessions (those with waiting requests) local waiting_requests = {}; function on_destroy_request(request) + log("debug", "Request destroyed: %s", tostring(request)); waiting_requests[request] = nil; - local session = sessions[request.sid]; + local session = sessions[request.context.sid]; if session then local requests = session.requests; - for i,r in ipairs(requests) do + for i, r in ipairs(requests) do if r == request then t_remove(requests, i); break; @@ -99,39 +100,39 @@ function on_destroy_request(request) end end -function handle_request(method, body, request) - if (not body) or request.method ~= "POST" then - if request.method == "OPTIONS" then - local headers = {}; - for k,v in pairs(default_headers) do headers[k] = v; end - headers["Content-Type"] = nil; - return { headers = headers, body = "" }; - else - return "You really don't look like a BOSH client to me... what do you want?"; - end - end - if not method then - log("debug", "Request %s suffered error %s", tostring(request.id), body); - return; - end - --log("debug", "Handling new request %s: %s\n----------", request.id, tostring(body)); - request.notopen = true; - request.log = log; - request.on_destroy = on_destroy_request; - - local stream = new_xmpp_stream(request, stream_callbacks); +local function handle_GET(request) + return "You really don't look like a BOSH client to me... what do you want?"; +end + +function handle_OPTIONS(request) + local headers = {}; + for k,v in pairs(default_headers) do headers[k] = v; end + headers["Content-Type"] = nil; + return { headers = headers, body = "" }; +end + +function handle_POST(event) + log("debug", "Handling new request %s: %s\n----------", tostring(event.request), tostring(event.request.body)); + + local request, response = event.request, event.response; + response.on_destroy = on_destroy_request; + local body = request.body; + + local context = { request = request, response = response, notopen = true }; + local stream = new_xmpp_stream(context, stream_callbacks); + response.context = context; -- stream:feed() calls the stream_callbacks, so all stanzas in -- the body are processed in this next line before it returns. - local ok, err = stream:feed(body); - if not ok then - log("error", "Failed to parse BOSH payload: %s", err); - end + -- In particular, the streamopened() stream callback is where + -- much of the session logic happens, because it's where we first + -- get to see the 'sid' of this request. + stream:feed(body); -- Stanzas (if any) in the request have now been processed, and -- we take care of the high-level BOSH logic here, including -- giving a response or putting the request "on hold". - local session = sessions[request.sid]; + local session = sessions[context.sid]; if session then -- Session was marked as inactive, since we have -- a request open now, unmark it @@ -140,8 +141,11 @@ function handle_request(method, body, request) end local r = session.requests; - log("debug", "Session %s has %d out of %d requests open", request.sid, #r, session.bosh_hold); - log("debug", "and there are %d things in the send_buffer", #session.send_buffer); + log("debug", "Session %s has %d out of %d requests open", context.sid, #r, session.bosh_hold); + log("debug", "and there are %d things in the send_buffer:", #session.send_buffer); + for i, thing in ipairs(session.send_buffer) do + log("debug", " %s", tostring(thing)); + end if #r > session.bosh_hold then -- We are holding too many requests, send what's in the buffer, log("debug", "We are holding too many requests, so..."); @@ -161,11 +165,11 @@ function handle_request(method, body, request) session.send(resp); end - if not request.destroyed then + if not response.finished then -- We're keeping this request open, to respond later log("debug", "Have nothing to say, so leaving request unanswered for now"); if session.bosh_wait then - waiting_requests[request] = os_time() + session.bosh_wait; + waiting_requests[response] = os_time() + session.bosh_wait; end end @@ -213,11 +217,10 @@ local function bosh_close_stream(session, reason) log("info", "Disconnecting client, is: %s", tostring(close_reply)); end - local session_close_response = { headers = default_headers, body = tostring(close_reply) }; - + local response_body = tostring(close_reply); for _, held_request in ipairs(session.requests) do - held_request:send(session_close_response); - held_request:destroy(); + held_request.headers = default_headers; + held_request:send(response_body); end sessions[session.sid] = nil; inactive_sessions[session] = nil; @@ -225,12 +228,13 @@ local function bosh_close_stream(session, reason) end -- Handle the tag in the request payload. -function stream_callbacks.streamopened(request, attr) +function stream_callbacks.streamopened(context, attr) + local request, response = context.request, context.response; local sid = attr.sid; log("debug", "BOSH body open (sid: %s)", sid or ""); if not sid then -- New session request - request.notopen = nil; -- Signals that we accept this opening tag + context.notopen = nil; -- Signals that we accept this opening tag -- TODO: Sanity checks here (rid, to, known host, etc.) if not hosts[attr.to] then @@ -238,7 +242,7 @@ function stream_callbacks.streamopened(request, attr) log("debug", "BOSH client tried to connect to unknown host: %s", tostring(attr.to)); local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", ["xmlns:stream"] = xmlns_streams, condition = "host-unknown" }); - request:send(tostring(close_reply)); + response:send(tostring(close_reply)); return; end @@ -258,7 +262,6 @@ function stream_callbacks.streamopened(request, attr) session.log("debug", "BOSH session created for request from %s", session.ip); log("info", "New BOSH session, assigned it sid '%s'", sid); local r, send_buffer = session.requests, session.send_buffer; - local response = { headers = default_headers } function session.send(s) -- We need to ensure that outgoing stanzas have the jabber:client xmlns if s.attr and not s.attr.xmlns then @@ -269,25 +272,14 @@ function stream_callbacks.streamopened(request, attr) local oldest_request = r[1]; if oldest_request and (not(auto_cork) or waiting_requests[oldest_request]) then log("debug", "We have an open request, so sending on that"); - response.body = t_concat({ + oldest_request.headers = default_headers; + oldest_request:send(t_concat({ "", tostring(s), "" - }); - oldest_request:send(response); - --log("debug", "Sent"); - if oldest_request.stayopen then - if #r>1 then - -- Move front request to back - t_insert(r, oldest_request); - t_remove(r, 1); - end - else - log("debug", "Destroying the request now..."); - oldest_request:destroy(); - end + })); elseif s ~= "" then log("debug", "Saved to send buffer because there are %d open requests", #r); -- Hmm, no requests are open :( @@ -303,7 +295,7 @@ function stream_callbacks.streamopened(request, attr) hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); fire_event("stream-features", session, features); --xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh' - local response = st.stanza("body", { xmlns = xmlns_bosh, + local body = st.stanza("body", { xmlns = xmlns_bosh, wait = attr.wait, inactivity = tostring(BOSH_DEFAULT_INACTIVITY), polling = tostring(BOSH_DEFAULT_POLLING), @@ -315,7 +307,8 @@ function stream_callbacks.streamopened(request, attr) ["xmlns:xmpp"] = "urn:xmpp:xbosh", ["xmlns:stream"] = "http://etherx.jabber.org/streams" }):add_child(features); - request:send{ headers = default_headers, body = tostring(response) }; + response.headers = default_headers; + response:send(tostring(body)); request.sid = sid; return; @@ -325,8 +318,9 @@ function stream_callbacks.streamopened(request, attr) if not session then -- Unknown sid log("info", "Client tried to use sid '%s' which we don't know about", sid); - request:send{ headers = default_headers, body = tostring(st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", condition = "item-not-found" })) }; - request.notopen = nil; + response.headers = default_headers; + response:send(tostring(st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", condition = "item-not-found" }))); + context.notopen = nil; return; end @@ -338,10 +332,10 @@ function stream_callbacks.streamopened(request, attr) elseif diff <= 0 then -- Repeated, ignore session.log("debug", "rid repeated (on request %s), ignoring: %s (diff %d)", request.id, session.rid, diff); - request.notopen = nil; - request.ignore = true; - request.sid = sid; - t_insert(session.requests, request); + context.notopen = nil; + context.ignore = true; + context.sid = sid; + t_insert(session.requests, response); return; end session.rid = rid; @@ -353,9 +347,9 @@ function stream_callbacks.streamopened(request, attr) session.bosh_terminate = true; end - request.notopen = nil; -- Signals that we accept this opening tag - t_insert(session.requests, request); - request.sid = sid; + context.notopen = nil; -- Signals that we accept this opening tag + t_insert(session.requests, response); + context.sid = sid; if session.notopen then local features = st.stanza("stream:features"); @@ -366,10 +360,10 @@ function stream_callbacks.streamopened(request, attr) end end -function stream_callbacks.handlestanza(request, stanza) - if request.ignore then return; end +function stream_callbacks.handlestanza(context, stanza) + if context.ignore then return; end log("debug", "BOSH stanza received: %s\n", stanza:top_tag()); - local session = sessions[request.sid]; + local session = sessions[context.sid]; if session then if stanza.attr.xmlns == xmlns_bosh then stanza.attr.xmlns = nil; @@ -378,14 +372,17 @@ function stream_callbacks.handlestanza(request, stanza) end end -function stream_callbacks.error(request, error) +function stream_callbacks.error(context, error) log("debug", "Error parsing BOSH request payload; %s", error); - if not request.sid then - request:send({ headers = default_headers, status = "400 Bad Request" }); + if not context.sid then + local response = context.response; + response.headers = default_headers; + response.status_code = 400; + request:send(); return; end - local session = sessions[request.sid]; + local session = sessions[context.sid]; if error == "stream-error" then -- Remote stream error, we close normally session:close(); else @@ -400,11 +397,11 @@ function on_timer() local now = os_time() + 3; for request, reply_before in pairs(waiting_requests) do if reply_before <= now then - log("debug", "%s was soon to timeout, sending empty response", request.id); + log("debug", "%s was soon to timeout (at %d, now %d), sending empty response", tostring(request), reply_before, now); -- Send empty response to let the -- client know we're still here if request.conn then - sessions[request.sid].send(""); + sessions[request.context.sid].send(""); end end end @@ -428,15 +425,16 @@ function on_timer() end return 1; end - - -local function setup() - local ports = module:get_option_array("bosh_ports") or { 5280 }; - httpserver.new_from_config(ports, handle_request, { base = "http-bind" }); - timer.add_task(1, on_timer); -end -if prosody.start_time then -- already started - setup(); -else - prosody.events.add_handler("server-started", setup); +module:add_timer(1, on_timer); + +function module.add_host(module) + module:depends("http"); + module:provides("http", { + default_path = "/http-bind"; + route = { + ["GET /"] = handle_GET; + ["OPTIONS /"] = handle_OPTIONS; + ["POST /"] = handle_POST; + }; + }); end -- cgit v1.2.3 From 169deb9ca6cd622b09f2b186cc605be034a8a44a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 23:16:37 +0100 Subject: util.debug: Some more magic constant fiddling. Don't ask me. --- util/debug.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/debug.lua b/util/debug.lua index 7caf21ce..16773cd1 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -88,9 +88,9 @@ function get_traceback_table(thread, start_level) for level = start_level, math.huge do local info; if thread then - info = debug.getinfo(thread, level); + info = debug.getinfo(thread, level+1); else - info = debug.getinfo(level); + info = debug.getinfo(level+1); end if not info then break; end @@ -128,13 +128,13 @@ function _traceback(thread, message, level) if type(message) == "number" then level, message = message, nil; else - level = 2; + level = 1; end end message = message and (message.."\n") or ""; - local levels = get_traceback_table(thread, level+2); + local levels = get_traceback_table(thread, level+3); local last_source_desc; -- cgit v1.2.3 From 2416b4e47b1dc2e04b394138fb38ace0f7e5cc61 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 04:24:43 +0100 Subject: net.http.server: Fix to compare priority if path lengths are the same (logic fail) --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index c108a398..58554f39 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -50,7 +50,7 @@ setmetatable(events._handlers, { table.sort(handlers_array, function(b, a) local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; for i = 1, #a_score do - if a ~= b then -- If equal, compare next score value + if a_score[i] ~= b_score[i] then -- If equal, compare next score value return a_score[i] < b_score[i]; end end -- cgit v1.2.3 From 4b51577e133576fb52fcae3afb46ac98c3dc4cc4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 05:39:27 +0100 Subject: moduleapi: Add module:unhook_object_event() --- core/moduleapi.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index d16ee410..1f8c492d 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -73,6 +73,10 @@ function api:hook_object_event(object, event, handler, priority) return object.add_handler(event, handler, priority); end +function api:unhook_object_event(event, handler) + return object.remove_handler(event, handler); +end + function api:hook(event, handler, priority) return self:hook_object_event((hosts[self.host] or prosody).events, event, handler, priority); end -- cgit v1.2.3 From 93f86e22d518f59c5b821eaaa778bbc2f538e4f7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 05:40:04 +0100 Subject: mod_http: Use module:hook/unhook_event_object() so that handlers get unregistered if mod_http is unloaded --- plugins/mod_http.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 6da4db24..2ba69cb5 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -57,7 +57,7 @@ function module.add_host(module) end if not app_handlers[event_name] then app_handlers[event_name] = handler; - server.add_handler(event_name, handler); + module:hook_object_event(server, event_name, handler); else module:log("warn", "App %s added handler twice for '%s', ignoring", app_name, event_name); end @@ -71,7 +71,7 @@ function module.add_host(module) local app_handlers = apps[event.item.name]; apps[event.item.name] = nil; for event, handler in pairs(app_handlers) do - server.remove_handler(event, handler); + module:unhook_object_event(server, event, handler); end end -- cgit v1.2.3 From 377c44d0ee680805ed30b43f51e1dd6af8581e86 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 05:56:24 +0100 Subject: mod_bosh: Update informational message on GET --- plugins/mod_bosh.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 4432bd03..13cea911 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -101,7 +101,10 @@ function on_destroy_request(request) end local function handle_GET(request) - return "You really don't look like a BOSH client to me... what do you want?"; + return [[ +

It works! Now point your BOSH client to this URL to connect to Prosody.

+

For more information see Prosody: Setting up BOSH.

+]]; end function handle_OPTIONS(request) -- cgit v1.2.3 From a0e480bef3460df549c48c6cbbc61fdaa083c3d2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 05:57:44 +0100 Subject: util.termcolours: tohtml() for converting output to HTML. I don't know. --- util/termcolours.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/util/termcolours.lua b/util/termcolours.lua index df204688..6ef3b689 100644 --- a/util/termcolours.lua +++ b/util/termcolours.lua @@ -9,6 +9,7 @@ local t_concat, t_insert = table.concat, table.insert; local char, format = string.char, string.format; +local tonumber = tonumber; local ipairs = ipairs; local io_write = io.write; @@ -34,6 +35,15 @@ local winstylemap = { ["1;31"] = 4+8 -- bold red } +local cssmap = { + [1] = "font-weight: bold", [2] = "opacity: 0.5", [4] = "text-decoration: underline", [8] = "visibility: hidden", + [30] = "color:black", [31] = "color:red", [32]="color:green", [33]="color:#FFD700", + [34] = "color:blue", [35] = "color: magenta", [36] = "color:cyan", [37] = "color: white", + [40] = "background-color:black", [41] = "background-color:red", [42]="background-color:green", + [43]="background-color:yellow", [44] = "background-color:blue", [45] = "background-color: magenta", + [46] = "background-color:cyan", [47] = "background-color: white"; +}; + local fmt_string = char(0x1B).."[%sm%s"..char(0x1B).."[0m"; function getstring(style, text) if style then @@ -76,4 +86,17 @@ if windows then end end +local function ansi2css(ansi_codes) + if ansi_codes == "0" then return ""; end + local css = {}; + for code in ansi_codes:gmatch("[^;]+") do + t_insert(css, cssmap[tonumber(code)]); + end + return ""; +end + +function tohtml(input) + return input:gsub("\027%[(.-)m", ansi2css); +end + return _M; -- cgit v1.2.3 From 2444af151cabc8fdbfae9c3ed2b66eb10adde8ee Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 05:58:39 +0100 Subject: util.debug: Remove 'white' from boundary style (leave at default colour) --- util/debug.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/debug.lua b/util/debug.lua index 16773cd1..1d3b5648 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -15,7 +15,7 @@ local styles; do _ = termcolours.getstyle; styles = { - boundary_padding = _("bright", "white"); + boundary_padding = _("bright"); filename = _("bright", "blue"); level_num = _("green"); funcname = _("yellow"); -- cgit v1.2.3 From eb427566464aa721caa9a482d01f59e460de2f51 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:10:14 +0100 Subject: mod_http_files: Return numeric error codes instead of custom error responses --- plugins/mod_http_files.lua | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index 437633e7..c5859283 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -14,10 +14,6 @@ local stat = lfs.attributes; local http_base = module:get_option_string("http_path", "www_files"); -local response_400 = "

Bad Request

Sorry, we didn't understand your request :("; -local response_403 = "

Forbidden

You don't have permission to view the contents of this directory :("; -local response_404 = "

Page Not Found

Sorry, we couldn't find what you were looking for :("; - -- TODO: Should we read this from /etc/mime.types if it exists? (startup time...?) local mime_map = { html = "text/html"; @@ -51,27 +47,23 @@ function serve_file(event, path) local response = event.response; path = path and preprocess_path(path); if not path then - response.status = 400; - return response:send(response_400); + return 400; end local full_path = http_base..path; if stat(full_path, "mode") == "directory" then if stat(full_path.."/index.html", "mode") == "file" then return serve_file(event, path.."/index.html"); end - response.status = 403; - return response:send(response_403); + return 403; end local f, err = open(full_path, "rb"); if not f then - response.status = 404; - return response:send(response_404.."
"..tostring(err)); + return 404; end local data = f:read("*a"); f:close(); if not data then - response.status = 403; - return response:send(response_403); + return 403; end local ext = path:match("%.([^.]*)$"); response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known -- cgit v1.2.3 From 603f08ca8419b8deff0334e206442f0c01bbd2f3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:30:29 +0100 Subject: mod_http_files: Log 404 failure reason --- plugins/mod_http_files.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index c5859283..9d5aa252 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -58,6 +58,7 @@ function serve_file(event, path) end local f, err = open(full_path, "rb"); if not f then + module:log("warn", "Failed to open file: %s", err); return 404; end local data = f:read("*a"); -- cgit v1.2.3 From 4d44ce6743c9dfa8ac31ddc082cf5d0e8501ab23 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:41:11 +0100 Subject: mod_http: Switch to single option for specifying HTTP app bases, http_paths. Keys are app/module names, values are base paths. --- plugins/mod_http.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 2ba69cb5..8b8d9c47 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -29,6 +29,12 @@ local function get_http_event(host, app_path, key) return method:upper().." "..host..app_path..path; end +local function get_base_path(host_module, app_name, default_app_path) + return host_module:get_option("http_paths", {})[app_name] -- Host + or module:get_option("http_paths", {})[app_name] -- Global + or default_app_path; -- Default +end + function module.add_host(module) local host = module.host; local apps = {}; @@ -36,7 +42,7 @@ function module.add_host(module) local function http_app_added(event) local app_name = event.item.name; local default_app_path = event.item.default_path or "/"..app_name; - local app_path = normalize_path(module:get_option_string(app_name.."_http_path", default_app_path)); + local app_path = normalize_path(get_base_path(module, app_name, default_app_path)); if not app_name then -- TODO: Link to docs module:log("error", "HTTP app has no 'name', add one or use module:provides('http', app)"); -- cgit v1.2.3 From 9b7e2f6d4b643bf24fb2adac75d2129dfb68bcd0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:42:02 +0100 Subject: mod_http_files: Change option name from http_path to http_files_dir --- plugins/mod_http_files.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index 9d5aa252..932d2547 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -12,7 +12,7 @@ local lfs = require "lfs"; local open = io.open; local stat = lfs.attributes; -local http_base = module:get_option_string("http_path", "www_files"); +local http_base = module:get_option_string("http_files_dir", module:get_option_string("http_path", "www_files")); -- TODO: Should we read this from /etc/mime.types if it exists? (startup time...?) local mime_map = { -- cgit v1.2.3 From e3d135d3f1ee7a956228e642024e00e3da5cd807 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:47:17 +0100 Subject: net.http.server: Lower score of wildcard handlers to ensure specific handlers beat them --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 58554f39..906b3eaf 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -41,7 +41,7 @@ setmetatable(events._handlers, { if event == curr_event or is_wildcard_event(event) and is_wildcard_match(event, curr_event) then for handler, priority in pairs(handlers_set) do - matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), priority }; + matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), is_wildcard_event(event) and 0 or 1, priority }; table.insert(handlers_array, handler); end end -- cgit v1.2.3 From ffb91300cca8ad4deff5b4f443f7d26933f90bee Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:58:21 +0100 Subject: util.helpers: show_events(): Make more robust, and allow filtering results to a specific event --- util/helpers.lua | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/util/helpers.lua b/util/helpers.lua index ad23dd79..6103a319 100644 --- a/util/helpers.lua +++ b/util/helpers.lua @@ -33,30 +33,32 @@ function revert_log_events(events) events.fire_event, events[events.fire_event] = events[events.fire_event], nil; -- :)) end -function show_events(events) +function show_events(events, specific_event) local event_handlers = events._handlers; local events_array = {}; local event_handler_arrays = {}; for event in pairs(events._event_map) do local handlers = event_handlers[event]; - table.insert(events_array, event); - local handler_strings = {}; - for i, handler in ipairs(handlers) do - local upvals = debug.string_from_var_table(debug.get_upvalues_table(handler)); - handler_strings[i] = " "..i..": "..tostring(handler)..(upvals and ("\n "..upvals) or ""); + if handlers and (event == specific_event or not specific_event) then + table.insert(events_array, event); + local handler_strings = {}; + for i, handler in ipairs(handlers) do + local upvals = debug.string_from_var_table(debug.get_upvalues_table(handler)); + handler_strings[i] = " "..i..": "..tostring(handler)..(upvals and ("\n "..upvals) or ""); + end + event_handler_arrays[event] = handler_strings; end - event_handler_arrays[event] = handler_strings; end table.sort(events_array); local i = 1; - repeat + while i <= #events_array do local handlers = event_handler_arrays[events_array[i]]; for j=#handlers, 1, -1 do table.insert(events_array, i+1, handlers[j]); end if i > 1 then events_array[i] = "\n"..events_array[i]; end i = i + #handlers + 1 - until i == #events_array; + end return table.concat(events_array, "\n"); end -- cgit v1.2.3 From 0e5b2786b420aeaea1372c532b464f9e48d40fa1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:58:57 +0100 Subject: net.http.server: Expose events object (for debug purposes) --- net/http/server.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/http/server.lua b/net/http/server.lua index 906b3eaf..d6c3a03a 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -241,4 +241,5 @@ end _M.listener = listener; _M.codes = codes; +_M._events = events; return _M; -- cgit v1.2.3 From 9b570dd0e2a96ef5bbb4a9811cd90205cb45edf5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 07:04:59 +0100 Subject: moduleapi: module:depends(): Don't load shared modules onto the current host if the current host is '*'... --- core/moduleapi.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 1f8c492d..1c676bc3 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -124,7 +124,8 @@ function api:depends(name) end); end local mod = modulemanager.get_module(self.host, name) or modulemanager.get_module("*", name); - if mod and mod.module.host == "*" and modulemanager.module_has_method(mod, "add_host") then + if mod and mod.module.host == "*" and self.host ~= "*" + and modulemanager.module_has_method(mod, "add_host") then mod = nil; -- This is a shared module, so we still want to load it on our host end if not mod then -- cgit v1.2.3 From 1ab99fe8b668463b3e72586426d2b4dff8c5be54 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 14:51:55 +0100 Subject: moduleapi: Fix parameters to unhook_object_event() --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 1c676bc3..8e76a247 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -73,7 +73,7 @@ function api:hook_object_event(object, event, handler, priority) return object.add_handler(event, handler, priority); end -function api:unhook_object_event(event, handler) +function api:unhook_object_event(object, event, handler) return object.remove_handler(event, handler); end -- cgit v1.2.3 From 99e8f3cff54ce9238756023e682e96d59bdde4ae Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 15:05:05 +0100 Subject: net.server.http: Parse absolute URIs in requests (thanks Maranda) --- net/http/server.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/http/server.lua b/net/http/server.lua index d6c3a03a..4f9e1463 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -1,6 +1,7 @@ local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; local parser_new = require "net.http.parser".new; +local url_parse = require "socket.url".parse; local events = require "util.events".new(); local addserver = require "net.server".addserver; local log = require "util.logger".init("http.server"); @@ -101,6 +102,9 @@ function listener.onconnect(conn) local function success_cb(request) --log("debug", "success_cb: %s", request.path); request.secure = secure; + local parsed_dest = url_parse(request.path); + request.url = parsed_dest; + request.path = parsed_dest.path; t_insert(pending, request); if not waiting then process_next(); -- cgit v1.2.3 From de13771c8b72ee9137ca8e661beb10b80e58d1e3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 15:14:19 +0100 Subject: net.http.server: Make error handling overrideable via 'http-error' event --- net/http/server.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index 4f9e1463..deaa44cd 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -76,7 +76,12 @@ setmetatable(events._handlers, { local handle_request; local _1, _2, _3; local function _handle_request() return handle_request(_1, _2, _3); end -local function _traceback_handler(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end + +local last_err; +local function _traceback_handler(err) last_err = err; log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end +events.add_handler("http-error", function (error) + return "Error processing request: "..codes[error.code]..". Check your error log for more information."; +end, -1); function listener.onconnect(conn) local secure = conn:ssl() and true or nil; @@ -91,7 +96,7 @@ function listener.onconnect(conn) --handle_request(conn, request, process_next); _1, _2, _3 = conn, request, process_next; if not xpcall(_handle_request, _traceback_handler) then - conn:write("HTTP/1.0 503 Internal Server Error\r\n\r\nAn error occured during the processing of this request."); + conn:write("HTTP/1.0 500 Internal Server Error\r\n\r\n"..events.fire_event("http-error", { code = 500, private_message = last_err })); conn:close(); end else @@ -168,7 +173,7 @@ function handle_request(conn, request, finish_cb) if not request.headers.host then response.status_code = 400; response.headers.content_type = "text/html"; - response:send("400 Bad Request400 Bad Request: No Host header."); + response:send(events.fire_event("http-error", { code = 400, message = "No 'Host' header" })); else local host = request.headers.host; if host then @@ -183,6 +188,9 @@ function handle_request(conn, request, finish_cb) local result_type = type(result); if result_type == "number" then response.status_code = result; + if result >= 400 then + body = events.fire_event("http-error", { code = result }); + end elseif result_type == "string" then body = result; elseif result_type == "table" then @@ -201,7 +209,7 @@ function handle_request(conn, request, finish_cb) -- if handler not called, return 404 response.status_code = 404; response.headers.content_type = "text/html"; - response:send("404 Not Found404 Not Found: No such page."); + response:send(events.fire_event("http-error", { code = 404 })); end end function _M.send_response(response, body) -- cgit v1.2.3 From 92345b94978870a084bdfc9929ba046e4b23ed0e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 15:16:29 +0100 Subject: mod_http_errors: Module to handle HTTP errors with a HTML page --- plugins/mod_http_errors.lua | 75 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 plugins/mod_http_errors.lua diff --git a/plugins/mod_http_errors.lua b/plugins/mod_http_errors.lua new file mode 100644 index 00000000..820bcc2f --- /dev/null +++ b/plugins/mod_http_errors.lua @@ -0,0 +1,75 @@ +module:set_global(); +module:depends("http"); + +local server = require "net.http.server"; +local codes = require "net.http.codes"; +local termcolours = require "util.termcolours"; + +local show_private = module:get_option_boolean("http_errors_detailed", false); + +local default_messages = { + [400] = { "What kind of request do you call that??" }; + [403] = { "You're not allowed to do that." }; + [404] = { "Whatever you were looking for is not here. %"; + "Where did you put it?", "It's behind you.", "Keep looking." }; + [500] = { "% Check your error log for more info."; + "Gremlins.", "It broke.", "Don't look at me." }; +}; + +local messages = setmetatable(module:get_option("http_errors_messages", {}), { __index = default_messages }); + +local html = [[ + + + + + + + +

$title

+

$message

+

$extra

+ +]]; +html = html:gsub("%s%s+", ""); + +local entities = { + ["<"] = "<", [">"] = ">", ["&"] = "&", + ["'"] = "'", ["\""] = """, ["\n"] = "
", +}; + +local function tohtml(plain) + return (plain:gsub("[<>&'\"\n]", entities)); + +end + +local function get_page(code, extra) + local message = messages[code]; + if message then + return (html:gsub("$(%a+)", { + title = rawget(codes, code) or ("Code "..tostring(code)); + message = message[1]:gsub("%%", function () + return message[math.random(2, math.max(#message,2))]; + end); + extra = tohtml(extra or ""); + })); + end +end + +module:hook_object_event(server, "http-error", function (event) + return get_page(event.code, (show_private and event.private_message) or event.message); +end); -- cgit v1.2.3 From 91393747a3aa127e13ba07bc71340c59b708430d Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Thu, 26 Apr 2012 20:07:13 +0500 Subject: net.http.parser: Handle full URLs in status line. --- net/http/parser.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/http/parser.lua b/net/http/parser.lua index c98c75af..fdcb8ebb 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -53,7 +53,6 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) else method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$"); if not method then error = true; return error_cb("invalid-status-line"); end - path = path:gsub("^//+", "/"); -- TODO parse url more end end end @@ -71,6 +70,12 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) responseheaders = headers; }; else + -- path normalization + if path:match("^https?://") then + headers.host, path = path:match("^https?://([^/]*)(.*)"); + end + path = path:gsub("^//+", "/"); -- TODO parse url more + len = len or 0; packet = { method = method; -- cgit v1.2.3 From 9db4ba52f9cbab52721ed819e4e81f3cbd62d106 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 16:11:08 +0100 Subject: net.http.server: Fire http-error 400 if request fails sanity checks --- net/http/server.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index deaa44cd..938e676d 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -170,10 +170,17 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; + local err; if not request.headers.host then + err = "No 'Host' header"; + elseif not request.path then + err = "Invalid path"; + end + + if err then response.status_code = 400; response.headers.content_type = "text/html"; - response:send(events.fire_event("http-error", { code = 400, message = "No 'Host' header" })); + response:send(events.fire_event("http-error", { code = 400, message = err })); else local host = request.headers.host; if host then -- cgit v1.2.3 From ec56b1a798e4a91ce5beaeeeee5d45847cd38a31 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 16:25:11 +0100 Subject: Revert 98bfebb38705, moved to net.http.parser in 4fc99f1b7570 --- net/http/server.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index 4f9e1463..d6c3a03a 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -1,7 +1,6 @@ local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; local parser_new = require "net.http.parser".new; -local url_parse = require "socket.url".parse; local events = require "util.events".new(); local addserver = require "net.server".addserver; local log = require "util.logger".init("http.server"); @@ -102,9 +101,6 @@ function listener.onconnect(conn) local function success_cb(request) --log("debug", "success_cb: %s", request.path); request.secure = secure; - local parsed_dest = url_parse(request.path); - request.url = parsed_dest; - request.path = parsed_dest.path; t_insert(pending, request); if not waiting then process_next(); -- cgit v1.2.3 From e64c982be472e25835cf9bf9f14efb131b73aece Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 16:48:16 +0100 Subject: mod_http_files, net.http.parser: Move path normalization to net.http.parser so that all modules can benefit --- net/http/parser.lua | 20 +++++++++++++++++++- plugins/mod_http_files.lua | 24 +----------------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/net/http/parser.lua b/net/http/parser.lua index fdcb8ebb..3d9d1a87 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -2,6 +2,24 @@ local tonumber = tonumber; local assert = assert; +local function preprocess_path(path) + if path:sub(1,1) ~= "/" then + path = "/"..path; + end + local level = 0; + for component in path:gmatch("([^/]+)/") do + if component == ".." then + level = level - 1; + elseif component ~= "." then + level = level + 1; + end + if level < 0 then + return nil; + end + end + return path; +end + local httpstream = {}; function httpstream.new(success_cb, error_cb, parser_type, options_cb) @@ -74,7 +92,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) if path:match("^https?://") then headers.host, path = path:match("^https?://([^/]*)(.*)"); end - path = path:gsub("^//+", "/"); -- TODO parse url more + path = preprocess_path(path); len = len or 0; packet = { diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index 932d2547..9be735b7 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -25,31 +25,9 @@ local mime_map = { css = "text/css"; }; -local function preprocess_path(path) - if path:sub(1,1) ~= "/" then - path = "/"..path; - end - local level = 0; - for component in path:gmatch("([^/]+)/") do - if component == ".." then - level = level - 1; - elseif component ~= "." then - level = level + 1; - end - if level < 0 then - return nil; - end - end - return path; -end - function serve_file(event, path) local response = event.response; - path = path and preprocess_path(path); - if not path then - return 400; - end - local full_path = http_base..path; + local full_path = http_base.."/"..path; if stat(full_path, "mode") == "directory" then if stat(full_path.."/index.html", "mode") == "file" then return serve_file(event, path.."/index.html"); -- cgit v1.2.3 From 25e1f9296a7543a976155556752ed397b6add7f4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 16:53:32 +0100 Subject: mod_http: Remove unused import of url.parse --- plugins/mod_http.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 8b8d9c47..7b962457 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -8,7 +8,6 @@ module:set_global(); -local parse_url = require "socket.url".parse; local server = require "net.http.server"; local function normalize_path(path) -- cgit v1.2.3 From f51b8f32b0f9031bf30ced4fd4c3f0ad6f6213e8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 18:34:40 +0100 Subject: moduleapi: get_host_type(): Return nil for global modules (no host) --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 8e76a247..8c57e301 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -42,7 +42,7 @@ function api:get_host() end function api:get_host_type() - return hosts[self.host].type; + return self.host ~= "*" and hosts[self.host].type or nil; end function api:set_global() -- cgit v1.2.3 From 1d2aa12ca4afcd02fa1d44df105268d4694d3e6d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 18:35:33 +0100 Subject: moduleapi: Rename module:hook_stanza() -> module:hook_tag() (hook_stanza works for compat) --- core/moduleapi.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 8c57e301..9e8489c2 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -85,7 +85,7 @@ function api:hook_global(event, handler, priority) return self:hook_object_event(prosody.events, event, handler, priority); end -function api:hook_stanza(xmlns, name, handler, priority) +function api:hook_tag(xmlns, name, handler, priority) if not handler and type(name) == "function" then -- If only 2 options then they specified no xmlns xmlns, name, handler, priority = nil, xmlns, name, handler; @@ -95,6 +95,7 @@ function api:hook_stanza(xmlns, name, handler, priority) end return self:hook("stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority); end +api.hook_stanza = api.hook_tag; -- COMPAT w/pre-0.9 function api:require(lib) local f, n = pluginloader.load_code(self.name, lib..".lib.lua"); -- cgit v1.2.3 From 5ecbd224981c62b5b1fe9526826de721f222a013 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 18:36:27 +0100 Subject: mod_http: Link to docs on routes in error message --- plugins/mod_http.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 7b962457..93d58439 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -67,7 +67,7 @@ function module.add_host(module) module:log("warn", "App %s added handler twice for '%s', ignoring", app_name, event_name); end else - module:log("error", "Invalid route in %s: %q", app_name, key); + module:log("error", "Invalid route in %s, %q. See http://prosody.im/doc/developers/http#routes", app_name, key); end end end -- cgit v1.2.3 From 9d5830281a0a149e0fbfdda68e7c1ba760d20c7b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 18:37:40 +0100 Subject: mod_http: Routes now require a method to be specified, but the path has become optional (defaults to the base path with no trailing '/' --- plugins/mod_http.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 93d58439..4c6c1299 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -18,13 +18,12 @@ end local function get_http_event(host, app_path, key) local method, path = key:match("^(%S+)%s+(.+)$"); - if not method then - if key:sub(1,1) ~= "/" then - return nil; - end - method, path = "GET", key; + if not method then -- No path specified, default to "" (base path) + method, path = key, ""; + end + if method:sub(1,1) == "/" then + return nil; end - path = normalize_path(path); return method:upper().." "..host..app_path..path; end -- cgit v1.2.3 From aa1d8dd97b673e7fdd4c72fd4216b593dbb79f87 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 18:40:44 +0100 Subject: mod_http_files: Specify method in HTTP route --- plugins/mod_http_files.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index 9be735b7..dc58ff5d 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -51,7 +51,7 @@ end module:provides("http", { route = { - ["/*"] = serve_file; + ["GET /*"] = serve_file; }; }); -- cgit v1.2.3 From 17fa5ed559e5dff83778f4f00768f58929744f8c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 18:54:51 +0100 Subject: net.http.codes: Add missing 418 status code --- net/http/codes.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/http/codes.lua b/net/http/codes.lua index 2e701027..0cadd079 100644 --- a/net/http/codes.lua +++ b/net/http/codes.lua @@ -44,6 +44,7 @@ local response_codes = { [415] = "Unsupported Media Type"; [416] = "Requested Range Not Satisfiable"; [417] = "Expectation Failed"; + [418] = "I'm a teapot"; [422] = "Unprocessable Entity"; [423] = "Locked"; [424] = "Failed Dependency"; -- cgit v1.2.3 From 27e152ee76cb2e262c6bfb73214ec9d4957e94bd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 19:02:36 +0100 Subject: mod_http: Allow a route value to be static data rather than a handler function --- plugins/mod_http.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 4c6c1299..ef3f0271 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -51,7 +51,10 @@ function module.add_host(module) for key, handler in pairs(event.item.route or {}) do local event_name = get_http_event(host, app_path, key); if event_name then - if event_name:sub(-2, -1) == "/*" then + if type(handler) ~= "function" then + local data = handler; + handler = function () return data; end + elseif event_name:sub(-2, -1) == "/*" then local base_path = event_name:match("/(.+)/*$"); local _handler = handler; handler = function (event) -- cgit v1.2.3 From 15b5936dc0c7fa70d003161b9db20a0ac83a0061 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 19:05:03 +0100 Subject: mod_bosh: Add routes without trailing-'/', so that both /http-bind and /http-bind/ work again --- plugins/mod_bosh.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 13cea911..84adb369 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -435,8 +435,11 @@ function module.add_host(module) module:provides("http", { default_path = "/http-bind"; route = { + ["GET"] = handle_GET; ["GET /"] = handle_GET; + ["OPTIONS"] = handle_OPTIONS; ["OPTIONS /"] = handle_OPTIONS; + ["POST"] = handle_POST; ["POST /"] = handle_POST; }; }); -- cgit v1.2.3 From 53a4656587ab75f31bcf2437f3b36b3c41c01e5c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 20:00:06 +0100 Subject: net.http.server: Correctly cache results of handler indexing, and also cache failures --- net/http/server.lua | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index d693fb52..00d98fcb 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -46,17 +46,20 @@ setmetatable(events._handlers, { end end end - if #handlers_array == 0 then return; end - table.sort(handlers_array, function(b, a) - local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; - for i = 1, #a_score do - if a_score[i] ~= b_score[i] then -- If equal, compare next score value - return a_score[i] < b_score[i]; + if #handlers_array > 0 then + table.sort(handlers_array, function(b, a) + local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; + for i = 1, #a_score do + if a_score[i] ~= b_score[i] then -- If equal, compare next score value + return a_score[i] < b_score[i]; + end end - end - return false; - end); - handlers[curr_event] = handlers_array; + return false; + end); + else + handlers_array = false; + end + rawset(handlers, curr_event, handlers_array); return handlers_array; end; __newindex = function (handlers, curr_event, handlers_array) @@ -69,6 +72,7 @@ setmetatable(events._handlers, { end end end + rawset(handlers, curr_event, handlers_array); end; }); -- cgit v1.2.3 From 6d82c54cfd5f73de76082d80ebbc1614811e9911 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 20:01:21 +0100 Subject: net.http.server: Remove unused variable --- net/http/server.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 00d98fcb..86d729f0 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -18,7 +18,6 @@ local legacy_httpserver = require "net.httpserver"; local _M = {}; local sessions = {}; -local handlers = {}; local listener = {}; -- cgit v1.2.3 From bcd870f7d33fd43ebaef0ab50a544776d4f04505 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 21:37:45 +0100 Subject: modulemanager: Set module.environment before calling add_host, otherwise the module will get the parent's environment (thanks xnyhps and Maranda) --- core/modulemanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index bf1e1924..5a97e5bf 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -142,10 +142,10 @@ local function do_load_module(host, module_name) },{ __index = modulemap["*"][module_name].module; }); + local host_module = setmetatable({ module = host_module_api }, { __index = mod }); + host_module_api.environment = host_module; local ok, result, module_err = call_module_method(mod, "add_host", host_module_api); if not ok or result == false then return nil, ok and module_err or result; end - local host_module = setmetatable({ module = host_module_api }, { __index = mod }); - host_module.module.environment = host_module; modulemap[host][module_name] = host_module; return host_module; end -- cgit v1.2.3 From 8aaef8d056c683a3ad42d99f59250d539ac2edd2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 22:18:20 +0100 Subject: mod_proxy65: Fix traceback when proxy65 service fails to bind a port --- plugins/mod_proxy65.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua index a0dd7233..b2f6f703 100644 --- a/plugins/mod_proxy65.lua +++ b/plugins/mod_proxy65.lua @@ -91,7 +91,7 @@ function module.add_host(module) local host, name = module:get_host(), module:get_option_string("name", "SOCKS5 Bytestreams Service"); local proxy_address = module:get_option("proxy65_address", host); - local proxy_port = module:get_option_number("proxy65_port", next(portmanager.get_active_services():search("proxy65", nil)[1])); + local proxy_port = module:get_option_number("proxy65_port", next(portmanager.get_active_services():search("proxy65", nil)[1] or {})); local proxy_acl = module:get_option("proxy65_acl"); module:add_identity("proxy", "bytestreams", name); -- cgit v1.2.3 From 65b45123fde803751782116779ab9a39f9e6dd67 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 22:31:53 +0100 Subject: hostmanager: Convert host-deactivating event parameters to a table --- core/hostmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 330a0d03..91c6d4e1 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -102,7 +102,7 @@ function deactivate(host, reason) local host_session = hosts[host]; if not host_session then return nil, "The host "..tostring(host).." is not activated"; end log("info", "Deactivating host: %s", host); - prosody_events.fire_event("host-deactivating", host, host_session); + prosody_events.fire_event("host-deactivating", { host = host, host_session = host_session, reason = reason }); if type(reason) ~= "table" then reason = { condition = "host-gone", text = tostring(reason or "This server has stopped serving "..host) }; -- cgit v1.2.3 From eece0f9d1ca55e86f36db9aa492e7435f4cd3061 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 22:32:04 +0100 Subject: hostmanager: Add some TODO comments --- core/hostmanager.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 91c6d4e1..39af5732 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -109,6 +109,7 @@ function deactivate(host, reason) end -- Disconnect local users, s2s connections + -- TODO: These should move to mod_c2s and mod_s2s (how do they know they're being unloaded and not reloaded?) if host_session.sessions then for username, user in pairs(host_session.sessions) do for resource, session in pairs(user.sessions) do @@ -133,6 +134,7 @@ function deactivate(host, reason) end end + -- TODO: This should be done in modulemanager if host_session.modules then for module in pairs(host_session.modules) do modulemanager.unload(host, module); -- cgit v1.2.3 From 59b9d9b0f3334d2e0b2ddc5de58d950a2b9fb6df Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 22:33:38 +0100 Subject: hostmanager: Remove unused host_config parameter from host-activated event --- core/hostmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 39af5732..4b9fa240 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -94,7 +94,7 @@ function activate(host, host_config) end log((hosts_loaded_once and "info") or "debug", "Activated host: %s", host); - prosody_events.fire_event("host-activated", host, host_config); + prosody_events.fire_event("host-activated", host); return true; end -- cgit v1.2.3 From 9fa672af8b196566d0099daf5cd12ef0374999dc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 22:36:35 +0100 Subject: modulemanager: Clear modulemap when a host is deactivated (thanks xnyhps) --- core/modulemanager.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 5a97e5bf..3829da57 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -82,6 +82,9 @@ function load_modules_for_host(host) end end prosody.events.add_handler("host-activated", load_modules_for_host); +prosody.events.add_handler("host-deactivated", function (host) + modulemap[host] = nil; +end); --- Private helpers --- -- cgit v1.2.3 From 1835139203be72741b50efaba1d7c1071542972a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 22:37:24 +0100 Subject: mod_component: Return true after denying a second component connection for a host (thanks xnyhps) --- plugins/mod_component.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua index 5b1eefc7..98eaa6de 100644 --- a/plugins/mod_component.lua +++ b/plugins/mod_component.lua @@ -69,6 +69,7 @@ function module.add_host(module) if env.connected then module:log("error", "Second component attempted to connect, denying connection"); session:close{ condition = "conflict", text = "Component already connected" }; + return true; end env.connected = true; -- cgit v1.2.3 From 987c79e4d0cbf7c35a6201603813a3a0ff491db2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 22:37:56 +0100 Subject: net.http.server: Improve comment --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 86d729f0..978a5a82 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -64,7 +64,7 @@ setmetatable(events._handlers, { __newindex = function (handlers, curr_event, handlers_array) if handlers_array == nil and is_wildcard_event(curr_event) then - -- Invalidate all matching + -- Invalidate the indexes of all matching events for event in pairs(handlers) do if is_wildcard_match(curr_event, event) then handlers[event] = nil; -- cgit v1.2.3 From fb9c619707409485b4b1afbd5585785759c6922d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 23:11:23 +0100 Subject: net.http.server, mod_http: Support http_default_host config option to specify where to direct requests for unknown HTTP vhosts --- net/http/server.lua | 97 ++++++++++++++++++++++++++++++---------------------- plugins/mod_http.lua | 7 ++++ 2 files changed, 63 insertions(+), 41 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index 978a5a82..325eb42f 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -18,8 +18,9 @@ local legacy_httpserver = require "net.httpserver"; local _M = {}; local sessions = {}; - local listener = {}; +local hosts = {}; +local default_host; local function is_wildcard_event(event) return event:sub(-2, -1) == "/*"; @@ -169,54 +170,59 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; - local err; - if not request.headers.host then - err = "No 'Host' header"; + local host = (request.headers.host or ""):match("[^:]+"); + + -- Some sanity checking + local err_code, err; + if not host then + err_code, err = 400, "Missing or invalid 'Host' header"; elseif not request.path then - err = "Invalid path"; + err_code, err = 400, "Invalid path"; + end + if not hosts[host] then + if hosts[default_host] then + host = default_host; + else + err_code, err = 404, "Unknown host: "..host; + end end if err then - response.status_code = 400; - response.headers.content_type = "text/html"; - response:send(events.fire_event("http-error", { code = 400, message = err })); - else - local host = request.headers.host; - if host then - host = host:match("[^:]*"):lower(); - local event = request.method.." "..host..request.path:match("[^?]*"); - local payload = { request = request, response = response }; - --log("debug", "Firing event: %s", event); - local result = events.fire_event(event, payload); - if result ~= nil then - if result ~= true then - local code, body = 200, ""; - local result_type = type(result); - if result_type == "number" then - response.status_code = result; - if result >= 400 then - body = events.fire_event("http-error", { code = result }); - end - elseif result_type == "string" then - body = result; - elseif result_type == "table" then - body = result.body; - result.body = nil; - for k, v in pairs(result) do - response[k] = v; - end - end - response:send(body); + response.status_code = err_code; + response:send(events.fire_event("http-error", { code = err_code, message = err })); + return; + end + + local event = request.method.." "..host..request.path:match("[^?]*"); + local payload = { request = request, response = response }; + --log("debug", "Firing event: %s", event); + local result = events.fire_event(event, payload); + if result ~= nil then + if result ~= true then + local code, body = 200, ""; + local result_type = type(result); + if result_type == "number" then + response.status_code = result; + if result >= 400 then + body = events.fire_event("http-error", { code = result }); + end + elseif result_type == "string" then + body = result; + elseif result_type == "table" then + body = result.body; + result.body = nil; + for k, v in pairs(result) do + response[k] = v; end - return; end + response:send(body); end - - -- if handler not called, return 404 - response.status_code = 404; - response.headers.content_type = "text/html"; - response:send(events.fire_event("http-error", { code = 404 })); + return; end + + -- if handler not called, return 404 + response.status_code = 404; + response:send(events.fire_event("http-error", { code = 404 })); end function _M.send_response(response, body) if response.finished then return; end @@ -256,6 +262,15 @@ end function _M.listen_on(port, interface, ssl) addserver(interface or "*", port, listener, "*a", ssl); end +function _M.add_host(host) + hosts[host] = true; +end +function _M.remove_host(host) + hosts[host] = nil; +end +function _M.set_default_host(host) + default_host = host; +end _M.listener = listener; _M.codes = codes; diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index ef3f0271..c379a562 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -10,6 +10,8 @@ module:set_global(); local server = require "net.http.server"; +server.set_default_host(module:get_option_string("http_default_host")); + local function normalize_path(path) if path:sub(1,1) ~= "/" then path = "/"..path; end if path:sub(-1,-1) == "/" then path = path:sub(1, -2); end @@ -83,6 +85,11 @@ function module.add_host(module) end module:handle_items("http-provider", http_app_added, http_app_removed); + + server.add_host(host); + function module.unload() + server.remove_host(host); + end end module:add_item("net-provider", { -- cgit v1.2.3 From d92bf9cb1b0ba4f50545be954ca386af4e735002 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 23:12:30 +0100 Subject: mod_http_errors: Add two new config options, http_errors_always_show (show even for unknown errors) and http_errors_default_message (message for unknown errors) --- plugins/mod_http_errors.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/mod_http_errors.lua b/plugins/mod_http_errors.lua index 820bcc2f..c7bcbbc1 100644 --- a/plugins/mod_http_errors.lua +++ b/plugins/mod_http_errors.lua @@ -6,7 +6,8 @@ local codes = require "net.http.codes"; local termcolours = require "util.termcolours"; local show_private = module:get_option_boolean("http_errors_detailed", false); - +local always_serve = module:get_option_boolean("http_errors_always_show", true); +local default_message = { module:get_option_string("http_errors_default_message", "That's all I know.") }; local default_messages = { [400] = { "What kind of request do you call that??" }; [403] = { "You're not allowed to do that." }; @@ -59,7 +60,8 @@ end local function get_page(code, extra) local message = messages[code]; - if message then + if always_serve or message then + message = message or default_message; return (html:gsub("$(%a+)", { title = rawget(codes, code) or ("Code "..tostring(code)); message = message[1]:gsub("%%", function () -- cgit v1.2.3 From ff4cc9de8ae7bea45b956f39d9d88ed4bfbbea1d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 00:45:33 +0100 Subject: mod_bosh: Remove unused import of net.httpserver --- plugins/mod_bosh.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 84adb369..1fe52dda 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -10,7 +10,6 @@ module:set_global(); -- Global module local hosts = _G.hosts; local new_xmpp_stream = require "util.xmppstream".new; -local httpserver = require "net.httpserver"; local sm = require "core.sessionmanager"; local sm_destroy_session = sm.destroy_session; local new_uuid = require "util.uuid".generate; @@ -181,7 +180,7 @@ function handle_POST(event) session:close(); return nil; else - return true; -- Inform httpserver we shall reply later + return true; -- Inform http server we shall reply later end end end -- cgit v1.2.3 From a5affc4e5d4639700b8a4ea34b2bf8d350e5ab74 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 00:51:36 +0100 Subject: net.http.server: Fix traceback on missing host header (thanks darkrain) --- net/http/server.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index 325eb42f..eb30da78 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -178,8 +178,7 @@ function handle_request(conn, request, finish_cb) err_code, err = 400, "Missing or invalid 'Host' header"; elseif not request.path then err_code, err = 400, "Invalid path"; - end - if not hosts[host] then + elseif not hosts[host] then if hosts[default_host] then host = default_host; else -- cgit v1.2.3 From bdbf9df051b000de88e6e2d962d23606d44cfb65 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 01:13:49 +0100 Subject: net.http.server: Try default_host if client sent no host anywhere, otherwise... fail. It's 2012. --- net/http/server.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index eb30da78..8b91b4d3 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -174,15 +174,15 @@ function handle_request(conn, request, finish_cb) -- Some sanity checking local err_code, err; - if not host then - err_code, err = 400, "Missing or invalid 'Host' header"; - elseif not request.path then + if not request.path then err_code, err = 400, "Invalid path"; elseif not hosts[host] then if hosts[default_host] then host = default_host; - else + elseif host then err_code, err = 404, "Unknown host: "..host; + else + err_code, err = 400, "Missing or invalid 'Host' header"; end end -- cgit v1.2.3 From d5ba56353d042b7bb23162ce038a944085d47e08 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 01:27:02 +0100 Subject: portmanager: Explicitly import some libraries --- core/portmanager.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index e2994308..afcfb797 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -1,5 +1,11 @@ +local config = require "core.configmanager"; +local server = require "net.server"; +local log = require "util.logger".init("portmanager"); local multitable = require "util.multitable"; +local set = require "util.set"; + +local prosody = prosody; local fire_event = prosody.events.fire_event; --- Config -- cgit v1.2.3 From ed03e1adb93034e888558abebd257faf41cb3ad3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 01:27:15 +0100 Subject: portmanager: Add module() definition --- core/portmanager.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index afcfb797..8f26c57e 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -8,6 +8,8 @@ local set = require "util.set"; local prosody = prosody; local fire_event = prosody.events.fire_event; +module "portmanager"; + --- Config local default_interfaces = { "*" }; -- cgit v1.2.3 From 7a8047ba3e200045b078c8818b50869a3df9ba88 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 01:27:46 +0100 Subject: portmanager: Rename activate_service() to activate() (to match deactivate()) --- core/portmanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 8f26c57e..337b8f4b 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -71,7 +71,7 @@ end); --- Public API -function activate_service(service_name) +function activate(service_name) local service_info = services[service_name][1]; if not service_info then return nil, "Unknown service: "..service_name; @@ -146,7 +146,7 @@ function register_service(service_name, service_info) if not active_services:get(service_name) then log("debug", "No active service for %s, activating...", service_name); - local ok, err = activate_service(service_name); + local ok, err = activate(service_name); if not ok then log("error", "Failed to activate service '%s': %s", service_name, err or "unknown error"); end -- cgit v1.2.3 From 7813edf719386c2c108d5415f8be2af11e6baa7c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 01:44:08 +0100 Subject: portmanager: Fix breakage (import ALL the functions) --- core/portmanager.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 337b8f4b..3bd143ef 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -5,6 +5,10 @@ local log = require "util.logger".init("portmanager"); local multitable = require "util.multitable"; local set = require "util.set"; +local table, package = table, package; +local setmetatable, rawset, rawget = setmetatable, rawset, rawget; +local type = type; + local prosody = prosody; local fire_event = prosody.events.fire_event; @@ -58,8 +62,6 @@ local function error_to_friendly_message(service_name, port, err) return friendly_message; end -module("portmanager", package.seeall); - prosody.events.add_handler("item-added/net-provider", function (event) local item = event.item; register_service(item.name, item); -- cgit v1.2.3 From f07d1d5441b89862306761de228161d09520e8a7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 02:18:17 +0100 Subject: modulemanager: Remove unused NULL declaration --- core/modulemanager.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 3829da57..40aa4ba2 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -47,8 +47,6 @@ local api = _G.require "core.moduleapi"; -- Module API container -- [host] = { [module] = module_env } local modulemap = { ["*"] = {} }; -local NULL = {}; - -- Load modules when a host is activated function load_modules_for_host(host) local component = config.get(host, "core", "component_module"); -- cgit v1.2.3 From a4d9a4101e53a2a1f86c4bc3af0a8ce3711d4afb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 02:22:42 +0100 Subject: modulemanager: Remove unused function imports --- core/modulemanager.lua | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 40aa4ba2..8fdd5c1d 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -14,15 +14,9 @@ local pluginloader = require "util.pluginloader"; local hosts = hosts; local prosody = prosody; -local loadfile, pcall, xpcall = loadfile, pcall, xpcall; -local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv; -local pairs, ipairs = pairs, ipairs; -local t_insert, t_concat = table.insert, table.concat; -local type = type; -local next = next; -local rawget = rawget; -local error = error; -local tostring, tonumber = tostring, tonumber; +local pcall, xpcall = pcall, xpcall; +local setmetatable, rawget, setfenv = setmetatable, rawget, setfenv; +local pairs, type, tostring = pairs, type, tostring; local debug_traceback = debug.traceback; local unpack, select = unpack, select; @@ -32,7 +26,7 @@ pcall = function(f, ...) return xpcall(function() return f(unpack(params, 1, n)) end, function(e) return tostring(e).."\n"..debug_traceback(); end); end -local array, set = require "util.array", require "util.set"; +local set = require "util.set"; local autoload_modules = {"presence", "message", "iq", "offline", "c2s", "s2s"}; local component_inheritable_modules = {"tls", "dialback", "iq"}; -- cgit v1.2.3 From de0b898bb86efba22e9c6c89a9719127c59710da Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 02:24:41 +0100 Subject: moduleapi: Remove unused variable 'module_items' --- core/moduleapi.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 9e8489c2..f23dc118 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -255,7 +255,6 @@ function api:get_option_set(name, ...) return set.new(value); end -local module_items = multitable_new(); function api:add_item(key, value) self.items = self.items or {}; self.items[key] = self.items[key] or {}; -- cgit v1.2.3 From a67da3de83a44c7866e123285222eb3fd89ae46d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 02:25:54 +0100 Subject: hostmanager: Remove import of unused setmetatable --- core/hostmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 4b9fa240..a9db1a92 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -24,7 +24,7 @@ if not _G.prosody.incoming_s2s then end local incoming_s2s = _G.prosody.incoming_s2s; -local pairs, setmetatable, select = pairs, setmetatable, select; +local pairs, select = pairs, select; local tostring, type = tostring, type; module "hostmanager" -- cgit v1.2.3 From 428aa05eb56db31b67a6c835525530b4f648735b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 02:38:56 +0100 Subject: util.stanza: Remove unused function imports --- util/stanza.lua | 6 ------ 1 file changed, 6 deletions(-) diff --git a/util/stanza.lua b/util/stanza.lua index 600212a4..1449f707 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -8,22 +8,16 @@ local t_insert = table.insert; -local t_concat = table.concat; local t_remove = table.remove; local t_concat = table.concat; local s_format = string.format; local s_match = string.match; local tostring = tostring; local setmetatable = setmetatable; -local getmetatable = getmetatable; local pairs = pairs; local ipairs = ipairs; local type = type; -local next = next; -local print = print; -local unpack = unpack; local s_gsub = string.gsub; -local s_char = string.char; local s_find = string.find; local os = os; -- cgit v1.2.3 From 096514c6702ceb70e3edd8e34a8c8244f8ca26cd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 02:40:36 +0100 Subject: moduleapi: Import util.timer --- core/moduleapi.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index f23dc118..703eb268 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -12,6 +12,7 @@ local array = require "util.array"; local set = require "util.set"; local logger = require "util.logger"; local pluginloader = require "util.pluginloader"; +local timer = require "util.timer"; local multitable_new = require "util.multitable".new; -- cgit v1.2.3 From ce25b6cbe15e1f10339956c9c3cc6a091f5ec585 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 02:41:53 +0100 Subject: util.timer: Remove unused function imports --- util/timer.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util/timer.lua b/util/timer.lua index d5b66473..d36fb8c4 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -15,8 +15,7 @@ local math_min = math.min local math_huge = math.huge local get_time = require "socket".gettime; local t_insert = table.insert; -local t_remove = table.remove; -local ipairs, pairs = ipairs, pairs; +local pairs = pairs; local type = type; local data = {}; -- cgit v1.2.3 From 2e3217d5d66a1fb87258c731b4d0691849db97bc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 02:56:10 +0100 Subject: mod_s2s: Add log() import --- plugins/s2s/mod_s2s.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index b0bd5b40..478ed558 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -29,6 +29,8 @@ local connect_timeout = module:get_option_number("s2s_timeout", 60); local sessions = module:shared("sessions"); +local log = module._log; + --- Handle stanzas to remote domains local bouncy_stanzas = { message = true, presence = true, iq = true }; -- cgit v1.2.3 From 7fe9ac03dec97aa0fa89f34879a1801db6277bee Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:05:03 +0100 Subject: mod_c2s: Remove unused import of portmanager --- plugins/mod_c2s.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 743fe3d2..69f2298f 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -11,7 +11,6 @@ module:set_global(); local add_task = require "util.timer".add_task; local new_xmpp_stream = require "util.xmppstream".new; local nameprep = require "util.encodings".stringprep.nameprep; -local portmanager = require "core.portmanager"; local sessionmanager = require "core.sessionmanager"; local st = require "util.stanza"; local sm_new_session, sm_destroy_session = sessionmanager.new_session, sessionmanager.destroy_session; -- cgit v1.2.3 From dac1c0e9d36a28635b684a885180adc7ace0703e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:05:35 +0100 Subject: mod_saslauth: Remove unused declaration of xmlns_stanzas --- plugins/mod_saslauth.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 7708572a..804db5f9 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -26,7 +26,6 @@ local log = module._log; local xmlns_sasl ='urn:ietf:params:xml:ns:xmpp-sasl'; local xmlns_bind ='urn:ietf:params:xml:ns:xmpp-bind'; -local xmlns_stanzas ='urn:ietf:params:xml:ns:xmpp-stanzas'; local function build_reply(status, ret, err_msg) local reply = st.stanza(status, {xmlns = xmlns_sasl}); -- cgit v1.2.3 From ab8723773c7076c27c15ca5b4945cdbd6fd9a43c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:06:14 +0100 Subject: mod_s2s: Fix imports and remove some unused variables --- plugins/s2s/mod_s2s.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index 478ed558..e40f35bd 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -8,6 +8,10 @@ module:set_global(); +local prosody = prosody; +local hosts = prosody.hosts; +local core_process_stanza = prosody.core_process_stanza; + local tostring, type = tostring, type; local t_insert = table.insert; local xpcall, traceback = xpcall, debug.traceback; @@ -20,6 +24,7 @@ local new_xmpp_stream = require "util.xmppstream".new; local s2s_new_incoming = require "core.s2smanager".new_incoming; local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; +local s2s_mark_connected = require "core.s2smanager".mark_connected; local uuid_gen = require "util.uuid".generate; local cert_verify_identity = require "util.x509".verify_identity; @@ -41,7 +46,7 @@ local function bounce_sendq(session, reason) local dummy = { type = "s2sin"; send = function(s) - (session.log or log)("error", "Replying to to an s2s error reply, please report this! Traceback: %s", get_traceback()); + (session.log or log)("error", "Replying to to an s2s error reply, please report this! Traceback: %s", traceback()); end; dummy = true; }; @@ -81,7 +86,7 @@ module:hook("route/remote", function (event) return true; elseif host.type == "local" or host.type == "component" then log("error", "Trying to send a stanza to ourselves??") - log("error", "Traceback: %s", get_traceback()); + log("error", "Traceback: %s", traceback()); log("error", "Stanza: %s", tostring(stanza)); return false; else @@ -442,7 +447,6 @@ function listener.onstatus(conn, status) if status == "ssl-handshake-complete" then local session = sessions[conn]; if session and session.direction == "outgoing" then - local to_host, from_host = session.to_host, session.from_host; session.log("debug", "Sending stream header..."); session:open_stream(session.from_host, session.to_host); end -- cgit v1.2.3 From 0d4dfb4a886fa081ebd50aac4c5a0699895ddaa5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:11:45 +0100 Subject: mod_s2s/s2sout.lib: Fix imports and some undefined variables --- plugins/s2s/s2sout.lib.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index af55b273..011c864f 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -12,16 +12,18 @@ 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 add_task = require "util.timer".add_task; local new_ip = require "util.ip".new_ip; local rfc3484_dest = require "util.rfc3484".destination; local socket = require "socket"; +local adns = require "net.adns"; +local dns = require "net.dns"; local t_insert, t_sort, ipairs = table.insert, table.sort, ipairs; local st = require "util.stanza"; -local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; +local log = module._log; + local sources = {}; local max_dns_depth = module:get_option_number("dns_max_depth", 3); @@ -67,7 +69,7 @@ function s2sout.initiate_connection(host_session) buffer = {}; host_session.send_buffer = buffer; end - log("debug", "Buffering data on unconnected s2sout to %s", to_host); + log("debug", "Buffering data on unconnected s2sout to %s", tostring(host_session.to_host)); buffer[#buffer+1] = data; log("debug", "Buffered item %d: %s", #buffer, tostring(data)); end -- cgit v1.2.3 From 0ef5422e6474f534b6bc900f743cbbf738bfef9f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:13:59 +0100 Subject: mod_component: Remove unused variable --- plugins/mod_component.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua index 98eaa6de..6be68e39 100644 --- a/plugins/mod_component.lua +++ b/plugins/mod_component.lua @@ -207,7 +207,6 @@ local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; local function session_close(session, reason) if session.destroyed then return; end - local log = session.log or log; if session.conn then if session.notopen then session.send(""); -- cgit v1.2.3 From 64f91f889353952ee3106d6ffea709c72dfdbcae Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:25:40 +0100 Subject: storagemanager: Fix incorrect variable name --- core/storagemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/storagemanager.lua b/core/storagemanager.lua index c96ef3ec..71e79271 100644 --- a/core/storagemanager.lua +++ b/core/storagemanager.lua @@ -47,7 +47,7 @@ prosody.events.add_handler("host-activated", initialize_host, 101); function load_driver(host, driver_name) if driver_name == "null" then - return null_storage_provider; + return null_storage_driver; end local driver = stores_available:get(host, driver_name); if driver then return driver; end -- cgit v1.2.3 From ea986bc75ab45fe02ae493b29e330502945643f2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:34:58 +0100 Subject: mod_message: Remove unused import of table.insert --- plugins/mod_message.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_message.lua b/plugins/mod_message.lua index df317532..ebff2fe7 100644 --- a/plugins/mod_message.lua +++ b/plugins/mod_message.lua @@ -14,7 +14,6 @@ local st = require "util.stanza"; local jid_bare = require "util.jid".bare; local jid_split = require "util.jid".split; local user_exists = require "core.usermanager".user_exists; -local t_insert = table.insert; local function process_to_bare(bare, origin, stanza) local user = bare_sessions[bare]; -- cgit v1.2.3 From c690a13e4023f8ca722cdd5d3340425cd9995e55 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:39:12 +0100 Subject: mod_iq: Remove unused import of jid.split, bare_sessions and don't unpack event.origin when it isn't used. Waqas. --- plugins/mod_iq.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/mod_iq.lua b/plugins/mod_iq.lua index 484a1f8f..55bf59c3 100644 --- a/plugins/mod_iq.lua +++ b/plugins/mod_iq.lua @@ -8,10 +8,8 @@ local st = require "util.stanza"; -local jid_split = require "util.jid".split; local full_sessions = full_sessions; -local bare_sessions = bare_sessions; if module:get_host_type() == "local" then module:hook("iq/full", function(data) @@ -33,7 +31,7 @@ end module:hook("iq/bare", function(data) -- IQ to bare JID recieved - local origin, stanza = data.origin, data.stanza; + local stanza = data.stanza; local type = stanza.attr.type; -- TODO fire post processing events @@ -49,7 +47,7 @@ end); module:hook("iq/self", function(data) -- IQ to self JID recieved - local origin, stanza = data.origin, data.stanza; + local stanza = data.stanza; local type = stanza.attr.type; if type == "get" or type == "set" then @@ -64,7 +62,7 @@ end); module:hook("iq/host", function(data) -- IQ to a local host recieved - local origin, stanza = data.origin, data.stanza; + local stanza = data.stanza; local type = stanza.attr.type; if type == "get" or type == "set" then -- cgit v1.2.3 From df0b3942fba8a8707de05d2559f754eb26424193 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:43:27 +0100 Subject: mod_dialback: Remove unused declaration of xmlns_dialback --- plugins/mod_dialback.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua index e578c412..35186c5e 100644 --- a/plugins/mod_dialback.lua +++ b/plugins/mod_dialback.lua @@ -17,7 +17,6 @@ local st = require "util.stanza"; local sha256_hash = require "util.hashes".sha256; local xmlns_stream = "http://etherx.jabber.org/streams"; -local xmlns_dialback = "jabber:server:dialback"; local dialback_requests = setmetatable({}, { __mode = 'v' }); -- cgit v1.2.3 From ff3d7898c82d33e1f0042810357303a3e645251f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:49:13 +0100 Subject: mod_auth_internal_plain: Remove unused imports --- plugins/mod_auth_internal_plain.lua | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/plugins/mod_auth_internal_plain.lua b/plugins/mod_auth_internal_plain.lua index 93b50351..89dd7f1b 100644 --- a/plugins/mod_auth_internal_plain.lua +++ b/plugins/mod_auth_internal_plain.lua @@ -7,19 +7,11 @@ -- local datamanager = require "util.datamanager"; -local log = require "util.logger".init("auth_internal_plain"); -local type = type; -local error = error; -local ipairs = ipairs; -local hashes = require "util.hashes"; -local jid_bare = require "util.jid".bare; -local config = require "core.configmanager"; local usermanager = require "core.usermanager"; local new_sasl = require "util.sasl".new; local nodeprep = require "util.encodings".stringprep.nodeprep; -local hosts = hosts; -local prosody = _G.prosody; +local log = module._log; function new_default_provider(host) local provider = { name = "internal_plain" }; -- cgit v1.2.3 From a86de6104de901746992d9a9e55ad74c93c1ec6f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:55:25 +0100 Subject: mod_auth_internal_hashed: Remove unused imports --- plugins/mod_auth_internal_hashed.lua | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 399044ad..65cc8143 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -9,18 +9,11 @@ local datamanager = require "util.datamanager"; local log = require "util.logger".init("auth_internal_hashed"); -local type = type; -local error = error; -local ipairs = ipairs; -local hashes = require "util.hashes"; -local jid_bare = require "util.jid".bare; local getAuthenticationDatabaseSHA1 = require "util.sasl.scram".getAuthenticationDatabaseSHA1; -local config = require "core.configmanager"; local usermanager = require "core.usermanager"; local generate_uuid = require "util.uuid".generate; local new_sasl = require "util.sasl".new; local nodeprep = require "util.encodings".stringprep.nodeprep; -local hosts = hosts; -- COMPAT w/old trunk: remove these two lines before 0.8 release local hmac_sha1 = require "util.hmac".sha1; @@ -47,8 +40,6 @@ do end -local prosody = _G.prosody; - -- Default; can be set per-user local iteration_count = 4096; -- cgit v1.2.3 From 5aced008ee1e86efae8f3b42d6877164ff70a929 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 03:59:31 +0100 Subject: mod_auth_internal_hashed: Remove COMPAT code (upgrading old hashed storage format from pre-0.8) --- plugins/mod_auth_internal_hashed.lua | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 65cc8143..607ecab4 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -15,10 +15,6 @@ local generate_uuid = require "util.uuid".generate; local new_sasl = require "util.sasl".new; local nodeprep = require "util.encodings".stringprep.nodeprep; --- COMPAT w/old trunk: remove these two lines before 0.8 release -local hmac_sha1 = require "util.hmac".sha1; -local sha1 = require "util.hashes".sha1; - local to_hex; do local function replace_byte_with_hex(byte) @@ -66,16 +62,6 @@ function new_hashpass_provider(host) return nil, "Auth failed. Stored salt and iteration count information is not complete."; end - -- convert hexpass to stored_key and server_key - -- COMPAT w/old trunk: remove before 0.8 release - if credentials.hashpass then - local salted_password = from_hex(credentials.hashpass); - credentials.stored_key = sha1(hmac_sha1(salted_password, "Client Key"), true); - credentials.server_key = to_hex(hmac_sha1(salted_password, "Server Key")); - credentials.hashpass = nil - datamanager.store(username, host, "accounts", credentials); - end - local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, credentials.salt, credentials.iteration_count); local stored_key_hex = to_hex(stored_key); @@ -149,16 +135,6 @@ function new_hashpass_provider(host) if not credentials then return; end end - -- convert hexpass to stored_key and server_key - -- COMPAT w/old trunk: remove before 0.8 release - if credentials.hashpass then - local salted_password = from_hex(credentials.hashpass); - credentials.stored_key = sha1(hmac_sha1(salted_password, "Client Key"), true); - credentials.server_key = to_hex(hmac_sha1(salted_password, "Server Key")); - credentials.hashpass = nil - datamanager.store(username, host, "accounts", credentials); - end - local stored_key, server_key, iteration_count, salt = credentials.stored_key, credentials.server_key, credentials.iteration_count, credentials.salt; stored_key = stored_key and from_hex(stored_key); server_key = server_key and from_hex(server_key); -- cgit v1.2.3 From 372670b761aca8f49e794aab3bbd29c7a4fe3366 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 04:01:59 +0100 Subject: mod_auth_anonymous: Remove unused logger init --- plugins/mod_auth_anonymous.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_auth_anonymous.lua b/plugins/mod_auth_anonymous.lua index 8d790508..056d7e6b 100644 --- a/plugins/mod_auth_anonymous.lua +++ b/plugins/mod_auth_anonymous.lua @@ -6,7 +6,6 @@ -- COPYING file in the source package for more information. -- -local log = require "util.logger".init("auth_anonymous"); local new_sasl = require "util.sasl".new; local datamanager = require "util.datamanager"; -- cgit v1.2.3 From 1c6fe5f7770b758b26a442eb628600a30eb1b333 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 04:12:34 +0100 Subject: mod_muc/muc.lib.lua: Remove unused imports and variables --- plugins/muc/muc.lib.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 286ad70c..5170c94a 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -9,7 +9,6 @@ local select = select; local pairs, ipairs = pairs, ipairs; -local datamanager = require "util.datamanager"; local datetime = require "util.datetime"; local dataform = require "util.dataforms"; @@ -19,7 +18,6 @@ local jid_bare = require "util.jid".bare; local jid_prep = require "util.jid".prep; local st = require "util.stanza"; local log = require "util.logger".init("mod_muc"); -local multitable_new = require "util.multitable".new; local t_insert, t_remove = table.insert, table.remove; local setmetatable = setmetatable; local base64 = require "util.encodings".base64; @@ -133,7 +131,6 @@ function room_mt:broadcast_message(stanza, historic) stanza = st.clone(stanza); stanza.attr.to = ""; local stamp = datetime.datetime(); - local chars = #tostring(stanza); stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = stamp}):up(); -- XEP-0203 stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) local entry = { stanza = stanza, stamp = stamp }; @@ -185,7 +182,6 @@ function room_mt:send_history(to, stanza) local n = 0; local charcount = 0; - local stanzacount = 0; for i=#history,1,-1 do local entry = history[i]; @@ -855,7 +851,6 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha end elseif stanza.name == "message" and type == "groupchat" then local from, to = stanza.attr.from, stanza.attr.to; - local room = jid_bare(to); local current_nick = self._jid_nick[from]; local occupant = self._occupants[current_nick]; if not occupant then -- not in room -- cgit v1.2.3 From ae8d94109d638d4e604cf4a37782aff2e1ea6001 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 13:58:13 +0100 Subject: mod_bosh: Remove unused import of util.timer --- plugins/mod_bosh.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 1fe52dda..3b5e3e1e 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -18,7 +18,6 @@ local core_process_stanza = core_process_stanza; local st = require "util.stanza"; local logger = require "util.logger"; local log = logger.init("mod_bosh"); -local timer = require "util.timer"; local xmlns_streams = "http://etherx.jabber.org/streams"; local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; -- cgit v1.2.3 From ca695ad8df44ad83ad2eb9d327cf4552e2780638 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 13:58:37 +0100 Subject: mod_bosh: Fix request/response mixup --- plugins/mod_bosh.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 3b5e3e1e..5b7a24ab 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -379,7 +379,7 @@ function stream_callbacks.error(context, error) local response = context.response; response.headers = default_headers; response.status_code = 400; - request:send(); + response:send(); return; end -- cgit v1.2.3 From 0438557094bd1d6405edbb1c93b48f06c4048f5d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 14:03:39 +0100 Subject: mod_bosh: Remove unused send_buffer variable --- plugins/mod_bosh.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 5b7a24ab..24dc3755 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -262,7 +262,7 @@ function stream_callbacks.streamopened(context, attr) session.log("debug", "BOSH session created for request from %s", session.ip); log("info", "New BOSH session, assigned it sid '%s'", sid); - local r, send_buffer = session.requests, session.send_buffer; + local r = session.requests; function session.send(s) -- We need to ensure that outgoing stanzas have the jabber:client xmlns if s.attr and not s.attr.xmlns then -- cgit v1.2.3 From a3c2321643b55ba02cba804e7b1bcf6a2b252fa4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 14:13:51 +0100 Subject: net.httpserver, net.httpserver_listener: Remove (obsoleted by net.http.* and mod_http, see http://prosody.im/doc/developers/http ) --- net/httpserver.lua | 238 -------------------------------------------- net/httpserver_listener.lua | 46 --------- 2 files changed, 284 deletions(-) delete mode 100644 net/httpserver.lua delete mode 100644 net/httpserver_listener.lua diff --git a/net/httpserver.lua b/net/httpserver.lua deleted file mode 100644 index 0f5a4186..00000000 --- a/net/httpserver.lua +++ /dev/null @@ -1,238 +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. --- - - -local url_parse = require "socket.url".parse; -local httpstream_new = require "util.httpstream".new; - ---local connlisteners_start = require "net.connlisteners".start; ---local connlisteners_get = require "net.connlisteners".get; -local listener; - -local t_insert, t_concat = table.insert, table.concat; -local tonumber, tostring, pairs, ipairs, type = tonumber, tostring, pairs, ipairs, type; -local xpcall = xpcall; -local debug_traceback = debug.traceback; - -local urlencode = function (s) return s and (s:gsub("%W", function (c) return ("%%%02x"):format(c:byte()); end)); end - -local log = require "util.logger".init("httpserver"); - -local http_servers = {}; - -module "httpserver" - -local default_handler; - -local function send_response(request, response) - -- Write status line - local resp; - 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; - if h then - for k, v in pairs(h) do - t_insert(resp, k..": "..v.."\r\n"); - end - end - if body and not (h and h["Content-Length"]) then - t_insert(resp, "Content-Length: "..#body.."\r\n"); - end - t_insert(resp, "\r\n"); - - if body and request.method ~= "HEAD" then - t_insert(resp, body); - end - request.write(t_concat(resp)); - else - -- Response we have is just a string (the body) - log("debug", "Sending 200 response to %s", request.id or ""); - - local resp = "HTTP/1.0 200 OK\r\n" - .. "Connection: close\r\n" - .. "Content-Type: text/html\r\n" - .. "Content-Length: "..#response.."\r\n" - .. "\r\n" - .. response; - - request.write(resp); - end - if not request.stayopen then - request:destroy(); - end -end - -local function call_callback(request, err) - if request.handled then return; end - request.handled = true; - local callback = request.callback; - if not callback and request.path then - local path = request.url.path; - local base = path:match("^/([^/?]+)"); - if not base then - base = path:match("^http://[^/?]+/([^/?]+)"); - end - - callback = (request.server and request.server.handlers[base]) or default_handler; - end - if callback then - local _callback = callback; - function callback(method, body, request) - local ok, result = xpcall(function() return _callback(method, body, request) end, debug_traceback); - if ok then return result; end - log("error", "Error in HTTP server handler: %s", result); - -- TODO: When we support pipelining, request.destroyed - -- won't be the right flag - we just want to see if there - -- has been a response to this request yet. - if not request.destroyed then - return { - status = "500 Internal Server Error"; - headers = { ["Content-Type"] = "text/plain" }; - body = "There was an error processing your request. See the error log for more details."; - }; - end - end - if err then - log("debug", "Request error: "..err); - if not callback(nil, err, request) then - destroy_request(request); - end - return; - end - - local response = callback(request.method, request.body and t_concat(request.body), request); - if response then - if response == true and not request.destroyed then - -- Keep connection open, we will reply later - log("debug", "Request %s left open, on_destroy is %s", request.id, tostring(request.on_destroy)); - elseif response ~= true then - -- Assume response - send_response(request, response); - destroy_request(request); - end - else - log("debug", "Request handler provided no response, destroying request..."); - -- No response, close connection - destroy_request(request); - end - end -end - -local function request_reader(request, data, startpos) - if not request.parser then - local function success_cb(r) - for k,v in pairs(r) do request[k] = v; end - request.url = url_parse(request.path); - request.url.path = request.url.path and request.url.path:gsub("%%(%x%x)", function(x) return x.char(tonumber(x, 16)) end); - request.body = { request.body }; - call_callback(request); - end - local function error_cb(r) - call_callback(request, r or "connection-closed"); - destroy_request(request); - end - request.parser = httpstream_new(success_cb, error_cb); - end - request.parser:feed(data); -end - --- The default handler for requests -default_handler = function (method, body, request) - log("debug", method.." request for "..tostring(request.path) .. " on port "..request.handler:serverport()); - return { status = "404 Not Found", - headers = { ["Content-Type"] = "text/html" }, - body = "Page Not FoundNot here :(" }; -end - - -function new_request(handler) - return { handler = handler, conn = handler, - write = function (...) return handler:write(...); end, state = "request", - server = http_servers[handler:serverport()], - send = send_response, - destroy = destroy_request, - id = tostring{}:match("%x+$") - }; -end - -function destroy_request(request) - log("debug", "Destroying request %s", request.id); - --listener = listener or connlisteners_get("httpserver"); - if not request.destroyed then - request.destroyed = true; - if request.on_destroy then - log("debug", "Request has destroy callback"); - request.on_destroy(request); - else - log("debug", "Request has no destroy callback"); - end - request.handler:close() - if request.conn then - listener.ondisconnect(request.conn, "closed"); - end - end -end - -function new(params) - local http_server = http_servers[params.port]; - if not http_server then - http_server = { handlers = {} }; - http_servers[params.port] = http_server; - -- We weren't already listening on this port, so start now - --connlisteners_start("httpserver", params); - end - if params.base then - http_server.handlers[params.base] = params.handler; - end -end - -function set_default_handler(handler) - default_handler = handler; -end - -function new_from_config(ports, handle_request, default_options) - if type(handle_request) == "string" then -- COMPAT with old plugins - log("warn", "Old syntax of httpserver.new_from_config being used to register %s", handle_request); - handle_request, default_options = default_options, { base = handle_request }; - end - ports = ports or {5280}; - for _, options in ipairs(ports) do - local port = default_options.port or 5280; - local base = default_options.base; - local ssl = default_options.ssl or false; - local interface = default_options.interface; - if type(options) == "number" then - port = options; - elseif type(options) == "table" then - port = options.port or port; - base = options.path or base; - ssl = options.ssl or ssl; - interface = options.interface or interface; - elseif type(options) == "string" then - base = options; - end - - if ssl then - ssl.mode = "server"; - ssl.protocol = "sslv23"; - ssl.options = "no_sslv2"; - end - - new{ port = port, interface = interface, - base = base, handler = handle_request, - ssl = ssl, type = (ssl and "ssl") or "tcp" }; - end -end - -_M.request_reader = request_reader; -_M.send_response = send_response; -_M.urlencode = urlencode; - -return _M; diff --git a/net/httpserver_listener.lua b/net/httpserver_listener.lua deleted file mode 100644 index dd14b43c..00000000 --- a/net/httpserver_listener.lua +++ /dev/null @@ -1,46 +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. --- - - - -local connlisteners_register = require "net.connlisteners".register; -local new_request = require "net.httpserver".new_request; -local request_reader = require "net.httpserver".request_reader; - -local requests = {}; -- Open requests - -local httpserver = { default_port = 80, default_mode = "*a" }; - -function httpserver.onincoming(conn, data) - local request = requests[conn]; - - if not request then - request = new_request(conn); - requests[conn] = request; - - -- If using HTTPS, request is secure - if conn:ssl() then - request.secure = true; - end - end - - if data and data ~= "" then - request_reader(request, data); - end -end - -function httpserver.ondisconnect(conn, err) - local request = requests[conn]; - if request and not request.destroyed then - request.conn = nil; - request_reader(request, nil); - end - requests[conn] = nil; -end - -connlisteners_register("httpserver", httpserver); -- cgit v1.2.3 From 423b3a5296a8b163ff05251892645a1b8ae27f5d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 14:27:48 +0100 Subject: net.http.server: Remove unused imports and variables (fixes traceback due to removed net.httpserver) --- net/http/server.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index 8b91b4d3..295592d3 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -12,8 +12,6 @@ local xpcall = xpcall; local debug = debug; local tostring = tostring; local codes = require "net.http.codes"; -local _G = _G; -local legacy_httpserver = require "net.httpserver"; local _M = {}; @@ -90,7 +88,7 @@ function listener.onconnect(conn) local secure = conn:ssl() and true or nil; local pending = {}; local waiting = false; - local function process_next(last_response) + local function process_next() --if waiting then log("debug", "can't process_next, waiting"); return; end if sessions[conn] and #pending > 0 then local request = t_remove(pending); @@ -198,7 +196,7 @@ function handle_request(conn, request, finish_cb) local result = events.fire_event(event, payload); if result ~= nil then if result ~= true then - local code, body = 200, ""; + local body = ""; local result_type = type(result); if result_type == "number" then response.status_code = result; -- cgit v1.2.3 From 3359d1e028dcc5f22a25d2117fcd1fa108923194 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 14:31:02 +0100 Subject: util.httpstream: Remove COMPAT properties from request --- util/httpstream.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/util/httpstream.lua b/util/httpstream.lua index bdc3fce7..190b3ed6 100644 --- a/util/httpstream.lua +++ b/util/httpstream.lua @@ -107,9 +107,6 @@ local function parser(success_cb, parser_type, options_cb) httpversion = httpversion; headers = headers; body = body; - -- COMPAT the properties below are deprecated - responseversion = httpversion; - responseheaders = headers; }); end else coroutine.yield("unknown-parser-type"); end -- cgit v1.2.3 From 2b144a94d1c0da5ab932497cad20e23996bdafc9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 14:35:52 +0100 Subject: usermanager: Add log error for use of COMPAT config option 'anonymous_login'. To be removed in next version. --- core/usermanager.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/usermanager.lua b/core/usermanager.lua index 9e5a016c..50aee701 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -41,7 +41,10 @@ function initialize_host(host) host_session.events.add_handler("item-added/auth-provider", function (event) local provider = event.item; local auth_provider = config.get(host, "core", "authentication") or default_provider; - if config.get(host, "core", "anonymous_login") then auth_provider = "anonymous"; end -- COMPAT 0.7 + if config.get(host, "core", "anonymous_login") then + log("error", "Deprecated config option 'anonymous_login'. Use authentication = 'anonymous' instead."); + auth_provider = "anonymous"; + end -- COMPAT 0.7 if provider.name == auth_provider then host_session.users = setmetatable(provider, provider_mt); end -- cgit v1.2.3 From 24c350bf73e07152e3db0eceb60c11c89288a348 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 14:46:36 +0100 Subject: mod_http: Depend on mod_http_errors --- plugins/mod_http.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index c379a562..b7376831 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -7,6 +7,7 @@ -- module:set_global(); +module:depends("http_errors"); local server = require "net.http.server"; -- cgit v1.2.3 From 235e9dba035be2e2db15eba4dcb11bbd03c25d56 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 14:48:12 +0100 Subject: prosody.cfg.lua.dist: Rename mod_httpserver to mod_http_files, and move both it and mod_bosh to a new 'HTTP modules' category --- prosody.cfg.lua.dist | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/prosody.cfg.lua.dist b/prosody.cfg.lua.dist index db85a70b..c0c729a9 100644 --- a/prosody.cfg.lua.dist +++ b/prosody.cfg.lua.dist @@ -56,11 +56,13 @@ modules_enabled = { -- Admin interfaces "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands --"admin_telnet"; -- Opens telnet console interface on localhost port 5582 + + -- HTTP modules + --"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP" + --"http_files"; -- Serve static files from a directory over HTTP -- Other specific functionality --"posix"; -- POSIX functionality, sends server to background, enables syslog, etc. - --"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP" - --"httpserver"; -- Serve static files from a directory over HTTP --"groups"; -- Shared roster support --"announce"; -- Send announcement to all online users --"welcome"; -- Welcome users who register accounts -- cgit v1.2.3 From 4bb43a3fb610cfa2ee3b972db188f93e5efece8c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 15:47:43 +0100 Subject: modulemanager: Fixes to handle circular dependencies in module:depends() --- core/modulemanager.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 8fdd5c1d..90116166 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -139,9 +139,12 @@ local function do_load_module(host, module_name) }); local host_module = setmetatable({ module = host_module_api }, { __index = mod }); host_module_api.environment = host_module; - local ok, result, module_err = call_module_method(mod, "add_host", host_module_api); - if not ok or result == false then return nil, ok and module_err or result; end modulemap[host][module_name] = host_module; + local ok, result, module_err = call_module_method(mod, "add_host", host_module_api); + if not ok or result == false then + modulemap[host][module_name] = nil; + return nil, ok and module_err or result; + end return host_module; end return nil, "global-module-already-loaded"; @@ -164,6 +167,7 @@ local function do_load_module(host, module_name) setfenv(mod, pluginenv); + modulemap[host][module_name] = pluginenv; local ok, err = pcall(mod); if ok then -- Call module's "load" @@ -174,12 +178,13 @@ local function do_load_module(host, module_name) end end - modulemap[api_instance.host][module_name] = pluginenv; if api_instance.host == "*" then if not api_instance.global then -- COMPAT w/pre-0.9 log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); api_instance:set_global(); end + modulemap[host][module_name] = nil; + modulemap[api_instance.host][module_name] = pluginenv; if host ~= api_instance.host and module_has_method(pluginenv, "add_host") then -- Now load the module again onto the host it was originally being loaded on ok, err = do_load_module(host, module_name); @@ -187,6 +192,7 @@ local function do_load_module(host, module_name) end end if not ok then + modulemap[api_instance.host][module_name] = nil; log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); end return ok and pluginenv, err; -- cgit v1.2.3 From 1a67790a0fa9788648d9c450892b477323275b54 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 28 Apr 2012 17:18:03 +0200 Subject: mod_admin_telnet: Add c2s:count() which shows number of connected users. --- plugins/mod_admin_telnet.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 202170ba..544ea161 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -440,6 +440,16 @@ local function show_c2s(callback) end end +function def_env.c2s:count(match_jid) + local count = 0; + show_c2s(function (jid, session) + if (not match_jid) or jid:match(match_jid) then + count = count + 1; + end + end); + return true, "Total: "..count.." clients"; +end + function def_env.c2s:show(match_jid) local print, count = self.session.print, 0; local curr_host; -- cgit v1.2.3 From 0dd908250dfd49f0e2e483040744630e60e3fc9d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 16:20:26 +0100 Subject: util.debug: Re-fix parameter handling (I think it matches debug.traceback() more accurately now) and document level fudge --- util/debug.lua | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/util/debug.lua b/util/debug.lua index 1d3b5648..321b3267 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -119,21 +119,26 @@ end function _traceback(thread, message, level) - if type(thread) ~= "thread" then + -- Lua manual says: debug.traceback ([thread,] [message [, level]]) + -- I fathom this to mean one of: + -- () + -- (thread) + -- (message, level) + -- (thread, message, level) + + if thread == nil then -- Defaults + thread, message, level = coroutine.running(), message, level; + elseif type(thread) == "string" then thread, message, level = coroutine.running(), thread, message; + elseif type(thread) ~= "thread" then + return nil; -- debug.traceback() does this end - if level and type(message) ~= "string" then - return nil, "invalid message"; - elseif not level then - if type(message) == "number" then - level, message = message, nil; - else - level = 1; - end - end - + + level = level or 1; + message = message and (message.."\n") or ""; + -- +3 counts for this function, and the pcall() and wrapper above us local levels = get_traceback_table(thread, level+3); local last_source_desc; -- cgit v1.2.3 From 4a8ac583de5767b4352cc19489e83037268d4c58 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 16:21:19 +0100 Subject: util.debug: Add +1 to level when getting locals table, to account for current function --- util/debug.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/debug.lua b/util/debug.lua index 321b3267..bff0e347 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -97,7 +97,7 @@ function get_traceback_table(thread, start_level) levels[(level-start_level)+1] = { level = level; info = info; - locals = get_locals_table(level); + locals = get_locals_table(level+1); upvalues = get_upvalues_table(info.func); }; end -- cgit v1.2.3 From 6b442d2717f120aae24ecb9061be22d133516db0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 16:21:57 +0100 Subject: mod_admin_adhoc: Small style fix --- plugins/mod_admin_adhoc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_admin_adhoc.lua b/plugins/mod_admin_adhoc.lua index 6f1357a9..4d2c60d7 100644 --- a/plugins/mod_admin_adhoc.lua +++ b/plugins/mod_admin_adhoc.lua @@ -24,7 +24,7 @@ local dataforms_new = require "util.dataforms".new; local array = require "util.array"; local modulemanager = require "modulemanager"; -module:depends"adhoc"; +module:depends("adhoc"); local adhoc_new = module:require "adhoc".new; function add_user_command_handler(self, data, state) -- cgit v1.2.3 From 54fc3f7da85c174d090d5eeba548302766b61769 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 16:29:00 +0100 Subject: mod_s2s: Fix import of core_process_stanza. I don't know why I thought it was there (thanks Zash) --- plugins/s2s/mod_s2s.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index e40f35bd..f44ab43d 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -10,7 +10,7 @@ module:set_global(); local prosody = prosody; local hosts = prosody.hosts; -local core_process_stanza = prosody.core_process_stanza; +local core_process_stanza = core_process_stanza; local tostring, type = tostring, type; local t_insert = table.insert; -- cgit v1.2.3 From 712235990b38806ec957a2a891f06a7f6e2b4fc8 Mon Sep 17 00:00:00 2001 From: Paul Aurich Date: Sat, 28 Apr 2012 08:35:34 -0700 Subject: mod_s2s: Log certificate errors, for troubleshooting goodness --- plugins/s2s/mod_s2s.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua index f44ab43d..cee7d6f6 100644 --- a/plugins/s2s/mod_s2s.lua +++ b/plugins/s2s/mod_s2s.lua @@ -134,6 +134,9 @@ local function check_cert_status(session) -- Is there any interest in printing out all/the number of errors here? if not chain_valid then (session.log or log)("debug", "certificate chain validation result: invalid"); + for depth, t in ipairs(errors) do + (session.log or log)("debug", "certificate error(s) at depth %d: %s", depth-1, table.concat(t, ", ")) + end session.cert_chain_status = "invalid"; else (session.log or log)("debug", "certificate chain validation result: valid"); -- cgit v1.2.3 From 54533bf122095fce26d32614040ad3261b0f89fa Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 16:41:35 +0100 Subject: net.httpserver: Add compatibility stub --- net/httpserver.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 net/httpserver.lua diff --git a/net/httpserver.lua b/net/httpserver.lua new file mode 100644 index 00000000..7d574788 --- /dev/null +++ b/net/httpserver.lua @@ -0,0 +1,15 @@ +-- COMPAT w/pre-0.9 +local log = require "util.logger".init("net.httpserver"); +local traceback = debug.traceback; + +module "httpserver" + +function fail() + log("error", "Attempt to use legacy HTTP API. For more info see http://prosody.im/doc/developers/legacy_http"); + log("error", "Legacy HTTP API usage, %s", traceback("", 2)); +end + +new, new_from_config = fail, fail; +set_default_handler = fail; + +return _M; -- cgit v1.2.3 From 67b91128c9fbbbfe49e0fc571c0542c7187cf762 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 18:36:03 +0100 Subject: mod_muc/muc.lib: Fall back to default_history_length if no length in config --- plugins/muc/muc.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 5170c94a..9be1736f 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -336,7 +336,7 @@ function room_mt:get_changesubject() return self._data.changesubject; end function room_mt:get_historylength() - return self._data.history_length + return self._data.history_length or default_history_length; end function room_mt:set_historylength(length) if tonumber(length) == nil then -- cgit v1.2.3 From 6521b0e6612036ceb4d4db1a8e6a99ac3d3a4ec2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 18:45:04 +0100 Subject: portmanager: Ensure port is always a number (thanks Zash) --- core/portmanager.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index 3bd143ef..5f53b61c 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -112,6 +112,7 @@ function activate(service_name) for interface in bind_interfaces do for port in bind_ports do + port = tonumber(port); if #active_services:search(nil, interface, port) > 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 "", service_name or ""); else -- cgit v1.2.3 From cd8b026b999a7b7abc77f557302ac751982f6d6d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 19:36:09 +0100 Subject: net.http.server: Use response.body if it exists and body is not specified to send_response --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 295592d3..e15e6e14 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -228,7 +228,7 @@ function _M.send_response(response, body) local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); local headers = response.headers; - body = body or ""; + body = body or response.body or ""; headers.content_length = #body; local output = { status_line }; -- cgit v1.2.3 From 2f5ffa76dcb1a7a5968b8cb4d5a20041b7d81d56 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 19:36:57 +0100 Subject: net.http.server: Code cleanup/adjustment now that send_response() accepts response.body --- net/http/server.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/http/server.lua b/net/http/server.lua index e15e6e14..69908e4e 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -196,7 +196,7 @@ function handle_request(conn, request, finish_cb) local result = events.fire_event(event, payload); if result ~= nil then if result ~= true then - local body = ""; + local body; local result_type = type(result); if result_type == "number" then response.status_code = result; @@ -206,8 +206,6 @@ function handle_request(conn, request, finish_cb) elseif result_type == "string" then body = result; elseif result_type == "table" then - body = result.body; - result.body = nil; for k, v in pairs(result) do response[k] = v; end -- cgit v1.2.3 From 53ab26d52831cce03092a0c120cf95276c52ac28 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 19:38:51 +0100 Subject: portmanager: Import tonumber (thanks Zash) --- core/portmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 5f53b61c..fed35987 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -7,7 +7,7 @@ local set = require "util.set"; local table, package = table, package; local setmetatable, rawset, rawget = setmetatable, rawset, rawget; -local type = type; +local type, tonumber = type, tonumber; local prosody = prosody; local fire_event = prosody.events.fire_event; -- cgit v1.2.3 From 31dd27582930bbd9e297531046b223c35491baa5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 19:41:04 +0100 Subject: moduleapi: Add module:get_directory() to get module file's directory, and module:load_resource() to load a file relative to that directory --- core/moduleapi.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 703eb268..57367255 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -329,4 +329,14 @@ function api:add_timer(delay, callback) end); end +local path_sep = package.config:sub(1,1); +function api:get_directory() + return self.path and (self.path:gsub("%"..path_sep.."[^"..path_sep.."]*$", "")) or nil; +end + +function api:load_resource(path, mode) + path = config.resolve_relative_path(self:get_directory(), path); + return io.open(path, mode); +end + return api; -- cgit v1.2.3 From c113aa80c244cafc1f14e4d5942fc35f207ab120 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Sat, 28 Apr 2012 22:34:05 +0200 Subject: s2sout.lib: Check whether lua-socket supports IPv6 --- plugins/s2s/s2sout.lib.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua index 011c864f..f3496597 100644 --- a/plugins/s2s/s2sout.lib.lua +++ b/plugins/s2s/s2sout.lib.lua @@ -271,6 +271,10 @@ function s2sout.make_connect(host_session, connect_host, connect_port) if connect_host.proto == "IPv4" then conn, handler = socket.tcp(); else + if not socket.tcp6 then + log("warn", "Could not connect to "..to_host..". Your version of lua-socket does not support IPv6"); + return false, "no-ipv6"; + end conn, handler = socket.tcp6(); end -- cgit v1.2.3 From 1ee9f6e09228b268bf3f5dc985d4ba9026a72666 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 23:13:49 +0100 Subject: net.connlisteners: Add COMPAT stub, use portmanager! --- net/connlisteners.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 net/connlisteners.lua diff --git a/net/connlisteners.lua b/net/connlisteners.lua new file mode 100644 index 00000000..99ddc720 --- /dev/null +++ b/net/connlisteners.lua @@ -0,0 +1,15 @@ +-- COMPAT w/pre-0.9 +local log = require "util.logger".init("net.connlisteners"); +local traceback = debug.traceback; + +module "httpserver" + +function fail() + log("error", "Attempt to use legacy connlisteners API. For more info see http://prosody.im/doc/developers/network"); + log("error", "Legacy connlisteners API usage, %s", traceback("", 2)); +end + +register, deregister = fail, fail; +get, start = fail, fail, epic_fail; + +return _M; -- cgit v1.2.3