diff options
-rw-r--r-- | core/loggingmanager.lua | 12 | ||||
-rw-r--r-- | core/modulemanager.lua | 7 | ||||
-rw-r--r-- | core/s2smanager.lua | 2 | ||||
-rw-r--r-- | core/storagemanager.lua | 16 | ||||
-rw-r--r-- | net/dns.lua | 46 | ||||
-rw-r--r-- | net/httpserver.lua | 1 | ||||
-rw-r--r-- | net/xmppclient_listener.lua | 13 | ||||
-rw-r--r-- | net/xmppcomponent_listener.lua | 71 | ||||
-rw-r--r-- | net/xmppserver_listener.lua | 25 | ||||
-rw-r--r-- | plugins/mod_admin_telnet.lua (renamed from plugins/mod_console.lua) | 2 | ||||
-rw-r--r-- | plugins/mod_bosh.lua | 15 | ||||
-rw-r--r-- | plugins/mod_pep.lua | 3 | ||||
-rw-r--r-- | plugins/mod_pubsub.lua | 32 | ||||
-rw-r--r-- | plugins/mod_saslauth.lua | 2 | ||||
-rw-r--r-- | plugins/mod_storage_sql.lua | 239 | ||||
-rwxr-xr-x | prosody | 2 | ||||
-rwxr-xr-x | prosodyctl | 50 | ||||
-rw-r--r-- | util-src/windows.c | 29 | ||||
-rw-r--r-- | util/pubsub.lua | 4 | ||||
-rw-r--r-- | util/serialization.lua | 23 | ||||
-rw-r--r-- | util/stanza.lua | 2 | ||||
-rw-r--r-- | util/termcolours.lua | 37 | ||||
-rw-r--r-- | util/x509.lua (renamed from util/certverification.lua) | 4 |
23 files changed, 516 insertions, 121 deletions
diff --git a/core/loggingmanager.lua b/core/loggingmanager.lua index 3cd09431..40b96d52 100644 --- a/core/loggingmanager.lua +++ b/core/loggingmanager.lua @@ -15,7 +15,7 @@ local tostring, setmetatable, rawset, pairs, ipairs, type = local io_open, io_write = io.open, io.write; local math_max, rep = math.max, string.rep; local os_date, os_getenv = os.date, os.getenv; -local getstyle, getstring = require "util.termcolours".getstyle, require "util.termcolours".getstring; +local getstyle, setstyle = require "util.termcolours".getstyle, require "util.termcolours".setstyle; if os.getenv("__FLUSH_LOG") then local io_flush = io.flush; @@ -217,7 +217,7 @@ function log_sink_types.stdout() end do - local do_pretty_printing = not os_getenv("WINDIR"); + local do_pretty_printing = true; local logstyles = {}; if do_pretty_printing then @@ -244,10 +244,14 @@ do if timestamps then io_write(os_date(timestamps), " "); end + io_write(name, rep(" ", sourcewidth-namelen)); + setstyle(logstyles[level]); + io_write(level); + setstyle(); if ... then - io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", format(message, ...), "\n"); + io_write("\t", format(message, ...), "\n"); else - io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", message, "\n"); + io_write("\t", message, "\n"); end end end diff --git a/core/modulemanager.lua b/core/modulemanager.lua index b12cddf0..8928ce14 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -77,6 +77,13 @@ function load_modules_for_host(host) end local modules = global_modules + host_modules; + -- COMPAT w/ pre 0.8 + if modules:contains("console") then + log("error", "The mod_console plugin has been renamed to mod_admin_telnet. Please update your config."); + modules:remove("console"); + modules:add("admin_telnet"); + end + if component then load(host, component); end diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 0990a0aa..2f6c8478 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -27,7 +27,7 @@ local modulemanager = require "core.modulemanager"; local st = require "stanza"; local stanza = st.stanza; local nameprep = require "util.encodings".stringprep.nameprep; -local cert_verify_identity = require "util.certverification".verify_identity; +local cert_verify_identity = require "util.x509".verify_identity; local fire_event = prosody.events.fire_event; local uuid_gen = require "util.uuid".generate; diff --git a/core/storagemanager.lua b/core/storagemanager.lua index 0857baf4..e0c12190 100644 --- a/core/storagemanager.lua +++ b/core/storagemanager.lua @@ -11,6 +11,7 @@ local log = require "util.logger".init("storagemanager"); local olddm = {}; -- maintain old datamanager, for backwards compatibility for k,v in pairs(datamanager) do olddm[k] = v; end +local prosody = prosody; module("storagemanager") @@ -25,6 +26,7 @@ function default_driver_mt:set(user, data) return olddm.store(user, self.host, s local stores_available = multitable.new(); function initialize_host(host) + local host_session = hosts[host]; host_session.events.add_handler("item-added/data-driver", function (event) local item = event.item; stores_available:set(host, item.name, item); @@ -35,19 +37,19 @@ function initialize_host(host) stores_available:set(host, item.name, nil); end); end +prosody.events.add_handler("host-activated", initialize_host, 101); local function load_driver(host, driver_name) if not driver_name then return; end local driver = stores_available:get(host, driver_name); - if not driver then - if driver_name ~= "internal" then - modulemanager.load(host, "storage_"..driver_name); - return stores_available:get(host, driver_name); - else - return setmetatable({host = host}, default_driver_mt); - end + if driver then return driver; end + if driver_name ~= "internal" then + modulemanager.load(host, "storage_"..driver_name); + return stores_available:get(host, driver_name); + else + return setmetatable({host = host}, default_driver_mt); end end diff --git a/net/dns.lua b/net/dns.lua index c0875b5a..23a453aa 100644 --- a/net/dns.lua +++ b/net/dns.lua @@ -21,8 +21,8 @@ local is_windows = (_ and windows) or os.getenv("WINDIR"); local coroutine, io, math, string, table = coroutine, io, math, string, table; -local ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack, select = - ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack, select; +local ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack, select, type= + ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack, select, type; local ztact = { -- public domain 20080404 lua@ztact.com get = function(parent, ...) @@ -160,29 +160,24 @@ resolver.timeout = default_timeout; local SRV_tostring; +local function default_rr_tostring(rr) + local rr_val = rr.type and rr[rr.type:lower()]; + if type(rr_val) ~= "string" then + return "<UNKNOWN RDATA TYPE>"; + end + return rr_val; +end + +local special_tostrings = { + LOC = resolver.LOC_tostring; + MX = function (rr) return string.format('%2i %s', rr.pref, rr.mx); end; + SRV = SRV_tostring; +}; local rr_metatable = {}; -- - - - - - - - - - - - - - - - - - - rr_metatable function rr_metatable.__tostring(rr) - local s0 = string.format('%2s %-5s %6i %-28s', rr.class, rr.type, rr.ttl, rr.name); - local s1 = ''; - if rr.type == 'A' then - s1 = ' '..rr.a; - elseif rr.type == 'MX' then - s1 = string.format(' %2i %s', rr.pref, rr.mx); - elseif rr.type == 'CNAME' then - s1 = ' '..rr.cname; - elseif rr.type == 'LOC' then - s1 = ' '..resolver.LOC_tostring(rr); - elseif rr.type == 'NS' then - s1 = ' '..rr.ns; - elseif rr.type == 'SRV' then - s1 = ' '..SRV_tostring(rr); - elseif rr.type == 'TXT' then - s1 = ' '..rr.txt; - else - s1 = ' <UNKNOWN RDATA TYPE>'; - end - return s0..s1; + local rr_string = (special_tostrings[rr.type] or default_rr_tostring)(rr); + return string.format('%2s %-5s %6i %-28s %s', rr.class, rr.type, rr.ttl, rr.name, rr_string); end @@ -939,6 +934,9 @@ function resolver:lookupex(handler, qname, qtype, qclass) -- - - - - - - - - return self:peek(qname, qtype, qclass) or self:query(qname, qtype, qclass); end +function resolver:tohostname(ip) + return dns.lookup(ip:gsub("(%d+)%.(%d+)%.(%d+)%.(%d+)", "%4.%3.%2.%1.in-addr.arpa."), "PTR"); +end --print ---------------------------------------------------------------- print @@ -1014,6 +1012,10 @@ function dns.lookup(...) -- - - - - - - - - - - - - - - - - - - - - lookup return _resolver:lookup(...); end +function dns.tohostname(...) + return _resolver:tohostname(...); +end + function dns.purge(...) -- - - - - - - - - - - - - - - - - - - - - - purge return _resolver:purge(...); end diff --git a/net/httpserver.lua b/net/httpserver.lua index ca830a5a..4c1200ac 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -190,6 +190,7 @@ function new_from_config(ports, handle_request, default_options) 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; diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index 7cffc0ca..4cc90cbf 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -10,22 +10,19 @@ local logger = require "logger"; local log = logger.init("xmppclient_listener"); -local lxp = require "lxp" local new_xmpp_stream = require "util.xmppstream".new; -local sm_new_session = require "core.sessionmanager".new_session; local connlisteners_register = require "net.connlisteners".register; -local t_insert = table.insert; -local t_concat = table.concat; -local t_concatall = function (t, sep) local tt = {}; for _, s in ipairs(t) do t_insert(tt, tostring(s)); end return t_concat(tt, sep); end -local m_random = math.random; -local format = string.format; 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"); @@ -62,7 +59,7 @@ function stream_callbacks.error(session, error, data) end end -local function handleerr(err) log("error", "Traceback[c2s]: %s: %s", tostring(err), debug.traceback()); 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 diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index 3ffa4ba4..90293559 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -10,6 +10,9 @@ local hosts = _G.hosts; local t_concat = table.concat; +local tostring = tostring; +local type = type; +local pairs = pairs; local lxp = require "lxp"; local logger = require "util.logger"; @@ -162,46 +165,42 @@ local function session_close(session, reason) end --- Component connlistener -function component_listener.onincoming(conn, data) - local session = sessions[conn]; - if not session then - local _send = conn.write; - session = { type = "component", conn = conn, send = function (data) return _send(conn, tostring(data)); end }; - sessions[conn] = session; - - -- 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 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; - 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; - + 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 - if data then - session.data(conn, data); + + 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 -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 diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index 6b782240..3af0b962 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -7,10 +7,16 @@ -- +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 lxp = require "lxp" +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; @@ -48,7 +54,7 @@ function stream_callbacks.error(session, error, data) end end -local function handleerr(err) log("error", "Traceback[s2s]: %s: %s", tostring(err), debug.traceback()); 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; @@ -59,17 +65,6 @@ function stream_callbacks.handlestanza(session, stanza) end end -local connlisteners_register = require "net.connlisteners".register; - -local t_insert = table.insert; -local t_concat = table.concat; -local t_concatall = function (t, sep) local tt = {}; for _, s in ipairs(t) do t_insert(tt, tostring(s)); end return t_concat(tt, sep); end -local m_random = math.random; -local format = string.format; -local sessionmanager = require "core.sessionmanager"; -local sm_new_session, sm_destroy_session = sessionmanager.new_session, sessionmanager.destroy_session; -local st = require "util.stanza"; - local sessions = {}; local xmppserver = { default_port = 5269, default_mode = "*a" }; @@ -173,9 +168,9 @@ function xmppserver.onstatus(conn, status) if status == "ssl-handshake-complete" then local session = sessions[conn]; if session and session.direction == "outgoing" then - local format, to_host, from_host = string.format, session.to_host, session.from_host; + local to_host, from_host = session.to_host, session.from_host; session.log("debug", "Sending stream header..."); - session.sends2s(format([[<stream:stream xmlns='jabber:server' xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' from='%s' to='%s' version='1.0'>]], from_host, to_host)); + session.sends2s(s_format([[<stream:stream xmlns='jabber:server' xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' from='%s' to='%s' version='1.0'>]], from_host, to_host)); end end end diff --git a/plugins/mod_console.lua b/plugins/mod_admin_telnet.lua index 0b2dd5f8..da40f57e 100644 --- a/plugins/mod_console.lua +++ b/plugins/mod_admin_telnet.lua @@ -19,7 +19,7 @@ local console_listener = { default_port = 5582; default_mode = "*l"; default_int require "util.iterators"; local jid_bare = require "util.jid".bare; local set, array = require "util.set", require "util.array"; -local cert_verify_identity = require "util.certverification".verify_identity; +local cert_verify_identity = require "util.x509".verify_identity; local commands = {}; local def_env = {}; diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index d346f247..58254169 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -33,7 +33,6 @@ local BOSH_DEFAULT_HOLD = tonumber(module:get_option("bosh_default_hold")) or 1; local BOSH_DEFAULT_INACTIVITY = tonumber(module:get_option("bosh_max_inactivity")) or 60; local BOSH_DEFAULT_POLLING = tonumber(module:get_option("bosh_max_polling")) or 5; local BOSH_DEFAULT_REQUESTS = tonumber(module:get_option("bosh_max_requests")) or 2; -local BOSH_DEFAULT_MAXPAUSE = tonumber(module:get_option("bosh_max_pause")) or 300; local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure"); @@ -283,9 +282,17 @@ function stream_callbacks.streamopened(request, attr) fire_event("stream-features", session, features); --xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh' local response = st.stanza("body", { xmlns = xmlns_bosh, - inactivity = tostring(BOSH_DEFAULT_INACTIVITY), polling = tostring(BOSH_DEFAULT_POLLING), requests = tostring(BOSH_DEFAULT_REQUESTS), hold = tostring(session.bosh_hold), maxpause = "120", - sid = sid, authid = sid, ver = '1.6', from = session.host, secure = 'true', ["xmpp:version"] = "1.0", - ["xmlns:xmpp"] = "urn:xmpp:xbosh", ["xmlns:stream"] = "http://etherx.jabber.org/streams" }):add_child(features); + wait = attr.wait, + inactivity = tostring(BOSH_DEFAULT_INACTIVITY), + polling = tostring(BOSH_DEFAULT_POLLING), + requests = tostring(BOSH_DEFAULT_REQUESTS), + hold = tostring(session.bosh_hold), + sid = sid, authid = sid, + ver = '1.6', from = session.host, + secure = 'true', ["xmpp:version"] = "1.0", + ["xmlns:xmpp"] = "urn:xmpp:xbosh", + ["xmlns:stream"] = "http://etherx.jabber.org/streams" + }):add_child(features); request:send{ headers = default_headers, body = tostring(response) }; request.sid = sid; diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 28c31294..9ff6cac2 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -134,7 +134,8 @@ module:hook("presence/bare", function(event) publish_all(user, recipient, origin); else recipients[user][recipient] = hash; - if self or origin.type ~= "c2s" then + local from_bare = origin.type == "c2s" and origin.username.."@"..origin.host; + if self or origin.type ~= "c2s" or (recipients[from_bare] and recipients[from_bare][origin.full_jid]) ~= hash then origin.send( st.stanza("iq", {from=stanza.attr.to, to=stanza.attr.from, id="disco", type="get"}) :query("http://jabber.org/protocol/disco#info") diff --git a/plugins/mod_pubsub.lua b/plugins/mod_pubsub.lua index 0953de28..dc1b1263 100644 --- a/plugins/mod_pubsub.lua +++ b/plugins/mod_pubsub.lua @@ -166,8 +166,40 @@ end module:hook("iq/host/http://jabber.org/protocol/pubsub:pubsub", handle_pubsub_iq); +local disco_info = st.stanza("query", { xmlns = "http://jabber.org/protocol/disco#info" }) + :tag("identity", { category = "pubsub", type = "service" }):up() + :tag("feature", { var = "http://jabber.org/protocol/pubsub" }):up(); + +module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function (event) + event.origin.send(st.reply(event.stanza):add_child(disco_info)); + return true; +end); + +module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function (event) + local ok, ret = service:get_nodes(event.stanza.attr.from); + if not ok then + event.origin.send(pubsub_error_reply(stanza, ret)); + else + local reply = st.reply(event.stanza) + :tag("query", { xmlns = "http://jabber.org/protocol/disco#items" }); + for node, node_obj in pairs(ret) do + reply:tag("item", { jid = module.host, node = node, name = node_obj.config.name }):up(); + end + event.origin.send(reply); + end + return true; +end); + service = pubsub.new({ broadcaster = simple_broadcast }); module.environment.service = service; +function module.save() + return { service = service }; +end + +function module.restore(data) + service = data.service; + module.environment.service = service; +end diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 37b2720b..7f9a27ad 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -14,7 +14,7 @@ local sm_make_authenticated = require "core.sessionmanager".make_authenticated; local s2s_make_authenticated = require "core.s2smanager".make_authenticated; local base64 = require "util.encodings".base64; -local cert_verify_identity = require "util.certverification".verify_identity; +local cert_verify_identity = require "util.x509".verify_identity; local nodeprep = require "util.encodings".stringprep.nodeprep; local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua new file mode 100644 index 00000000..e3eb3c77 --- /dev/null +++ b/plugins/mod_storage_sql.lua @@ -0,0 +1,239 @@ + +--[[ + +DB Tables: + Prosody - key-value, map + | host | user | store | key | subkey | type | value | + ProsodyArchive - list + | host | user | store | key | time | stanzatype | jsonvalue | + +Mapping: + Roster - Prosody + | host | user | "roster" | "contactjid" | item-subkey | type | value | + | host | user | "roster" | NULL | NULL | "json" | roster[false] data | + Account - Prosody + | host | user | "accounts" | "username" | NULL | type | value | + + Offline - ProsodyArchive + | host | user | "offline" | "contactjid" | time | "message" | json|XML | + +]] + +local type = type; +local tostring = tostring; +local tonumber = tonumber; +local pairs = pairs; +local next = next; +local setmetatable = setmetatable; +local json = { stringify = function(s) return require"util.serialization".serialize(s) end, parse = require"util.serialization".deserialze }; + +local connection = ...; +local host,user,store = module.host; + +do -- process options to get a db connection + local DBI = require "DBI"; + + local params = module:get_option("sql") or { driver = "SQLite3", database = "prosody.sqlite" }; + assert(params and params.driver and params.database, "invalid params"); + + prosody.unlock_globals(); + local dbh, err = DBI.Connect( + params.driver, params.database, + params.username, params.password, + params.host, params.port + ); + prosody.lock_globals(); + assert(dbh, err); + + dbh:autocommit(false); -- don't commit automatically + connection = dbh; + + if params.driver == "SQLite3" then -- auto initialize + local stmt = assert(connection:prepare("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='Prosody';")); + local ok = assert(stmt:execute()); + local count = stmt:fetch()[1]; + if count == 0 then + local stmt = assert(connection:prepare("CREATE TABLE Prosody (host TEXT, user TEXT, store TEXT, key TEXT, subkey TEXT, type TEXT, value TEXT);")); + assert(stmt:execute()); + assert(connection:commit()); + module:log("debug", "Initialized new SQLite3 database"); + end + --print("===", json.stringify()) + end +end + +local function serialize(value) + local t = type(value); + if t == "string" or t == "boolean" or t == "number" then + return t, tostring(value); + elseif t == "table" then + local value,err = json.stringify(value); + if value then return "json", value; end + return nil, err; + end + return nil, "Unhandled value type: "..t; +end +local function deserialize(t, value) + if t == "string" then return value; + elseif t == "boolean" then + if value == "true" then return true; + elseif value == "false" then return false; end + elseif t == "number" then return tonumber(value); + elseif value == "json" then + return json.parse(value); + end +end + +local function getsql(sql, ...) + -- do prepared statement stuff + local stmt, err = connection:prepare(sql); + if not stmt then return nil, err; end + -- run query + local ok, err = stmt:execute(host, user, store, ...); + if not ok then return nil, err; end + + return stmt; +end +local function setsql(sql, ...) + local stmt, err = getsql(sql, ...); + if not stmt then return stmt, err; end + return stmt:affected(); +end +local function transact(...) + -- ... +end +local function rollback(...) + connection:rollback(); -- FIXME check for rollback error? + return ...; +end +local function commit(...) + if not connection:commit() then return nil, "SQL commit failed"; end + return ...; +end + +local keyval_store = {}; +keyval_store.__index = keyval_store; +function keyval_store:get(username) + user,store = username,self.store; + local stmt, err = getsql("SELECT * FROM Prosody WHERE host IS ? AND user IS ? AND store IS ? AND subkey IS NULL"); + if not stmt then return nil, err; end + + local haveany; + local result = {}; + for row in stmt:rows(true) do + haveany = true; + local k = row.key; + local v = deserialize(row.type, row.value); + if v then + if k then result[k] = v; elseif type(v) == "table" then + for a,b in pairs(v) do + result[a] = b; + end + end + end + end + return haveany and result or nil; +end +function keyval_store:set(username, data) + user,store = username,self.store; + -- start transaction + local affected, err = setsql("DELETE FROM Prosody WHERE host IS ? AND user IS ? AND store IS ? AND subkey IS NULL"); + + if data and next(data) ~= nil then + local extradata = {}; + for key, value in pairs(data) do + if type(key) == "string" then + local t, value = serialize(value); + if not t then return rollback(t, value); end + local ok, err = setsql("INSERT INTO Prosody (host,user,store,key,type,value) VALUES (?,?,?,?,?,?)", key, t, value); + if not ok then return rollback(ok, err); end + else + extradata[key] = value; + end + end + if next(extradata) ~= nil then + local t, extradata = serialize(extradata); + if not t then return rollback(t, extradata); end + local ok, err = setsql("INSERT INTO Prosody (host,user,store,key,type,value) VALUES (?,?,?,?,?,?)", nil, t, extradata); + if not ok then return rollback(ok, err); end + end + end + return commit(true); +end + +local map_store = {}; +map_store.__index = map_store; +function map_store:get(username, key) + user,store = username,self.store; + local stmt, err = getsql("SELECT * FROM Prosody WHERE host IS ? AND user IS ? AND store IS ? AND key IS ?", key); + if not stmt then return nil, err; end + + local haveany; + local result = {}; + for row in stmt:rows(true) do + haveany = true; + local k = row.subkey; + local v = deserialize(row.type, row.value); + if v then + if k then result[k] = v; elseif type(v) == "table" then + for a,b in pairs(v) do + result[a] = b; + end + end + end + end + return haveany and result or nil; +end +function map_store:set(username, key, data) + user,store = username,self.store; + -- start transaction + local affected, err = setsql("DELETE FROM Prosody WHERE host IS ? AND user IS ? AND store IS ? AND key IS ?", key); + + if data and next(data) ~= nil then + local extradata = {}; + for subkey, value in pairs(data) do + if type(subkey) == "string" then + local t, value = serialize(value); + if not t then return rollback(t, value); end + local ok, err = setsql("INSERT INTO Prosody (host,user,store,key,subkey,type,value) VALUES (?,?,?,?,?,?)", key, subkey, t, value); + if not ok then return rollback(ok, err); end + else + extradata[subkey] = value; + end + end + if next(extradata) ~= nil then + local t, extradata = serialize(extradata); + if not t then return rollback(t, extradata); end + local ok, err = setsql("INSERT INTO Prosody (host,user,store,key,subkey,type,value) VALUES (?,?,?,?,?,?)", key, nil, t, extradata); + if not ok then return rollback(ok, err); end + end + end + return commit(true); +end + +local list_store = {}; +list_store.__index = list_store; +function list_store:scan(username, from, to, jid, typ) + user,store = username,self.store; + + local cols = {"from", "to", "jid", "typ"}; + local vals = { from , to , jid , typ }; + local stmt, err; + local query = "SELECT * FROM ProsodyArchive WHERE host IS ? AND user IS ? AND store IS ?"; + + query = query.." ORDER BY time"; + --local stmt, err = getsql("SELECT * FROM Prosody WHERE host IS ? AND user IS ? AND store IS ? AND key IS ?", key); + + return nil, "not-implemented" +end + +local driver = { name = "sql" }; + +function driver:open(store, typ) + if not typ then -- default key-value store + return setmetatable({ store = store }, keyval_store); + end + return nil, "unsupported-store"; +end + +module:add_item("data-driver", driver); @@ -7,6 +7,8 @@ -- COPYING file in the source package for more information. -- +-- prosody - main executable for Prosody XMPP server + -- Will be modified by configure script if run -- CFG_SOURCEDIR=os.getenv("PROSODY_SRCDIR"); @@ -1,7 +1,7 @@ #!/usr/bin/env lua -- Prosody IM --- Copyright (C) 2008-2009 Matthew Wild --- Copyright (C) 2008-2009 Waqas Hussain +-- 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. @@ -11,18 +11,20 @@ -- Will be modified by configure script if run -- -CFG_SOURCEDIR=nil; +CFG_SOURCEDIR=os.getenv("PROSODY_SRCDIR"); CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR"); -CFG_PLUGINDIR=nil; +CFG_PLUGINDIR=os.getenv("PROSODY_PLUGINDIR"); CFG_DATADIR=os.getenv("PROSODY_DATADIR"); --- -- -- -- -- -- -- ---- -- -- -- -- -- -- -- -- +-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +-- Tell Lua where to find our libraries if CFG_SOURCEDIR then - package.path = CFG_SOURCEDIR.."/?.lua;"..package.path - package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath + package.path = CFG_SOURCEDIR.."/?.lua;"..package.path; + package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath; end +-- Substitute ~ with path to home directory in data path if CFG_DATADIR then if os.getenv("HOME") then CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME")); @@ -40,9 +42,30 @@ local prosody = prosody; config = require "core.configmanager" do - -- TODO: Check for other formats when we add support for them - -- Use lfs? Make a new conf/ dir? - local ok, level, err = config.load((CFG_CONFIGDIR or ".").."/prosody.cfg.lua"); + local filenames = {}; + + local filename; + if arg[1] == "--config" and arg[2] then + table.insert(filenames, arg[2]); + table.remove(arg, 1); table.remove(arg, 1); + if CFG_CONFIGDIR then + table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]); + end + else + for _, format in ipairs(config.parsers()) do + table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg."..format); + end + end + for _,_filename in ipairs(filenames) do + filename = _filename; + local file = io.open(filename); + if file then + file:close(); + CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$"); + break; + end + end + local ok, level, err = config.load(filename); if not ok then print("\n"); print("**************************"); @@ -509,11 +532,8 @@ function commands.restart(arg) return 1; end - local ret = commands.stop(arg); - if ret == 0 then - ret = commands.start(arg); - end - return ret; + commands.stop(arg); + return commands.start(arg); end -- ejabberdctl compatibility diff --git a/util-src/windows.c b/util-src/windows.c index 12bd7ce9..e1e07608 100644 --- a/util-src/windows.c +++ b/util-src/windows.c @@ -43,9 +43,38 @@ static int Lget_nameservers(lua_State *L) { } } +static void lassert(lua_State *L, BOOL test, char* string) { + if (!test) { + luaL_error(L, "%s: %d", string, GetLastError()); + } +} + +static int Lget_consolecolor(lua_State *L) { + HWND console = GetStdHandle(STD_OUTPUT_HANDLE); + WORD color; DWORD read_len; + + CONSOLE_SCREEN_BUFFER_INFO info; + + lassert(L, console != INVALID_HANDLE_VALUE, "GetStdHandle"); + lassert(L, GetConsoleScreenBufferInfo(console, &info), "GetConsoleScreenBufferInfo"); + lassert(L, ReadConsoleOutputAttribute(console, &color, sizeof(WORD), info.dwCursorPosition, &read_len), "ReadConsoleOutputAttribute"); + + lua_pushnumber(L, color); + return 1; +} +static int Lset_consolecolor(lua_State *L) { + int color = luaL_checkint(L, 1); + HWND console = GetStdHandle(STD_OUTPUT_HANDLE); + lassert(L, console != INVALID_HANDLE_VALUE, "GetStdHandle"); + lassert(L, SetConsoleTextAttribute(console, color), "SetConsoleTextAttribute"); + return 0; +} + static const luaL_Reg Reg[] = { { "get_nameservers", Lget_nameservers }, + { "get_consolecolor", Lget_consolecolor }, + { "set_consolecolor", Lset_consolecolor }, { NULL, NULL } }; diff --git a/util/pubsub.lua b/util/pubsub.lua index 02e845e1..811f4a15 100644 --- a/util/pubsub.lua +++ b/util/pubsub.lua @@ -77,4 +77,8 @@ function service:get(node, actor, id) end end +function service:get_nodes(actor) + return true, self.nodes; +end + return _M; diff --git a/util/serialization.lua b/util/serialization.lua index bad2fe43..e193b64f 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -15,6 +15,10 @@ local error = error; local pairs = pairs; local next = next; +local loadstring = loadstring; +local setfenv = setfenv; +local pcall = pcall; + local debug_traceback = debug.traceback; local log = require "util.logger".init("serialization"); module "serialization" @@ -24,14 +28,20 @@ local indent = function(i) end local function basicSerialize (o) if type(o) == "number" or type(o) == "boolean" then - return tostring(o); + -- no need to check for NaN, as that's not a valid table index + if o == 1/0 then return "(1/0)"; + elseif o == -1/0 then return "(-1/0)"; + else return tostring(o); end else -- assume it is a string -- FIXME make sure it's a string. throw an error otherwise. return (("%q"):format(tostring(o)):gsub("\\\n", "\\n")); end end local function _simplesave(o, ind, t, func) if type(o) == "number" then - func(t, tostring(o)); + if o ~= o then func(t, "(0/0)"); + elseif o == 1/0 then func(t, "(1/0)"); + elseif o == -1/0 then func(t, "(-1/0)"); + else func(t, tostring(o)); end elseif type(o) == "string" then func(t, (("%q"):format(o):gsub("\\\n", "\\n"))); elseif type(o) == "table" then @@ -72,7 +82,14 @@ function serialize(o) end function deserialize(str) - error("Not implemented"); + if type(str) ~= "string" then return nil; end + str = "return "..str; + local f, err = loadstring(str, "@data"); + if not f then return nil, err; end + setfenv(f, {}); + local success, ret = pcall(f); + if not success then return nil, ret; end + return ret; end return _M; diff --git a/util/stanza.lua b/util/stanza.lua index 307a858a..16d558af 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -254,7 +254,7 @@ function stanza_mt.get_error(stanza) end end end - return type, condition or "undefined-condition", text or ""; + return type, condition or "undefined-condition", text; end function stanza_mt.__add(s1, s2) diff --git a/util/termcolours.lua b/util/termcolours.lua index 4e267bee..df204688 100644 --- a/util/termcolours.lua +++ b/util/termcolours.lua @@ -10,6 +10,14 @@ local t_concat, t_insert = table.concat, table.insert; local char, format = string.char, string.format; local ipairs = ipairs; +local io_write = io.write; + +local windows; +if os.getenv("WINDIR") then + windows = require "util.windows"; +end +local orig_color = windows and windows.get_consolecolor and windows.get_consolecolor(); + module "termcolours" local stylemap = { @@ -19,6 +27,13 @@ local stylemap = { bold = 1, dark = 2, underline = 4, underlined = 4, normal = 0; } +local winstylemap = { + ["0"] = orig_color, -- reset + ["1"] = 7+8, -- bold + ["1;33"] = 2+4+8, -- bold yellow + ["1;31"] = 4+8 -- bold red +} + local fmt_string = char(0x1B).."[%sm%s"..char(0x1B).."[0m"; function getstring(style, text) if style then @@ -39,4 +54,26 @@ function getstyle(...) return t_concat(result, ";"); end +local last = "0"; +function setstyle(style) + style = style or "0"; + if style ~= last then + io_write("\27["..style.."m"); + last = style; + end +end + +if windows then + function setstyle(style) + style = style or "0"; + if style ~= last then + windows.set_consolecolor(winstylemap[style] or orig_color); + last = style; + end + end + if not orig_color then + function setstyle(style) end + end +end + return _M; diff --git a/util/certverification.lua b/util/x509.lua index d323f4b4..11f231a0 100644 --- a/util/certverification.lua +++ b/util/x509.lua @@ -20,9 +20,9 @@ local nameprep = require "util.encodings".stringprep.nameprep; local idna_to_ascii = require "util.encodings".idna.to_ascii; -local log = require "util.logger".init("certverification"); +local log = require "util.logger".init("x509"); -module "certverification" +module "x509" local oid_commonname = "2.5.4.3"; -- [LDAP] 2.3 local oid_subjectaltname = "2.5.29.17"; -- [PKIX] 4.2.1.6 |