From afd41edcdeddffd842332973fdcd386e326a2700 Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Thu, 4 Apr 2013 23:36:30 +0000 Subject: portmanager: add logic to allow specification of service default values for ssl config and / or overrides. --- core/portmanager.lua | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 68c7617a..1b3740cf 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -70,6 +70,16 @@ prosody.events.add_handler("item-removed/net-provider", function (event) unregister_service(item.name, item); end); +local function duplicate_ssl_config(ssl_config) + local ssl_config = type(ssl_config) == "table" and ssl_config or {}; + + local _config = {}; + for k, v in pairs(ssl_config) do + _config[k] = v; + end + return _config; +end + --- Public API function activate(service_name) @@ -114,9 +124,24 @@ function activate(service_name) local err; -- Create SSL context for this service/port if service_info.encryption == "ssl" then - local ssl_config = config.get("*", config_prefix.."ssl"); - ssl, err = certmanager.create_context(service_info.name.." port "..port, "server", ssl_config and (ssl_config[port_number] - or (ssl_config.certificate and ssl_config))); + local ssl_config = duplicate_ssl_config((config.get("*", config_prefix.."ssl") and config.get("*", config_prefix.."ssl")[interface]) + or (config.get("*", config_prefix.."ssl") and config.get("*", config_prefix.."ssl")[port]) + or config.get("*", config_prefix.."ssl") + or (config.get("*", "ssl") and config.get("*", "ssl")[interface]) + or (config.get("*", "ssl") and config.get("*", "ssl")[port]) + or config.get("*", "ssl")); + -- add default entries for, or override ssl configuration + if ssl_config and service_info.ssl_config then + for key, value in pairs(service_info.ssl_config) do + if not service_info.ssl_config_override and not ssl_config[key] then + ssl_config[key] = value; + elseif service_info.ssl_config_override then + ssl_config[key] = value; + end + end + end + + ssl, err = certmanager.create_context(service_info.name.." port "..port, "server", ssl_config); if not ssl then log("error", "Error binding encrypted port for %s: %s", service_info.name, error_to_friendly_message(service_name, port_number, err) or "unknown error"); end -- cgit v1.2.3 From 993348f0f768887090474010f36b212701b5acf2 Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Thu, 4 Apr 2013 23:41:36 +0000 Subject: mod_http: disable ssl peer verification by default. --- plugins/mod_http.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 2fa87421..0689634e 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -139,6 +139,7 @@ module:provides("net", { listener = server.listener; default_port = 5281; encryption = "ssl"; + ssl_config = { verify = "none" }; multiplex = { pattern = "^[A-Z]"; }; -- cgit v1.2.3 From 4dbba9413895c16146d754f6115532837ab0fc04 Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 5 Apr 2013 04:49:32 +0000 Subject: prosody: load rostermanager after usermanager during environment initialization. --- prosody | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prosody b/prosody index 875140de..9a88eac0 100755 --- a/prosody +++ b/prosody @@ -290,12 +290,12 @@ function load_secondary_libraries() --- Load and initialise core modules require "util.import" require "util.xmppstream" - require "core.rostermanager" require "core.stanza_router" require "core.hostmanager" require "core.portmanager" require "core.modulemanager" require "core.usermanager" + require "core.rostermanager" require "core.sessionmanager" package.loaded['core.componentmanager'] = setmetatable({},{__index=function() log("warn", "componentmanager is deprecated: %s", debug.traceback():match("\n[^\n]*\n[ \t]*([^\n]*)")); -- cgit v1.2.3 From 8d52e3bb0a372bcd37712b4b702287d900e5b029 Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 5 Apr 2013 04:52:11 +0000 Subject: rostermanager: do not save rosters for unexistant users. --- core/rostermanager.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/rostermanager.lua b/core/rostermanager.lua index f14bb435..5e06e3f7 100644 --- a/core/rostermanager.lua +++ b/core/rostermanager.lua @@ -18,6 +18,7 @@ local hosts = hosts; local bare_sessions = bare_sessions; local datamanager = require "util.datamanager" +local um_user_exists = require "core.usermanager".user_exists; local st = require "util.stanza"; module "rostermanager" @@ -105,6 +106,11 @@ function load_roster(username, host) end function save_roster(username, host, roster) + if not um_user_exists(username, host) then + log("debug", "not saving roster for %s@%s: the user doesn't exist", username, host); + return nil; + end + log("debug", "save_roster: saving roster for %s@%s", username, host); if not roster then roster = hosts[host] and hosts[host].sessions[username] and hosts[host].sessions[username].roster; -- cgit v1.2.3 From 1a1562f13793ede08ae068ed0094d006ccbc30b6 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 5 Apr 2013 13:06:26 +0100 Subject: portmanager: import pairs() (thanks Maranda) --- core/portmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/portmanager.lua b/core/portmanager.lua index 1b3740cf..1b767a09 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -9,7 +9,7 @@ local set = require "util.set"; local table = table; local setmetatable, rawset, rawget = setmetatable, rawset, rawget; -local type, tonumber, tostring, ipairs = type, tonumber, tostring, ipairs; +local type, tonumber, tostring, ipairs, pairs = type, tonumber, tostring, ipairs, pairs; local prosody = prosody; local fire_event = prosody.events.fire_event; -- cgit v1.2.3 From c816af85d801e02f508ece2cf5e088ba5c40c34a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 5 Apr 2013 19:13:46 +0100 Subject: moduleapi: assert() that prosody.core_post_stanza is not nil --- core/moduleapi.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index de900bf0..fa20c3cd 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -21,7 +21,10 @@ local tonumber, tostring = tonumber, tostring; local prosody = prosody; local hosts = prosody.hosts; -local core_post_stanza = prosody.core_post_stanza; + +-- FIXME: This assert() is to try and catch an obscure bug (2013-04-05) +local core_post_stanza = assert(prosody.core_post_stanza, + "prosody.core_post_stanza is nil, please report this as a bug"); -- Registry of shared module data local shared_data = setmetatable({}, { __mode = "v" }); -- cgit v1.2.3 From 3f6a48f7d0dcce5ccfe7d41dbc7cb8ac7da065bd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 5 Apr 2013 19:59:48 +0100 Subject: util.stanza: Use correct index when replacing the tag in .tags (thanks daurnimator) --- util/stanza.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/stanza.lua b/util/stanza.lua index 59c88c4e..7c214210 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -166,7 +166,7 @@ function stanza_mt:maptags(callback) curr_tag = curr_tag - 1; else self[i] = ret; - tags[i] = ret; + tags[curr_tag] = ret; end curr_tag = curr_tag + 1; end -- cgit v1.2.3 From 15c88d0896ba618781629162901b175929d259c8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 6 Apr 2013 12:20:31 +0100 Subject: util.json: Convert \uXXXX to UTF-8 when decoding --- util/json.lua | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/util/json.lua b/util/json.lua index abb87eab..ff7351a7 100644 --- a/util/json.lua +++ b/util/json.lua @@ -1,3 +1,12 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- +-- utf8char copyright (C) 2007 Rici Lake +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- local type = type; local t_insert, t_concat, t_remove, t_sort = table.insert, table.concat, table.remove, table.sort; @@ -29,6 +38,32 @@ for i=0,31 do if not escapes[ch] then escapes[ch] = ("\\u%.4X"):format(i); end end +local function utf8char(i) + if i >= 0 then + i = i - i%1 + if i < 128 then + return s_char(i) + else + local c1 = i % 64 + i = (i - c1) / 64 + if i < 32 then + return s_char(0xC0+i, 0x80+c1) + else + local c2 = i % 64 + i = (i - c2) / 64 + if i < 16 and (i ~= 13 or c2 < 32) then + return s_char(0xE0+i, 0x80+c2, 0x80+c1) + elseif i >= 16 and i < 0x110 then + local c3 = i % 64 + i = (i - c3) / 64 + return s_char(0xF0+i, 0x80+c3, 0x80+c2, 0x80+c1) + end + end + end + end +end + + local valid_types = { number = true, string = true, @@ -249,7 +284,7 @@ function json.decode(json) if not ch:match("[0-9a-fA-F]") then error("invalid unicode escape sequence in string"); end seq = seq..ch; end - s = s..s.char(tonumber(seq, 16)); -- FIXME do proper utf-8 + s = s..utf8char(tonumber(seq, 16)); next(); else error("invalid escape sequence in string"); end end -- cgit v1.2.3 From 5fddb95ee309376e86e34774be78c7f10283e07c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 6 Apr 2013 20:07:08 +0100 Subject: prosodyctl: Define prosody.core_post_stanza as an empty function --- prosodyctl | 1 + 1 file changed, 1 insertion(+) diff --git a/prosodyctl b/prosodyctl index a8cf0e69..71b99f9e 100755 --- a/prosodyctl +++ b/prosodyctl @@ -51,6 +51,7 @@ local prosody = { lock_globals = function () end; unlock_globals = function () end; installed = CFG_SOURCEDIR ~= nil; + core_post_stanza = function () end; -- TODO: mod_router! }; _G.prosody = prosody; -- cgit v1.2.3 From 33e438dd33a6933f015b97cd05ae63bcb13755b2 Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Sun, 7 Apr 2013 12:23:29 +0000 Subject: net.http.server: add API to allow firing events directly on the server. --- net/http/server.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/http/server.lua b/net/http/server.lua index 20c2da3e..830579c9 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -284,6 +284,9 @@ end function _M.set_default_host(host) default_host = host; end +function _M.fire_event(event, ...) + return events.fire_event(event, ...); +end _M.listener = listener; _M.codes = codes; -- cgit v1.2.3 From 98d807f5b6963c688656cc598bca6ea396e2029f Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Sun, 7 Apr 2013 16:56:49 +0000 Subject: util.datamanager: expose path decode and encode functions. --- util/datamanager.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/datamanager.lua b/util/datamanager.lua index 383e738f..9f29458c 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -354,4 +354,6 @@ function purge(username, host) return #errs == 0, t_concat(errs, ", "); end +_M.path_decode = decode; +_M.path_encode = encode; return _M; -- cgit v1.2.3 From aae8df412a2bae509d24b4af42f845003f6b9935 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 7 Apr 2013 20:28:12 +0100 Subject: util.datamanager: Clear the cache of created directories on storage failure, and retry --- util/datamanager.lua | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/util/datamanager.lua b/util/datamanager.lua index 9f29458c..4a4d62b3 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -187,17 +187,25 @@ function store(username, host, datastore, data) -- save the datastore local d = "return " .. serialize(data) .. ";\n"; - local ok, msg = atomic_store(getpath(username, host, datastore, nil, true), d); - if not ok then - log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil"); - return nil, "Error saving to storage"; - end - if next(data) == nil then -- try to delete empty datastore - log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil"); - os_remove(getpath(username, host, datastore)); - end - -- we write data even when we are deleting because lua doesn't have a - -- platform independent way of checking for non-exisitng files + local mkdir_cache_cleared; + repeat + local ok, msg = atomic_store(getpath(username, host, datastore, nil, true), d); + if not ok then + if not mkdir_cache_cleared then -- We may need to recreate a removed directory + _mkdir = {}; + mkdir_cache_cleared = true; + else + log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil"); + return nil, "Error saving to storage"; + end + end + if next(data) == nil then -- try to delete empty datastore + log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil"); + os_remove(getpath(username, host, datastore)); + end + -- we write data even when we are deleting because lua doesn't have a + -- platform independent way of checking for non-exisitng files + until ok; return true; end -- cgit v1.2.3 From 746de7c5337281b98f05a287f7f3bd2024eb8e46 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Apr 2013 15:04:55 +0100 Subject: mod_pubsub: Don't attempt to handle iq stanzas with no action element --- plugins/mod_pubsub.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_pubsub.lua b/plugins/mod_pubsub.lua index 5ff3a029..8f078f54 100644 --- a/plugins/mod_pubsub.lua +++ b/plugins/mod_pubsub.lua @@ -22,6 +22,7 @@ function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; local pubsub = stanza.tags[1]; local action = pubsub.tags[1]; + if not action then return; end local handler = handlers[stanza.attr.type.."_"..action.name]; if handler then handler(origin, stanza, action); -- cgit v1.2.3 From b132f180a986056588208f0604f293bef2c41b2a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Apr 2013 15:32:24 +0100 Subject: mod_pubsub: Send bad-request when no action specified (thanks Maranda) --- plugins/mod_pubsub.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/mod_pubsub.lua b/plugins/mod_pubsub.lua index 8f078f54..7bd33102 100644 --- a/plugins/mod_pubsub.lua +++ b/plugins/mod_pubsub.lua @@ -22,7 +22,9 @@ function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; local pubsub = stanza.tags[1]; local action = pubsub.tags[1]; - if not action then return; end + if not action then + return origin.send(st.error_reply(stanza, "cancel", "bad-request")); + end local handler = handlers[stanza.attr.type.."_"..action.name]; if handler then handler(origin, stanza, action); -- cgit v1.2.3 From 6d91ae532b70bb72cdef4fdc718235da2898be80 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Apr 2013 15:53:18 +0100 Subject: sessionmanager, s2smanager: Remove open_session tracing --- core/s2smanager.lua | 9 +-------- core/sessionmanager.lua | 11 +---------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 5777cb8e..e4de498a 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -24,15 +24,8 @@ local fire_event = prosody.events.fire_event; module "s2smanager" -local open_sessions = 0; - function new_incoming(conn) local session = { conn = conn, type = "s2sin_unauthed", direction = "incoming", hosts = {} }; - if true then - session.trace = newproxy(true); - getmetatable(session.trace).__gc = function () open_sessions = open_sessions - 1; end; - end - open_sessions = open_sessions + 1; session.log = logger_init("s2sin"..tostring(session):match("[a-f0-9]+$")); incoming_s2s[session] = true; return session; @@ -62,7 +55,7 @@ local resting_session = { -- Resting, not dead function retire_session(session, reason) local log = session.log or log; for k in pairs(session) do - if k ~= "trace" and k ~= "log" and k ~= "id" and k ~= "conn" then + if k ~= "log" and k ~= "id" and k ~= "conn" then session[k] = nil; end end diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index e721835d..d178fb2d 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -29,17 +29,8 @@ local getmetatable = getmetatable; module "sessionmanager" -local open_sessions = 0; - function new_session(conn) local session = { conn = conn, type = "c2s_unauthed", conntime = gettime() }; - if true then - session.trace = newproxy(true); - getmetatable(session.trace).__gc = function () open_sessions = open_sessions - 1; end; - end - open_sessions = open_sessions + 1; - log("debug", "open sessions now: %d", open_sessions); - local filter = initialize_filters(session); local w = conn.write; session.send = function (t) @@ -72,7 +63,7 @@ local resting_session = { -- Resting, not dead function retire_session(session) local log = session.log or log; for k in pairs(session) do - if k ~= "trace" and k ~= "log" and k ~= "id" then + if k ~= "log" and k ~= "id" then session[k] = nil; end end -- cgit v1.2.3 From ef120fb92c575cb9b0d138ce5ea751b484163823 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Apr 2013 16:40:27 +0100 Subject: net.http: Throw error when connecting to a http:// URL without LuaSec available --- net/http.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/http.lua b/net/http.lua index a1e4e523..ec55af92 100644 --- a/net/http.lua +++ b/net/http.lua @@ -11,6 +11,8 @@ local b64 = require "util.encodings".base64.encode; local url = require "socket.url" local httpstream_new = require "util.httpstream".new; +local ssl_available = pcall(require, "ssl"); + local server = require "net.server" local t_insert, t_concat = table.insert, table.concat; @@ -177,6 +179,9 @@ function request(u, ex, callback) req.method, req.headers, req.body = method, headers, body; local using_https = req.scheme == "https"; + if using_https and not ssl_available then + error("SSL not available, unable to contact https URL"); + end local port = tonumber(req.port) or (using_https and 443 or 80); -- Connect the socket, and wrap it with net.server -- cgit v1.2.3 From cacb08e2a8100fee1c256a48be20eb27e247edc5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Apr 2013 16:56:40 +0100 Subject: util.pposix: syslog(): Support an optional source parameter (producing messages of the form ': ' --- util-src/pposix.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/util-src/pposix.c b/util-src/pposix.c index 99a308cf..c8c25ba9 100644 --- a/util-src/pposix.c +++ b/util-src/pposix.c @@ -204,12 +204,13 @@ int level_constants[] = { }; int lc_syslog_log(lua_State* L) { - int level = luaL_checkoption(L, 1, "notice", level_strings); - level = level_constants[level]; + int level = level_constants[luaL_checkoption(L, 1, "notice", level_strings)]; - luaL_checkstring(L, 2); + if(lua_gettop(L) == 3) + syslog(level, "%s: %s", luaL_checkstring(L, 2), luaL_checkstring(L, 3)); + else + syslog(level, "%s", lua_tostring(L, 2)); - syslog(level, "%s", lua_tostring(L, 2)); return 0; } -- cgit v1.2.3 From 64d0e35dd37bd146cf920a6cbd8db899e674f532 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Apr 2013 16:57:05 +0100 Subject: mod_posix: Pass logger name to syslog, so that sources now get logged --- plugins/mod_posix.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_posix.lua b/plugins/mod_posix.lua index e871e5cf..96a05d73 100644 --- a/plugins/mod_posix.lua +++ b/plugins/mod_posix.lua @@ -118,9 +118,9 @@ function syslog_sink_maker(config) local syslog, format = pposix.syslog_log, string.format; return function (name, level, message, ...) if ... then - syslog(level, format(message, ...)); + syslog(level, name, format(message, ...)); else - syslog(level, message); + syslog(level, name, message); end end; end -- cgit v1.2.3 From 3434e9d0f30bfb37f7a849bb6f48566e9c0e872d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Apr 2013 16:57:59 +0100 Subject: mod_posix, util.pposix: Bump version for API change --- plugins/mod_posix.lua | 2 +- util-src/pposix.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_posix.lua b/plugins/mod_posix.lua index 96a05d73..f1957d1d 100644 --- a/plugins/mod_posix.lua +++ b/plugins/mod_posix.lua @@ -7,7 +7,7 @@ -- -local want_pposix_version = "0.3.5"; +local want_pposix_version = "0.3.6"; local pposix = assert(require "util.pposix"); if pposix._VERSION ~= want_pposix_version then module:log("warn", "Unknown version (%s) of binary pposix module, expected %s", tostring(pposix._VERSION), want_pposix_version); end diff --git a/util-src/pposix.c b/util-src/pposix.c index c8c25ba9..f5cc8270 100644 --- a/util-src/pposix.c +++ b/util-src/pposix.c @@ -13,7 +13,7 @@ * POSIX support functions for Lua */ -#define MODULE_VERSION "0.3.5" +#define MODULE_VERSION "0.3.6" #include #include -- cgit v1.2.3 From f70962f30c9429df130271897211b8067caa180d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Apr 2013 17:21:16 +0100 Subject: mod_posix: Improve error message for a pposix version mismatch --- plugins/mod_posix.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/mod_posix.lua b/plugins/mod_posix.lua index f1957d1d..28fd7f38 100644 --- a/plugins/mod_posix.lua +++ b/plugins/mod_posix.lua @@ -10,7 +10,9 @@ local want_pposix_version = "0.3.6"; local pposix = assert(require "util.pposix"); -if pposix._VERSION ~= want_pposix_version then module:log("warn", "Unknown version (%s) of binary pposix module, expected %s", tostring(pposix._VERSION), want_pposix_version); end +if pposix._VERSION ~= want_pposix_version then + module:log("warn", "Unknown version (%s) of binary pposix module, expected %s. Perhaps you need to recompile?", tostring(pposix._VERSION), want_pposix_version); +end local signal = select(2, pcall(require, "util.signal")); if type(signal) == "string" then -- cgit v1.2.3 From 95bc304291d365a1e1092607ffd7f19b1441eb4a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 8 Apr 2013 22:42:38 +0200 Subject: mod_s2s: Adjust priority of route/remote hooks to negative values (like most other internal hooks) --- plugins/mod_s2s/mod_s2s.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index 1547345d..eb10cd35 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -140,8 +140,8 @@ function module.add_host(module) module:log("warn", "The 'disallow_s2s' config option is deprecated, please see http://prosody.im/doc/s2s#disabling"); return nil, "This host has disallow_s2s set"; end - module:hook("route/remote", route_to_existing_session, 200); - module:hook("route/remote", route_to_new_session, 100); + module:hook("route/remote", route_to_existing_session, -1); + module:hook("route/remote", route_to_new_session, -10); module:hook("s2s-authenticated", make_authenticated, -1); end -- cgit v1.2.3 From bbf4d75f418176c8deb9f6bf75258160e0aaf229 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Apr 2013 17:32:59 +0100 Subject: net.http, util.http: Move definitions of urlencode/decode and formencode/decode to util.http (possible to use them without unnecessary network-related dependencies) --- net/http.lua | 48 +++++++----------------------------------------- util/http.lua | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/net/http.lua b/net/http.lua index ec55af92..516afe58 100644 --- a/net/http.lua +++ b/net/http.lua @@ -10,6 +10,7 @@ local socket = require "socket" local b64 = require "util.encodings".base64.encode; local url = require "socket.url" local httpstream_new = require "util.httpstream".new; +local util_http = require "util.http"; local ssl_available = pcall(require, "ssl"); @@ -70,46 +71,7 @@ function listener.ondisconnect(conn, err) requests[conn] = nil; end -function urlencode(s) return s and (s:gsub("[^a-zA-Z0-9.~_-]", 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 - -local function _formencodepart(s) - return s and (s:gsub("%W", function (c) - if c ~= " " then - return format("%%%02x", c:byte()); - else - return "+"; - end - end)); -end - -function formencode(form) - local result = {}; - if form[1] then -- Array of ordered { name, value } - for _, field in ipairs(form) do - t_insert(result, _formencodepart(field.name).."=".._formencodepart(field.value)); - end - else -- Unordered map of name -> value - for name, value in pairs(form) do - t_insert(result, _formencodepart(name).."=".._formencodepart(value)); - end - end - return t_concat(result, "&"); -end - -function formdecode(s) - if not s:match("=") then return urldecode(s); end - local r = {}; - for k, v in s:gmatch("([^=&]*)=([^&]*)") do - k, v = k:gsub("%+", "%%20"), v:gsub("%+", "%%20"); - k, v = urldecode(k), urldecode(v); - t_insert(r, { name = k, value = v }); - r[k] = v; - end - return r; -end - -local function request_reader(request, data, startpos) +local function request_reader(request, data) if not request.parser then if not data then return; end local function success_cb(r) @@ -216,6 +178,10 @@ function destroy_request(request) end end -_M.urlencode = urlencode; +local urlencode, urldecode = util_http.urlencode, util_http.urldecode; +local formencode, formdecode = util_http.formencode, util_http.formdecode; + +_M.urlencode, _M.urldecode = urlencode, urldecode; +_M.formencode, _M.formdecode = formencode, formdecode; return _M; diff --git a/util/http.lua b/util/http.lua index 5b49d1d0..5dd636d9 100644 --- a/util/http.lua +++ b/util/http.lua @@ -7,9 +7,54 @@ local http = {}; +function http.urlencode(s) + return s and (s:gsub("[^a-zA-Z0-9.~_-]", function (c) return format("%%%02x", c:byte()); end)); +end +function http.urldecode(s) + return s and (s:gsub("%%(%x%x)", function (c) return char(tonumber(c,16)); end)); +end + +local function _formencodepart(s) + return s and (s:gsub("%W", function (c) + if c ~= " " then + return format("%%%02x", c:byte()); + else + return "+"; + end + end)); +end + +function http.formencode(form) + local result = {}; + if form[1] then -- Array of ordered { name, value } + for _, field in ipairs(form) do + t_insert(result, _formencodepart(field.name).."=".._formencodepart(field.value)); + end + else -- Unordered map of name -> value + for name, value in pairs(form) do + t_insert(result, _formencodepart(name).."=".._formencodepart(value)); + end + end + return t_concat(result, "&"); +end + +function http.formdecode(s) + if not s:match("=") then return urldecode(s); end + local r = {}; + for k, v in s:gmatch("([^=&]*)=([^&]*)") do + k, v = k:gsub("%+", "%%20"), v:gsub("%+", "%%20"); + k, v = urldecode(k), urldecode(v); + t_insert(r, { name = k, value = v }); + r[k] = v; + end + return r; +end + function http.contains_token(field, token) field = ","..field:gsub("[ \t]", ""):lower()..","; return field:find(","..token:lower()..",", 1, true) ~= nil; end + + return http; -- cgit v1.2.3 From 93f32dcdfd995abe2d80a73d5c2e03b5893a4344 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Apr 2013 17:35:39 +0100 Subject: sessionmanager, s2smanager: Remove unused imports --- core/s2smanager.lua | 4 ++-- core/sessionmanager.lua | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index e4de498a..06d3f2c9 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -9,8 +9,8 @@ local hosts = prosody.hosts; -local tostring, pairs, getmetatable, newproxy, setmetatable - = tostring, pairs, getmetatable, newproxy, setmetatable; +local tostring, pairs, setmetatable + = tostring, pairs, setmetatable; local logger_init = require "util.logger".init; diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index d178fb2d..98ead07f 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -24,9 +24,6 @@ local uuid_generate = require "util.uuid".generate; local initialize_filters = require "util.filters".initialize; local gettime = require "socket".gettime; -local newproxy = newproxy; -local getmetatable = getmetatable; - module "sessionmanager" function new_session(conn) -- cgit v1.2.3 From bd8884707ad0417875b28ab02beeb75ac14c7ed8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Apr 2013 17:37:37 +0100 Subject: net.http.parser: Depend on util.http instead of net.http for urlencode --- net/http/parser.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/parser.lua b/net/http/parser.lua index 2545b5ac..45a8b168 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -2,7 +2,7 @@ local tonumber = tonumber; local assert = assert; local url_parse = require "socket.url".parse; -local urldecode = require "net.http".urldecode; +local urldecode = require "util.http".urldecode; local function preprocess_path(path) path = urldecode((path:gsub("//+", "/"))); -- cgit v1.2.3 From 68339a4c86588ea52693d38a737e50501ea489c8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Apr 2013 17:39:10 +0100 Subject: net.http.parser: Break when no more usable data in buffer (client part of e5ec60dfb202) --- net/http/parser.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/http/parser.lua b/net/http/parser.lua index 45a8b168..73a8fb6a 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -136,6 +136,8 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) elseif len and #buf >= len then packet.body, buf = buf:sub(1, len), buf:sub(len + 1); state = nil; success_cb(packet); + else + break; end elseif #buf >= len then packet.body, buf = buf:sub(1, len), buf:sub(len + 1); -- cgit v1.2.3 From 358b0b204c09ad931cf4ccb1dce642094074f889 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Apr 2013 19:58:53 +0100 Subject: net.http.parser: Convert status_code to a number before trying to compare it to numbers --- net/http/parser.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/http/parser.lua b/net/http/parser.lua index 73a8fb6a..684d62fe 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -65,6 +65,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) first_line = line; if client then httpversion, status_code, reason_phrase = line:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$"); + status_code = tonumber(status_code); if not status_code then error = true; return error_cb("invalid-status-line"); end have_body = not ( (options_cb and options_cb().method == "HEAD") -- cgit v1.2.3 From 4c333cb417474a8d787b50129c1e7d7f765f8de1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Apr 2013 20:01:03 +0100 Subject: net.http.parser: Fix chunked encoding response parsing, and make it more robust --- net/http/parser.lua | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/net/http/parser.lua b/net/http/parser.lua index 684d62fe..34742d2b 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -1,4 +1,3 @@ - local tonumber = tonumber; local assert = assert; local url_parse = require "socket.url".parse; @@ -29,7 +28,7 @@ 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 chunked, chunk_size, chunk_start; local state = nil; local packet; local len; @@ -71,7 +70,6 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) ( (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 @@ -79,6 +77,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) end end if not first_line then error = true; return error_cb("invalid-status-line"); end + chunked = have_body and headers["transfer-encoding"] == "chunked"; len = tonumber(headers["content-length"]); -- TODO check for invalid len if client then -- FIXME handle '100 Continue' response (by skipping it) @@ -121,19 +120,25 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) 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); + if not buf:find("\r\n", nil, true) then + return; + end -- not enough data + if not chunk_size then + chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()"); + chunk_size = chunk_size and tonumber(chunk_size, 16); + if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end + end + if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then + state, chunk_size = nil, nil; + buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped + success_cb(packet); + elseif #buf - chunk_start + 2 >= chunk_size then -- we have a chunk + packet.body = packet.body..buf:sub(chunk_start, chunk_start + chunk_size); + buf = buf:sub(chunk_start + chunk_size + 2); + chunk_size, chunk_start = nil, nil; + else -- Partial chunk remaining + break; 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); -- cgit v1.2.3 From 666ffa216867ac5db04c24280fa37ae843d73fff Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Apr 2013 20:24:37 +0100 Subject: net.http: Switch from util.httpstream to net.http.parser, introduces small but backwards-incompatible API changes - see http://prosody.im/doc/developers/http --- net/http.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http.lua b/net/http.lua index 516afe58..b3bd5a67 100644 --- a/net/http.lua +++ b/net/http.lua @@ -9,7 +9,7 @@ local socket = require "socket" local b64 = require "util.encodings".base64.encode; local url = require "socket.url" -local httpstream_new = require "util.httpstream".new; +local httpstream_new = require "net.http.parser".new; local util_http = require "util.http"; local ssl_available = pcall(require, "ssl"); -- cgit v1.2.3 From b29f3740f6979ec337ef25a1982dafd6a3c64a38 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Apr 2013 21:55:20 +0100 Subject: util.httpstream: Unused, remove --- util/httpstream.lua | 134 ---------------------------------------------------- 1 file changed, 134 deletions(-) delete mode 100644 util/httpstream.lua diff --git a/util/httpstream.lua b/util/httpstream.lua deleted file mode 100644 index 190b3ed6..00000000 --- a/util/httpstream.lua +++ /dev/null @@ -1,134 +0,0 @@ - -local coroutine = coroutine; -local tonumber = tonumber; - -local deadroutine = coroutine.create(function() end); -coroutine.resume(deadroutine); - -module("httpstream") - -local function parser(success_cb, parser_type, options_cb) - local data = coroutine.yield(); - local function readline() - local pos = data:find("\r\n", nil, true); - while not pos do - data = data..coroutine.yield(); - pos = data:find("\r\n", nil, true); - end - local r = data:sub(1, pos-1); - data = data:sub(pos+2); - return r; - end - local function readlength(n) - while #data < n do - data = data..coroutine.yield(); - end - local r = data:sub(1, n); - data = data:sub(n + 1); - return r; - end - local function readheaders() - local headers = {}; -- read headers - while true do - local line = readline(); - if line == "" then break; end -- headers done - local key, val = line:match("^([^%s:]+): *(.*)$"); - if not key then coroutine.yield("invalid-header-line"); end -- TODO handle multi-line and invalid headers - key = key:lower(); - headers[key] = headers[key] and headers[key]..","..val or val; - end - return headers; - end - - if not parser_type or parser_type == "server" then - while true do - -- read status line - local status_line = readline(); - local method, path, httpversion = status_line:match("^(%S+)%s+(%S+)%s+HTTP/(%S+)$"); - if not method then coroutine.yield("invalid-status-line"); end - path = path:gsub("^//+", "/"); -- TODO parse url more - local headers = readheaders(); - - -- read body - local len = tonumber(headers["content-length"]); - len = len or 0; -- TODO check for invalid len - local body = readlength(len); - - success_cb({ - method = method; - path = path; - httpversion = httpversion; - headers = headers; - body = body; - }); - end - elseif parser_type == "client" then - while true do - -- read status line - local status_line = readline(); - local httpversion, status_code, reason_phrase = status_line:match("^HTTP/(%S+)%s+(%d%d%d)%s+(.*)$"); - status_code = tonumber(status_code); - if not status_code then coroutine.yield("invalid-status-line"); end - local headers = readheaders(); - - -- read body - local 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) ); - - local body; - if have_body then - local len = tonumber(headers["content-length"]); - if headers["transfer-encoding"] == "chunked" then - body = ""; - while true do - local chunk_size = readline():match("^%x+"); - if not chunk_size then coroutine.yield("invalid-chunk-size"); end - chunk_size = tonumber(chunk_size, 16) - if chunk_size == 0 then break; end - body = body..readlength(chunk_size); - if readline() ~= "" then coroutine.yield("invalid-chunk-ending"); end - end - local trailers = readheaders(); - elseif len then -- TODO check for invalid len - body = readlength(len); - else -- read to end - repeat - local newdata = coroutine.yield(); - data = data..newdata; - until newdata == ""; - body, data = data, ""; - end - end - - success_cb({ - code = status_code; - httpversion = httpversion; - headers = headers; - body = body; - }); - end - else coroutine.yield("unknown-parser-type"); end -end - -function new(success_cb, error_cb, parser_type, options_cb) - local co = coroutine.create(parser); - coroutine.resume(co, success_cb, parser_type, options_cb) - return { - feed = function(self, data) - if not data then - if parser_type == "client" then coroutine.resume(co, ""); end - co = deadroutine; - return error_cb(); - end - local success, result = coroutine.resume(co, data); - if result then - co = deadroutine; - return error_cb(result); - end - end; - }; -end - -return _M; -- cgit v1.2.3 From 5487d228903f73cb9eba1bc385c7774dfba9a29c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 12 Apr 2013 00:31:05 +0100 Subject: net.http: Swap response and request parameters passed to callback (will break some modules) --- net/http.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/http.lua b/net/http.lua index b3bd5a67..4eb4a2ac 100644 --- a/net/http.lua +++ b/net/http.lua @@ -76,8 +76,7 @@ local function request_reader(request, data) if not data then return; end local function success_cb(r) if request.callback then - for k,v in pairs(r) do request[k] = v; end - request.callback(r.body, r.code, request, r); + request.callback(r.body, r.code, r, request); request.callback = nil; end destroy_request(request); -- cgit v1.2.3 From 23904ba399f223019ed73c5967d2cf24f089325f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 12 Apr 2013 00:44:49 +0100 Subject: util.iterators: Add ripairs() (ipairs() in reverse) (thanks Maranda) --- util/iterators.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/iterators.lua b/util/iterators.lua index fb89f4a5..1f6aacb8 100644 --- a/util/iterators.lua +++ b/util/iterators.lua @@ -122,6 +122,11 @@ function it.tail(n, f, s, var) --return reverse(head(n, reverse(f, s, var))); end +local function _ripairs_iter(t, key) if key > 1 then return key-1, t[key-1]; end end +function it.ripairs(t) + return _ripairs_iter, t, #t+1; +end + local function _range_iter(max, curr) if curr < max then return curr + 1; end end function it.range(x, y) if not y then x, y = 1, x; end -- Default to 1..x if y not given -- cgit v1.2.3 From 159d5b44a7ffa3eeaae3d5efebc6aad71b497600 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 9 Apr 2013 15:50:46 +0200 Subject: prosodyctl: Bump util.pposix version for API change --- prosodyctl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prosodyctl b/prosodyctl index 71b99f9e..24d28157 100755 --- a/prosodyctl +++ b/prosodyctl @@ -135,7 +135,7 @@ dependencies.log_warnings(); -- Switch away from root and into the prosody user -- local switched_user, current_uid; -local want_pposix_version = "0.3.5"; +local want_pposix_version = "0.3.6"; local ok, pposix = pcall(require, "util.pposix"); if ok and pposix then -- cgit v1.2.3 From 49e8f27d3b1142ff7f81e1cd84ff4a5159ecd004 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 12 Apr 2013 20:26:35 +0100 Subject: util.http: Refactor and import all necessary functions --- util/http.lua | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/util/http.lua b/util/http.lua index 5dd636d9..f7259920 100644 --- a/util/http.lua +++ b/util/http.lua @@ -5,12 +5,14 @@ -- COPYING file in the source package for more information. -- -local http = {}; +local format, char = string.format, string.char; +local pairs, ipairs, tonumber = pairs, ipairs, tonumber; +local t_insert, t_concat = table.insert, table.concat; -function http.urlencode(s) +local function urlencode(s) return s and (s:gsub("[^a-zA-Z0-9.~_-]", function (c) return format("%%%02x", c:byte()); end)); end -function http.urldecode(s) +local function urldecode(s) return s and (s:gsub("%%(%x%x)", function (c) return char(tonumber(c,16)); end)); end @@ -24,7 +26,7 @@ local function _formencodepart(s) end)); end -function http.formencode(form) +local function formencode(form) local result = {}; if form[1] then -- Array of ordered { name, value } for _, field in ipairs(form) do @@ -38,7 +40,7 @@ function http.formencode(form) return t_concat(result, "&"); end -function http.formdecode(s) +local function formdecode(s) if not s:match("=") then return urldecode(s); end local r = {}; for k, v in s:gmatch("([^=&]*)=([^&]*)") do @@ -50,11 +52,13 @@ function http.formdecode(s) return r; end -function http.contains_token(field, token) +local function contains_token(field, token) field = ","..field:gsub("[ \t]", ""):lower()..","; return field:find(","..token:lower()..",", 1, true) ~= nil; end - - -return http; +return { + urlencode = urlencode, urldecode = urldecode; + formencode = formencode, formdecode = formdecode; + contains_token = contains_token; +}; -- cgit v1.2.3 From 31ca3b068102fec971e5f00ff09d6f0fa63e64d1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Apr 2013 12:30:40 +0100 Subject: Makefile: Specify explicit mode when installing prosody.version, to avoid it defaulting to something nasty (executable) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0e69192a..b96b6732 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ install: prosody.install prosodyctl.install prosody.cfg.lua.install util/encodin install -m644 certs/* $(CONFIG)/certs 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 + test -e prosody.version && install -m644 prosody.version $(SOURCE)/prosody.version || true $(MAKE) install -C util-src clean: -- cgit v1.2.3 From d1c7fed605d0616e3b2d68c69a35879f705c0422 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 15 Apr 2013 19:37:15 +0200 Subject: mod_s2s: Add missing space --- plugins/mod_s2s/mod_s2s.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index eb10cd35..6764e857 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -642,7 +642,7 @@ function check_auth_policy(event) if must_secure and not session.cert_identity_status then module:log("warn", "Forbidding insecure connection to/from %s", host); if session.direction == "incoming" then - session:close({ condition = "not-authorized", text = "Your server's certificate is invalid, expired, or not trusted by"..session.to_host }); + session:close({ condition = "not-authorized", text = "Your server's certificate is invalid, expired, or not trusted by "..session.to_host }); else -- Close outgoing connections without warning session:close(false); end -- cgit v1.2.3 From 01d1134ca40ee32b1d84d10181c4d22e60669456 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Apr 2013 21:21:57 +0100 Subject: net.http.parser: Fix off-by-one error in chunked encoding parser --- net/http/parser.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/http/parser.lua b/net/http/parser.lua index 34742d2b..9688e052 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -133,7 +133,8 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped success_cb(packet); elseif #buf - chunk_start + 2 >= chunk_size then -- we have a chunk - packet.body = packet.body..buf:sub(chunk_start, chunk_start + chunk_size); + print(chunk_start, chunk_size, ("%q"):format(buf)) + packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1)); buf = buf:sub(chunk_start + chunk_size + 2); chunk_size, chunk_start = nil, nil; else -- Partial chunk remaining -- cgit v1.2.3 From 8bd9c918ede69b74c4efb72ee0d848cdd8c728aa Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Apr 2013 21:25:59 +0100 Subject: net.http.parser: Remove accidentally-committed debugging --- net/http/parser.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/net/http/parser.lua b/net/http/parser.lua index 9688e052..f9e6cea0 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -133,7 +133,6 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped success_cb(packet); elseif #buf - chunk_start + 2 >= chunk_size then -- we have a chunk - print(chunk_start, chunk_size, ("%q"):format(buf)) packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1)); buf = buf:sub(chunk_start + chunk_size + 2); chunk_size, chunk_start = nil, nil; -- cgit v1.2.3 From 66bd53b9d22135237afb974a8d2d2a665777379b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Apr 2013 21:30:44 +0200 Subject: net.server_select: Normalize indentation --- net/server_select.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/server_select.lua b/net/server_select.lua index f123f4b7..6592de9b 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -569,7 +569,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport end out_put( "server.lua: ssl handshake error: ", tostring(err or "handshake too long") ) _ = handler and handler:force_close("ssl handshake failed") - return false, err -- handshake failed + return false, err -- handshake failed end ) end @@ -613,7 +613,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.readbuffer = handshake handler.sendbuffer = handshake - return handshake( socket ) -- do handshake + return handshake( socket ) -- do handshake end end @@ -629,10 +629,10 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport if sslctx and luasec then out_put "server.lua: auto-starting ssl negotiation..." handler.autostart_ssl = true; - local ok, err = handler:starttls(sslctx); - if ok == false then - return nil, nil, err - end + local ok, err = handler:starttls(sslctx); + if ok == false then + return nil, nil, err + end end return handler, socket -- cgit v1.2.3 From 65d488d97548f04cdece2495f5f8c84f154f0073 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 16 Apr 2013 18:15:10 -0400 Subject: net.server_select: Move socket timeout cleanup code out of a timer, into the select loop (which makes util.timer the only timer using server_select._addtimer). --- net/server_select.lua | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/net/server_select.lua b/net/server_select.lua index 6592de9b..86c9daef 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -846,6 +846,26 @@ loop = function(once) -- this is the main loop of the program _closelist[ handler ] = nil; end _currenttime = luasocket_gettime( ) + + local difftime = os_difftime( _currenttime - _starttime ) + if difftime > _checkinterval then + _starttime = _currenttime + for handler, timestamp in pairs( _writetimes ) do + if os_difftime( _currenttime - timestamp ) > _sendtimeout then + --_writetimes[ handler ] = nil + handler.disconnect( )( handler, "send timeout" ) + handler:force_close() -- forced disconnect + end + end + for handler, timestamp in pairs( _readtimes ) do + if os_difftime( _currenttime - timestamp ) > _readtimeout then + --_readtimes[ handler ] = nil + handler.disconnect( )( handler, "read timeout" ) + handler:close( ) -- forced disconnect? + end + end + end + if _currenttime - _timer >= math_min(next_timer_time, 1) then next_timer_time = math_huge; for i = 1, _timerlistlen do @@ -921,28 +941,6 @@ use "setmetatable" ( _writetimes, { __mode = "k" } ) _timer = luasocket_gettime( ) _starttime = luasocket_gettime( ) -addtimer( function( ) - local difftime = os_difftime( _currenttime - _starttime ) - if difftime > _checkinterval then - _starttime = _currenttime - for handler, timestamp in pairs( _writetimes ) do - if os_difftime( _currenttime - timestamp ) > _sendtimeout then - --_writetimes[ handler ] = nil - handler.disconnect( )( handler, "send timeout" ) - handler:force_close() -- forced disconnect - end - end - for handler, timestamp in pairs( _readtimes ) do - if os_difftime( _currenttime - timestamp ) > _readtimeout then - --_readtimes[ handler ] = nil - handler.disconnect( )( handler, "read timeout" ) - handler:close( ) -- forced disconnect? - end - end - end - end -) - local function setlogger(new_logger) local old_logger = log; if new_logger then -- cgit v1.2.3 From f76ad9a7f360ddb10e694bfffccd3b5388e87eca Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 16 Apr 2013 18:18:22 -0400 Subject: net.server_select: Add and improve some comments. --- net/server_select.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/server_select.lua b/net/server_select.lua index 86c9daef..983b06a2 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -847,6 +847,7 @@ loop = function(once) -- this is the main loop of the program end _currenttime = luasocket_gettime( ) + -- Check for socket timeouts local difftime = os_difftime( _currenttime - _starttime ) if difftime > _checkinterval then _starttime = _currenttime @@ -866,6 +867,7 @@ loop = function(once) -- this is the main loop of the program end end + -- Fire timers if _currenttime - _timer >= math_min(next_timer_time, 1) then next_timer_time = math_huge; for i = 1, _timerlistlen do @@ -876,8 +878,9 @@ loop = function(once) -- this is the main loop of the program else next_timer_time = next_timer_time - (_currenttime - _timer); end - socket_sleep( _sleeptime ) -- wait some time - --collectgarbage( ) + + -- wait some time (0 by default) + socket_sleep( _sleeptime ) until quitting; if once and quitting == "once" then quitting = nil; return; end return "quitting" -- cgit v1.2.3 From 319b50bacf6c985834028dc6335313678f8c9573 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 17 Apr 2013 14:12:47 +0100 Subject: mod_pubsub: Add id to stored item when auto-generated. Fixes #335 --- plugins/mod_pubsub.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/mod_pubsub.lua b/plugins/mod_pubsub.lua index 7bd33102..4d3911bb 100644 --- a/plugins/mod_pubsub.lua +++ b/plugins/mod_pubsub.lua @@ -190,7 +190,11 @@ function handlers.set_publish(origin, stanza, publish) return origin.send(pubsub_error_reply(stanza, "nodeid-required")); end local item = publish:get_child("item"); - local id = (item and item.attr.id) or uuid_generate(); + local id = (item and item.attr.id); + if not id then + id = uuid_generate(); + item.attr.id = id; + end local ok, ret = service:publish(node, stanza.attr.from, id, item); local reply; if ok then -- cgit v1.2.3 From bd94f0a6c2d19df775c324ef184e4a434ceeef6c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 17 Apr 2013 14:32:26 +0100 Subject: mod_pubsub: Only assign id to item element if there is one --- plugins/mod_pubsub.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/mod_pubsub.lua b/plugins/mod_pubsub.lua index 4d3911bb..22969ab5 100644 --- a/plugins/mod_pubsub.lua +++ b/plugins/mod_pubsub.lua @@ -193,7 +193,9 @@ function handlers.set_publish(origin, stanza, publish) local id = (item and item.attr.id); if not id then id = uuid_generate(); - item.attr.id = id; + if item then + item.attr.id = id; + end end local ok, ret = service:publish(node, stanza.attr.from, id, item); local reply; -- cgit v1.2.3 From 6789631edd446040ca4183398fc7a925305a3bbd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Apr 2013 19:10:04 +0200 Subject: net.server_select: Don't call onconnect twice on SSL connections --- net/server_select.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/server_select.lua b/net/server_select.lua index 8ce9eed2..f123f4b7 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -551,9 +551,6 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.readbuffer = _readbuffer -- when handshake is done, replace the handshake function with regular functions handler.sendbuffer = _sendbuffer _ = status and status( handler, "ssl-handshake-complete" ) - if self.autostart_ssl and listeners.onconnect then - listeners.onconnect(self); - end _readlistlen = addsocket(_readlist, client, _readlistlen) return true else -- cgit v1.2.3 From eb6601038d104544eb034e3846344d2ba833d82c Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 17 Apr 2013 15:28:20 -0400 Subject: net.http.server: The correct Connection header value to look for is Keep-Alive, not keep-alive. --- 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 830579c9..a983b8d5 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -159,7 +159,7 @@ function handle_request(conn, request, finish_cb) local conn_header = request.headers.connection; conn_header = conn_header and ","..conn_header:gsub("[ \t]", ""):lower().."," or "" local httpversion = request.httpversion - local persistent = conn_header:find(",keep-alive,", 1, true) + local persistent = conn_header:find(",Keep-Alive,", 1, true) or (httpversion == "1.1" and not conn_header:find(",close,", 1, true)); local response_conn_header; -- cgit v1.2.3 From fc2f7c817af24bcb2c1d978120f81825b2d986a2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 18 Apr 2013 00:08:58 +0100 Subject: net.http: When HTTP request fails due to a network or SSL error, call the callback to let it know --- net/http.lua | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/net/http.lua b/net/http.lua index 4eb4a2ac..639ecf6a 100644 --- a/net/http.lua +++ b/net/http.lua @@ -66,24 +66,29 @@ end function listener.ondisconnect(conn, err) local request = requests[conn]; if request and request.conn then - request:reader(nil); + request:reader(nil, err); end requests[conn] = nil; end -local function request_reader(request, data) +local function request_reader(request, data, err) if not request.parser then - if not data then return; end - local function success_cb(r) + local function error_cb(reason) if request.callback then - request.callback(r.body, r.code, r, request); + request.callback(reason or "connection-closed", 0, request); request.callback = nil; end destroy_request(request); end - local function error_cb(r) + + if not data then + error_cb(err); + return; + end + + local function success_cb(r) if request.callback then - request.callback(r or "connection-closed", 0, request); + request.callback(r.body, r.code, r, request); request.callback = nil; end destroy_request(request); -- cgit v1.2.3 From bad45a4ad41914b066408fcc06ece6a4a8048edc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 18 Apr 2013 00:39:59 +0100 Subject: Backed out changeset f2631a14b953 --- net/server_select.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/server_select.lua b/net/server_select.lua index f123f4b7..8ce9eed2 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -551,6 +551,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.readbuffer = _readbuffer -- when handshake is done, replace the handshake function with regular functions handler.sendbuffer = _sendbuffer _ = status and status( handler, "ssl-handshake-complete" ) + if self.autostart_ssl and listeners.onconnect then + listeners.onconnect(self); + end _readlistlen = addsocket(_readlist, client, _readlistlen) return true else -- cgit v1.2.3 From d5d5ac230db5b83a3c45381a1dc5ab66d0cfc53b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 18 Apr 2013 00:41:03 +0100 Subject: net.server_select: Don't call onconnect twice for SSL connections --- net/server_select.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_select.lua b/net/server_select.lua index ca80534c..28f1dc6d 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -243,7 +243,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx ) -- t end connections = connections + 1 out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport)) - if dispatch then + if dispatch and not sslctx then -- SSL connections will notify onconnect when handshake completes return dispatch( handler ); end return; -- cgit v1.2.3 From b06f38170afcf5801bf38add8f141e624c613a82 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 18 Apr 2013 11:13:40 +0100 Subject: mod_storage_sql2 (temporary name), sql.lib, util.sql: New SQL API supporting cross-module connection sharing, transactions and Things - a work in progress --- plugins/mod_storage_sql2.lua | 237 ++++++++++++++++++++++++++++++ plugins/sql.lib.lua | 9 ++ util/sql.lua | 340 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 586 insertions(+) create mode 100644 plugins/mod_storage_sql2.lua create mode 100644 plugins/sql.lib.lua create mode 100644 util/sql.lua diff --git a/plugins/mod_storage_sql2.lua b/plugins/mod_storage_sql2.lua new file mode 100644 index 00000000..7d705b0b --- /dev/null +++ b/plugins/mod_storage_sql2.lua @@ -0,0 +1,237 @@ + +local json = require "util.json"; +local resolve_relative_path = require "core.configmanager".resolve_relative_path; + +local mod_sql = module:require("sql"); +local params = module:get_option("sql"); + +local engine; -- TODO create engine + +local function create_table() + --[[local Table,Column,Index = mod_sql.Table,mod_sql.Column,mod_sql.Index; + local ProsodyTable = Table { + name="prosody"; + Column { name="host", type="TEXT", nullable=false }; + Column { name="user", type="TEXT", nullable=false }; + Column { name="store", type="TEXT", nullable=false }; + Column { name="key", type="TEXT", nullable=false }; + Column { name="type", type="TEXT", nullable=false }; + Column { name="value", type="TEXT", nullable=false }; + Index { name="prosody_index", "host", "user", "store", "key" }; + }; + engine:transaction(function() + ProsodyTable:create(engine); + end);]] + if not module:get_option("sql_manage_tables", true) then + return; + end + + local create_sql = "CREATE TABLE `prosody` (`host` TEXT, `user` TEXT, `store` TEXT, `key` TEXT, `type` TEXT, `value` TEXT);"; + if params.driver == "PostgreSQL" then + create_sql = create_sql:gsub("`", "\""); + elseif params.driver == "MySQL" then + create_sql = create_sql:gsub("`value` TEXT", "`value` MEDIUMTEXT") + :gsub(";$", " CHARACTER SET 'utf8' COLLATE 'utf8_bin';"); + end + + local index_sql = "CREATE INDEX `prosody_index` ON `prosody` (`host`, `user`, `store`, `key`)"; + if params.driver == "PostgreSQL" then + index_sql = index_sql:gsub("`", "\""); + elseif params.driver == "MySQL" then + index_sql = index_sql:gsub("`([,)])", "`(20)%1"); + end + + local success,err = engine:transaction(function() + engine:execute(create_sql); + engine:execute(index_sql); + end); + if not success then -- so we failed to create + if params.driver == "MySQL" then + success,err = engine:transaction(function() + local result = engine:execute("SHOW COLUMNS FROM prosody WHERE Field='value' and Type='text'"); + if result:rowcount() > 0 then + module:log("info", "Upgrading database schema..."); + engine:execute("ALTER TABLE prosody MODIFY COLUMN `value` MEDIUMTEXT"); + module:log("info", "Database table automatically upgraded"); + end + return true; + end); + if not success then + module:log("error", "Failed to check/upgrade database schema (%s), please see " + .."http://prosody.im/doc/mysql for help", + err or "unknown error"); + end + end + end +end +local function set_encoding() + if params.driver ~= "SQLite3" then + local set_names_query = "SET NAMES 'utf8';"; + if params.driver == "MySQL" then + set_names_query = set_names_query:gsub(";$", " COLLATE 'utf8_bin';"); + end + local success,err = engine:transaction(function() return engine:execute(set_names_query); end); + if not success then + module:log("error", "Failed to set database connection encoding to UTF8: %s", err); + return; + end + if params.driver == "MySQL" then + -- COMPAT w/pre-0.9: Upgrade tables to UTF-8 if not already + local check_encoding_query = "SELECT `COLUMN_NAME`,`COLUMN_TYPE` FROM `information_schema`.`columns` WHERE `TABLE_NAME`='prosody' AND ( `CHARACTER_SET_NAME`!='utf8' OR `COLLATION_NAME`!='utf8_bin' );"; + local success,err = engine:transaction(function() + local result = engine:execute(check_encoding_query); + local n_bad_columns = result:rowcount(); + if n_bad_columns > 0 then + module:log("warn", "Found %d columns in prosody table requiring encoding change, updating now...", n_bad_columns); + local fix_column_query1 = "ALTER TABLE `prosody` CHANGE `%s` `%s` BLOB;"; + local fix_column_query2 = "ALTER TABLE `prosody` CHANGE `%s` `%s` %s CHARACTER SET 'utf8' COLLATE 'utf8_bin';"; + for row in success:rows() do + local column_name, column_type = unpack(row); + engine:execute(fix_column_query1:format(column_name, column_name)); + engine:execute(fix_column_query2:format(column_name, column_name, column_type)); + end + module:log("info", "Database encoding upgrade complete!"); + end + end); + local success,err = engine:transaction(function() return engine:execute(check_encoding_query); end); + if not success then + module:log("error", "Failed to check/upgrade database encoding: %s", err or "unknown error"); + end + end + end +end + +do -- process options to get a db connection + params = params or { driver = "SQLite3" }; + + if params.driver == "SQLite3" then + params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); + end + + assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); + + --local dburi = db2uri(params); + engine = mod_sql:create_engine(params); + + -- Encoding mess + set_encoding(); + + -- Automatically create table, ignore failure (table probably already exists) + create_table(); +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.encode(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 t == "json" then + return json.decode(value); + end +end + +local host = module.host; +local user, store; + +local function keyval_store_get() + local haveany; + local result = {}; + for row in engine:select("SELECT `key`,`type`,`value` FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=?", host, user, store) do + haveany = true; + local k = row[1]; + local v = deserialize(row[2], row[3]); + if k and 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 + if haveany then + return result; + end +end +local function keyval_store_set(data) + engine:delete("DELETE FROM `prosody` WHERE `host`=? AND `user`=? AND `store`=?", host, user, store); + + if data and next(data) ~= nil then + local extradata = {}; + for key, value in pairs(data) do + if type(key) == "string" and key ~= "" then + local t, value = serialize(value); + assert(t, value); + engine:insert("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)", host, user, store, key, t, value); + else + extradata[key] = value; + end + end + if next(extradata) ~= nil then + local t, extradata = serialize(extradata); + assert(t, extradata); + engine:insert("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)", host, user, store, "", t, extradata); + end + end + return true; +end + +local keyval_store = {}; +keyval_store.__index = keyval_store; +function keyval_store:get(username) + user,store = username,self.store; + return select(2, engine:transaction(keyval_store_get)); +end +function keyval_store:set(username, data) + user,store = username,self.store; + return engine:transaction(function() + return keyval_store_set(data); + end); +end +function keyval_store:users() + return engine:transaction(function() + return engine:select("SELECT DISTINCT `user` FROM `prosody` WHERE `host`=? AND `store`=?", host, self.store); + end); +end + +local driver = {}; + +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 + +function driver:stores(username) + local sql = "SELECT DISTINCT `store` FROM `prosody` WHERE `host`=? AND `user`" .. + (username == true and "!=?" or "=?"); + if username == true or not username then + username = ""; + end + return engine:transaction(function() + return engine:select(sql, host, username); + end); +end + +function driver:purge(username) + return engine:transaction(function() + local stmt,err = engine:delete("DELETE FROM `prosody` WHERE `host`=? AND `user`=?", host, username); + return true,err; + end); +end + +module:provides("storage", driver); + + diff --git a/plugins/sql.lib.lua b/plugins/sql.lib.lua new file mode 100644 index 00000000..005ee45d --- /dev/null +++ b/plugins/sql.lib.lua @@ -0,0 +1,9 @@ +local cache = module:shared("/*/sql.lib/util.sql"); + +if not cache._M then + prosody.unlock_globals(); + cache._M = require "util.sql"; + prosody.lock_globals(); +end + +return cache._M; diff --git a/util/sql.lua b/util/sql.lua new file mode 100644 index 00000000..f360d6d0 --- /dev/null +++ b/util/sql.lua @@ -0,0 +1,340 @@ + +local setmetatable, getmetatable = setmetatable, getmetatable; +local ipairs, unpack, select = ipairs, unpack, select; +local tonumber, tostring = tonumber, tostring; +local assert, xpcall, debug_traceback = assert, xpcall, debug.traceback; +local t_concat = table.concat; +local s_char = string.char; +local log = require "util.logger".init("sql"); + +local DBI = require "DBI"; +-- This loads all available drivers while globals are unlocked +-- LuaDBI should be fixed to not set globals. +DBI.Drivers(); +local build_url = require "socket.url".build; + +module("sql") + +local column_mt = {}; +local table_mt = {}; +local query_mt = {}; +--local op_mt = {}; +local index_mt = {}; + +function is_column(x) return getmetatable(x)==column_mt; end +function is_index(x) return getmetatable(x)==index_mt; end +function is_table(x) return getmetatable(x)==table_mt; end +function is_query(x) return getmetatable(x)==query_mt; end +--function is_op(x) return getmetatable(x)==op_mt; end +--function expr(...) return setmetatable({...}, op_mt); end +function Integer(n) return "Integer()" end +function String(n) return "String()" end + +--[[local ops = { + __add = function(a, b) return "("..a.."+"..b..")" end; + __sub = function(a, b) return "("..a.."-"..b..")" end; + __mul = function(a, b) return "("..a.."*"..b..")" end; + __div = function(a, b) return "("..a.."/"..b..")" end; + __mod = function(a, b) return "("..a.."%"..b..")" end; + __pow = function(a, b) return "POW("..a..","..b..")" end; + __unm = function(a) return "NOT("..a..")" end; + __len = function(a) return "COUNT("..a..")" end; + __eq = function(a, b) return "("..a.."=="..b..")" end; + __lt = function(a, b) return "("..a.."<"..b..")" end; + __le = function(a, b) return "("..a.."<="..b..")" end; +}; + +local functions = { + +}; + +local cmap = { + [Integer] = Integer(); + [String] = String(); +};]] + +function Column(definition) + return setmetatable(definition, column_mt); +end +function Table(definition) + local c = {} + for i,col in ipairs(definition) do + if is_column(col) then + c[i], c[col.name] = col, col; + elseif is_index(col) then + col.table = definition.name; + end + end + return setmetatable({ __table__ = definition, c = c, name = definition.name }, table_mt); +end +function Index(definition) + return setmetatable(definition, index_mt); +end + +function table_mt:__tostring() + local s = { 'name="'..self.__table__.name..'"' } + for i,col in ipairs(self.__table__) do + s[#s+1] = tostring(col); + end + return 'Table{ '..t_concat(s, ", ")..' }' +end +table_mt.__index = {}; +function table_mt.__index:create(engine) + return engine:_create_table(self); +end +function table_mt:__call(...) + -- TODO +end +function column_mt:__tostring() + return 'Column{ name="'..self.name..'", type="'..self.type..'" }' +end +function index_mt:__tostring() + local s = 'Index{ name="'..self.name..'"'; + for i=1,#self do s = s..', "'..self[i]:gsub("[\\\"]", "\\%1")..'"'; end + return s..' }'; +-- return 'Index{ name="'..self.name..'", type="'..self.type..'" }' +end +-- + +local function urldecode(s) return s and (s:gsub("%%(%x%x)", function (c) return s_char(tonumber(c,16)); end)); end +local function parse_url(url) + local scheme, secondpart, database = url:match("^([%w%+]+)://([^/]*)/?(.*)"); + assert(scheme, "Invalid URL format"); + local username, password, host, port; + local authpart, hostpart = secondpart:match("([^@]+)@([^@+])"); + if not authpart then hostpart = secondpart; end + if authpart then + username, password = authpart:match("([^:]*):(.*)"); + username = username or authpart; + password = password and urldecode(password); + end + if hostpart then + host, port = hostpart:match("([^:]*):(.*)"); + host = host or hostpart; + port = port and assert(tonumber(port), "Invalid URL format"); + end + return { + scheme = scheme:lower(); + username = username; password = password; + host = host; port = port; + database = #database > 0 and database or nil; + }; +end + +--[[local session = {}; + +function session.query(...) + local rets = {...}; + local query = setmetatable({ __rets = rets, __filters }, query_mt); + return query; +end +-- + +local function db2uri(params) + return build_url{ + scheme = params.driver, + user = params.username, + password = params.password, + host = params.host, + port = params.port, + path = params.database, + }; +end]] + +local engine = {}; +function engine:connect() + if self.conn then return true; end + + local params = self.params; + assert(params.driver, "no driver") + local dbh, err = DBI.Connect( + params.driver, params.database, + params.username, params.password, + params.host, params.port + ); + if not dbh then return nil, err; end + dbh:autocommit(false); -- don't commit automatically + self.conn = dbh; + self.prepared = {}; + return true; +end +function engine:execute(sql, ...) + local success, err = self:connect(); + if not success then return success, err; end + local prepared = self.prepared; + + local stmt = prepared[sql]; + if not stmt then + local err; + stmt, err = self.conn:prepare(sql); + if not stmt then return stmt, err; end + prepared[sql] = stmt; + end + + local success, err = stmt:execute(...); + if not success then return success, err; end + return stmt; +end + +local result_mt = { __index = { + affected = function(self) return self.__affected; end; + rowcount = function(self) return self.__rowcount; end; +} }; + +function engine:execute_query(sql, ...) + if self.params.driver == "PostgreSQL" then + sql = sql:gsub("`", "\""); + end + local stmt = assert(self.conn:prepare(sql)); + assert(stmt:execute(...)); + return stmt:rows(); +end +function engine:execute_update(sql, ...) + if self.params.driver == "PostgreSQL" then + sql = sql:gsub("`", "\""); + end + local prepared = self.prepared; + local stmt = prepared[sql]; + if not stmt then + stmt = assert(self.conn:prepare(sql)); + prepared[sql] = stmt; + end + assert(stmt:execute(...)); + return setmetatable({ __affected = stmt:affected(), __rowcount = stmt:rowcount() }, result_mt); +end +engine.insert = engine.execute_update; +engine.select = engine.execute_query; +engine.delete = engine.execute_update; +engine.update = engine.execute_update; +function engine:_transaction(func, ...) + if not self.conn then + local a,b = self:connect(); + if not a then return a,b; end + end + --assert(not self.__transaction, "Recursive transactions not allowed"); + local args, n_args = {...}, select("#", ...); + local function f() return func(unpack(args, 1, n_args)); end + self.__transaction = true; + local success, a, b, c = xpcall(f, debug_traceback); + self.__transaction = nil; + if success then + log("debug", "SQL transaction success [%s]", tostring(func)); + local ok, err = self.conn:commit(); + if not ok then return ok, err; end -- commit failed + return success, a, b, c; + else + log("debug", "SQL transaction failure [%s]: %s", tostring(func), a); + if self.conn then self.conn:rollback(); end + return success, a; + end +end +function engine:transaction(...) + local a,b = self:_transaction(...); + if not a then + local conn = self.conn; + if not conn or not conn:ping() then + self.conn = nil; + a,b = self:_transaction(...); + end + end + return a,b; +end +function engine:_create_index(index) + local sql = "CREATE INDEX `"..index.name.."` ON `"..index.table.."` ("; + for i=1,#index do + sql = sql.."`"..index[i].."`"; + if i ~= #index then sql = sql..", "; end + end + sql = sql..");" + if self.params.driver == "PostgreSQL" then + sql = sql:gsub("`", "\""); + elseif self.params.driver == "MySQL" then + sql = sql:gsub("`([,)])", "`(20)%1"); + end + --print(sql); + return self:execute(sql); +end +function engine:_create_table(table) + local sql = "CREATE TABLE `"..table.name.."` ("; + for i,col in ipairs(table.c) do + sql = sql.."`"..col.name.."` "..col.type; + if col.nullable == false then sql = sql.." NOT NULL"; end + if i ~= #table.c then sql = sql..", "; end + end + sql = sql.. ");" + if self.params.driver == "PostgreSQL" then + sql = sql:gsub("`", "\""); + end + local success,err = self:execute(sql); + if not success then return success,err; end + for i,v in ipairs(table.__table__) do + if is_index(v) then + self:_create_index(v); + end + end + return success; +end +local engine_mt = { __index = engine }; + +local function db2uri(params) + return build_url{ + scheme = params.driver, + user = params.username, + password = params.password, + host = params.host, + port = params.port, + path = params.database, + }; +end +local engine_cache = {}; -- TODO make weak valued +function create_engine(self, params) + local url = db2uri(params); + if not engine_cache[url] then + local engine = setmetatable({ url = url, params = params }, engine_mt); + engine_cache[url] = engine; + end + return engine_cache[url]; +end + + +--[[Users = Table { + name="users"; + Column { name="user_id", type=String(), primary_key=true }; +}; +print(Users) +print(Users.c.user_id)]] + +--local engine = create_engine('postgresql://scott:tiger@localhost:5432/mydatabase'); +--[[local engine = create_engine{ driver = "SQLite3", database = "./alchemy.sqlite" }; + +local i = 0; +for row in assert(engine:execute("select * from sqlite_master")):rows(true) do + i = i+1; + print(i); + for k,v in pairs(row) do + print("",k,v); + end +end +print("---") + +Prosody = Table { + name="prosody"; + Column { name="host", type="TEXT", nullable=false }; + Column { name="user", type="TEXT", nullable=false }; + Column { name="store", type="TEXT", nullable=false }; + Column { name="key", type="TEXT", nullable=false }; + Column { name="type", type="TEXT", nullable=false }; + Column { name="value", type="TEXT", nullable=false }; + Index { name="prosody_index", "host", "user", "store", "key" }; +}; +--print(Prosody); +assert(engine:transaction(function() + assert(Prosody:create(engine)); +end)); + +for row in assert(engine:execute("select user from prosody")):rows(true) do + print("username:", row['username']) +end +--result.close();]] + +return _M; -- cgit v1.2.3 From 1cf3761516d65ee399a027aebf7b94756c6a2e4d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 19 Apr 2013 13:29:47 +0100 Subject: moduleapi: Add module:open_store() as a front-end to storagemanager.open() --- core/moduleapi.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/moduleapi.lua b/core/moduleapi.lua index fa20c3cd..a8e6881e 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -337,4 +337,8 @@ function api:load_resource(path, mode) return io.open(path, mode); end +function api:open_store(name, type) + return storagemanager.open(self.host, name, type); +end + return api; -- cgit v1.2.3 From ee1ddadb4c2747bf4b74b4b410833702d1cd4a97 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 22 Apr 2013 12:24:42 +0100 Subject: net.server.http: Ensure that event map cannot grow forever (limit to 10K wildcard-only entries) --- net/http/server.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/http/server.lua b/net/http/server.lua index a983b8d5..3c2b55d5 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -27,6 +27,8 @@ local function is_wildcard_match(wildcard_event, event) return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1); end +local recent_wildcard_events, max_cached_wildcard_events = {}, 10000; + local event_map = events._event_map; setmetatable(events._handlers, { __index = function (handlers, curr_event) @@ -58,6 +60,12 @@ setmetatable(events._handlers, { handlers_array = false; end rawset(handlers, curr_event, handlers_array); + if not event_map[curr_event] then -- Only wildcard handlers match, if any + table.insert(recent_wildcard_events, curr_event); + if #recent_wildcard_events > max_cached_wildcard_events then + rawset(handlers, table.remove(recent_wildcard_events, 1), nil); + end + end return handlers_array; end; __newindex = function (handlers, curr_event, handlers_array) -- cgit v1.2.3 From 10e2b82e095ad30cb755117e0ae3e60c578ec615 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 22 Apr 2013 12:25:00 +0100 Subject: net.server.http: Add a comment --- net/http/server.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/http/server.lua b/net/http/server.lua index 3c2b55d5..577b05ef 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -31,6 +31,7 @@ local recent_wildcard_events, max_cached_wildcard_events = {}, 10000; local event_map = events._event_map; setmetatable(events._handlers, { + -- Called when firing an event that doesn't exist (but may match a wildcard handler) __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 -- cgit v1.2.3 From 093cf12d36b125db7afa71a4b7bdffd05c42fb71 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 22 Apr 2013 12:35:52 +0100 Subject: mod_c2s, mod_s2s, net.http, net.http.server: Improve tracebacks (omit traceback function), to make it clearer where an error occured --- net/http.lua | 8 ++++---- net/http/server.lua | 4 ++-- plugins/mod_c2s.lua | 2 +- plugins/mod_s2s/mod_s2s.lua | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/net/http.lua b/net/http.lua index 639ecf6a..3b783a41 100644 --- a/net/http.lua +++ b/net/http.lua @@ -17,9 +17,9 @@ local ssl_available = pcall(require, "ssl"); local server = require "net.server" local t_insert, t_concat = table.insert, table.concat; -local pairs, ipairs = pairs, ipairs; -local tonumber, tostring, xpcall, select, debug_traceback, char, format = - tonumber, tostring, xpcall, select, debug.traceback, string.char, string.format; +local pairs = pairs; +local tonumber, tostring, xpcall, select, traceback = + tonumber, tostring, xpcall, select, debug.traceback; local log = require "util.logger".init("http"); @@ -101,7 +101,7 @@ local function request_reader(request, data, err) request.parser:feed(data); end -local function handleerr(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug_traceback()); end +local function handleerr(err) log("error", "Traceback[http]: %s", traceback(tostring(err), 2)); end function request(u, ex, callback) local req = url.parse(u); diff --git a/net/http/server.lua b/net/http/server.lua index 577b05ef..dec7da19 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -9,7 +9,7 @@ local pairs = pairs; local s_upper = string.upper; local setmetatable = setmetatable; local xpcall = xpcall; -local debug = debug; +local traceback = debug.traceback; local tostring = tostring; local codes = require "net.http.codes"; @@ -88,7 +88,7 @@ local _1, _2, _3; local function _handle_request() return handle_request(_1, _2, _3); end local last_err; -local function _traceback_handler(err) last_err = err; log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end +local function _traceback_handler(err) last_err = err; log("error", "Traceback[httpserver]: %s", traceback(tostring(err), 2)); end events.add_handler("http-error", function (error) return "Error processing request: "..codes[error.code]..". Check your error log for more information."; end, -1); diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 4a3197d9..cafd0c71 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -116,7 +116,7 @@ function stream_callbacks.error(session, error, data) end end -local function handleerr(err) log("error", "Traceback[c2s]: %s: %s", tostring(err), traceback()); end +local function handleerr(err) log("error", "Traceback[c2s]: %s", traceback(tostring(err), 2)); end function stream_callbacks.handlestanza(session, stanza) stanza = session.filter("stanzas/in", stanza); if stanza then diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index 6764e857..0ece23a6 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -429,7 +429,7 @@ function stream_callbacks.error(session, error, data) end end -local function handleerr(err) log("error", "Traceback[s2s]: %s: %s", tostring(err), traceback()); end +local function handleerr(err) log("error", "Traceback[s2s]: %s", traceback(tostring(err), 2)); 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; -- cgit v1.2.3