From 9d5e9921e6945b5dd7e0d70d8e340a74f018f574 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 19 Feb 2011 02:31:06 +0000 Subject: util.stanza: Clean up matching_tags() and replace :childtags() with it --- util/stanza.lua | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/util/stanza.lua b/util/stanza.lua index 7d1f5693..ca79a728 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -138,29 +138,20 @@ function stanza_mt:children() end, self, i; end -function stanza_mt:matching_tags(name, xmlns) +function stanza_mt:childtags(name, xmlns) xmlns = xmlns or self.attr.xmlns; local tags = self.tags; local start_i, max_i = 1, #tags; return function () - for i=start_i,max_i do - v = tags[i]; - if (not name or v.name == name) - and (not xmlns or xmlns == v.attr.xmlns) then - start_i = i+1; - return v; - end + for i = start_i, max_i do + local v = tags[i]; + if (not name or v.name == name) + and (not xmlns or xmlns == v.attr.xmlns) then + start_i = i+1; + return v; end - end, tags, i; -end - -function stanza_mt:childtags() - local i = 0; - return function (a) - i = i + 1 - local v = self.tags[i] - if v then return v; end - end, self.tags[1], i; + end + end; end function stanza_mt:maptags(callback) -- cgit v1.2.3 From 7138d1090a1747232a53761903933ccffd584837 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 14 Jan 2011 15:52:36 +0100 Subject: MUC: Add option to allow participants to change the subject. --- plugins/muc/muc.lib.lua | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 7a069852..8bbf0df2 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -327,6 +327,16 @@ end function room_mt:is_hidden() return self._data.hidden; end +function room_mt:set_changesubject(changesubject) + changesubject = changesubject and true or nil; + if self._data.changesubject ~= changesubject then + self._data.changesubject = changesubject; + if self.save then self:save(true); end + end +end +function room_mt:get_changesubject() + return self._data.changesubject; +end function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc local from, to = stanza.attr.from, stanza.attr.to; @@ -564,6 +574,12 @@ function room_mt:get_form_layout() label = 'Make Room Publicly Searchable?', value = not self:is_hidden() }, + { + name = 'muc#roomconfig_changesubject', + type = 'boolean', + label = 'Allow Occupants to Change Subject?', + value = self:get_changesubject() + }, { name = 'muc#roomconfig_whois', type = 'list-single', @@ -637,6 +653,10 @@ function room_mt:process_form(origin, stanza) local public = fields['muc#roomconfig_publicroom']; dirty = dirty or (self:is_hidden() ~= (not public and true or nil)) + local changesubject = fields['muc#roomconfig_changesubject']; + dirty = dirty or (self:get_changesubject() ~= (not changesubject and true or nil)) + module:log('debug', 'changesubject=%s', changesubject and "true" or "false") + local whois = fields['muc#roomconfig_whois']; if not valid_whois[whois] then origin.send(st.error_reply(stanza, 'cancel', 'bad-request', "Invalid value for 'whois'")); @@ -654,6 +674,7 @@ function room_mt:process_form(origin, stanza) self:set_members_only(membersonly); self:set_persistent(persistent); self:set_hidden(not public); + self:set_changesubject(changesubject); if self.save then self:save(true); end origin.send(st.reply(stanza)); @@ -817,7 +838,8 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha stanza.attr.from = current_nick; local subject = getText(stanza, {"subject"}); if subject then - if occupant.role == "moderator" then + if occupant.role == "moderator" or + ( self._data.changesubject and occupant.role == "participant" ) then -- and participant self:set_subject(current_nick, subject); -- TODO use broadcast_message_stanza else stanza.attr.from = from; -- cgit v1.2.3 From 57e4411584014a9e902d070e3a3944fa824cc6fa Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Fri, 21 Jan 2011 04:35:49 +0500 Subject: util.pluginloader: Add support for multiple plugin directories. --- util/pluginloader.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/util/pluginloader.lua b/util/pluginloader.lua index 31ab1e88..1aedd630 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -6,8 +6,13 @@ -- COPYING file in the source package for more information. -- - -local plugin_dir = CFG_PLUGINDIR or "./plugins/"; +local dir_sep, path_sep = package.config:match("^(%S+)%s(%S+)"); +local plugin_dir = {}; +for path in (CFG_PLUGINDIR or "./plugins/"):gsub("[/\\]", dir_sep):gmatch("[^"..path_sep.."]+") do + path = path..dir_sep; -- add path separator to path end + path = path:gsub(dir_sep..dir_sep.."+", dir_sep); -- coalesce multiple separaters + plugin_dir[#plugin_dir + 1] = path; +end local io_open, os_time = io.open, os.time; local loadstring, pairs = loadstring, pairs; @@ -15,7 +20,11 @@ local loadstring, pairs = loadstring, pairs; module "pluginloader" local function load_file(name) - local file, err = io_open(plugin_dir..name); + local file, err; + for i=1,#plugin_dir do + file, err = io_open(plugin_dir[i]..name); + if file then break; end + end if not file then return file, err; end local content = file:read("*a"); file:close(); -- cgit v1.2.3 From bf0dc06e0fae47f023e76519efd1dbfe2b177410 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Fri, 21 Jan 2011 04:36:31 +0500 Subject: prosody: Add config option plugin_path to allow overriding plugin directories. --- prosody | 1 + 1 file changed, 1 insertion(+) diff --git a/prosody b/prosody index 8dcb0096..48977c30 100755 --- a/prosody +++ b/prosody @@ -184,6 +184,7 @@ function init_global_state() prosody.hosts = hosts; local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data"; + CFG_PLUGINDIR = config.get("*", "core", "plugin_path") or CFG_PLUGINDIR or "plugins" prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR, plugins = CFG_PLUGINDIR, data = data_path }; -- cgit v1.2.3 From 79b099facccb0a60dcf44003c29fb081b90d0404 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 1 Feb 2011 02:32:07 +0000 Subject: TODO: Remove 0.8 milestones --- TODO | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/TODO b/TODO index c0d2b959..203fe70d 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,3 @@ -== 0.8 == -- Ad-hoc commands: - http://code.google.com/p/prosody-modules/wiki/mod_adhoc - http://code.google.com/p/prosody-modules/wiki/mod_adhoc_cmd_admin - http://code.google.com/p/prosody-modules/wiki/mod_adhoc_cmd_ping - http://code.google.com/p/prosody-modules/wiki/mod_adhoc_cmd_uptime - -- Pubsub -- Data storage backend abstraction - == 0.9 == - Clustering -- cgit v1.2.3 From 1dbc76669af58f2f553ca1c639e77a274bb4771a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 1 Feb 2011 02:33:50 +0000 Subject: TODO: Update for 0.9 milestones --- TODO | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index 203fe70d..a49bb52b 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,9 @@ == 0.9 == -- Clustering +- IPv6 +- SASL EXTERNAL +- Roster providers +- Web interface == 1.0 == -- Web interface? +- Clustering - World domination -- cgit v1.2.3 From cda0d3e598bd09e7b7c928ff065babec02e909e8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 10 Feb 2011 21:09:20 +0000 Subject: mod_presence: Bounce errors for invalid presence types (thanks nolan/Astro) --- plugins/mod_presence.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_presence.lua b/plugins/mod_presence.lua index 61239c9a..6d039d83 100644 --- a/plugins/mod_presence.lua +++ b/plugins/mod_presence.lua @@ -203,6 +203,8 @@ function handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_ rostermanager.roster_push(node, host, to_bare); end core_post_stanza(origin, stanza); + else + origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid presence type")); end stanza.attr.from, stanza.attr.to = st_from, st_to; return true; @@ -253,7 +255,9 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b sessionmanager.send_to_interested_resources(node, host, stanza); rostermanager.roster_push(node, host, from_bare); end - end -- discard any other type + else + origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid presence type")); + end stanza.attr.from, stanza.attr.to = st_from, st_to; return true; end @@ -307,6 +311,8 @@ module:hook("presence/bare", function(data) end -- no resources not online, discard elseif not t or t == "unavailable" then handle_normal_presence(origin, stanza); + else + origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid presence type")); end return true; end); -- cgit v1.2.3 From 0f0601ab2dab456fa337f43fd26403cb7f04c5d2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 13 Feb 2011 18:35:39 +0000 Subject: loggingmanager: Iterate over logging config rules using ipairs rather than pairs --- core/loggingmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/loggingmanager.lua b/core/loggingmanager.lua index b637058c..be76681d 100644 --- a/core/loggingmanager.lua +++ b/core/loggingmanager.lua @@ -101,7 +101,7 @@ function apply_sink_rules(sink_type) end end - for _, sink_config in pairs(logging_config) do + for _, sink_config in ipairs(logging_config) do if (type(sink_config) == "table" and sink_config.to == sink_type) then add_rule(sink_config); elseif (type(sink_config) == "string" and sink_config:match("^%*(.+)") == sink_type) then -- cgit v1.2.3 From 31faa98f1425903ce76e85a16ada833e3f1da2a0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 13 Feb 2011 18:37:34 +0000 Subject: loggingmanager: Allow specifying a sink type in per-level logging config (thanks ruskie) --- core/loggingmanager.lua | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/core/loggingmanager.lua b/core/loggingmanager.lua index be76681d..88f2bbbf 100644 --- a/core/loggingmanager.lua +++ b/core/loggingmanager.lua @@ -88,14 +88,20 @@ end function apply_sink_rules(sink_type) if type(logging_config) == "table" then - if sink_type == "file" then - for _, level in ipairs(logging_levels) do - if type(logging_config[level]) == "string" then + for _, level in ipairs(logging_levels) do + if type(logging_config[level]) == "string" then + local value = logging_config[level]; + if sink_type == "file" then + add_rule({ + to = sink_type; + filename = value; + timestamps = true; + levels = { min = level }; + }); + elseif value == "*"..sink_type then add_rule({ - to = "file", - filename = logging_config[level], - timestamps = true, - levels = { min = level }, + to = sink_type; + levels = { min = level }; }); end end -- cgit v1.2.3 From 87b53fccc2608de5657663610186a6a78c7adcce Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 13 Feb 2011 19:28:29 +0000 Subject: prosodyctl, util.prosodyctl: Move UI functions to util.prosodyctl so they can be used outside of prosodyctl itself --- prosodyctl | 85 ++++------------------------------------------------ util/prosodyctl.lua | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 80 deletions(-) diff --git a/prosodyctl b/prosodyctl index 2c31c641..02f2e136 100755 --- a/prosodyctl +++ b/prosodyctl @@ -226,86 +226,11 @@ require "util.prosodyctl" require "socket" ----------------------- -function show_message(msg, ...) - print(msg:format(...)); -end - -function show_warning(msg, ...) - print(msg:format(...)); -end - -function show_usage(usage, desc) - print("Usage: "..arg[0].." "..usage); - if desc then - print(" "..desc); - end -end - -local function getchar(n) - local stty_ret = os.execute("stty raw -echo 2>/dev/null"); - local ok, char; - if stty_ret == 0 then - ok, char = pcall(io.read, n or 1); - os.execute("stty sane"); - else - ok, char = pcall(io.read, "*l"); - if ok then - char = char:sub(1, n or 1); - end - end - if ok then - return char; - end -end - -local function getpass() - local stty_ret = os.execute("stty -echo 2>/dev/null"); - if stty_ret ~= 0 then - io.write("\027[08m"); -- ANSI 'hidden' text attribute - end - local ok, pass = pcall(io.read, "*l"); - if stty_ret == 0 then - os.execute("stty sane"); - else - io.write("\027[00m"); - end - io.write("\n"); - if ok then - return pass; - end -end - -function show_yesno(prompt) - io.write(prompt, " "); - local choice = getchar():lower(); - io.write("\n"); - if not choice:match("%a") then - choice = prompt:match("%[.-(%U).-%]$"); - if not choice then return nil; end - end - return (choice == "y"); -end - -local function read_password() - local password; - while true do - io.write("Enter new password: "); - password = getpass(); - if not password then - show_message("No password - cancelled"); - return; - end - io.write("Retype new password: "); - if getpass() ~= password then - if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then - return; - end - else - break; - end - end - return password; -end +local show_message, show_warning = prosodyctl.show_message, prosodyctl.show_warning; +local show_usage = prosodyctl.show_usage; +local getchar, getpass = prosodyctl.getchar, prosodyctl.getpass; +local show_yesno = prosodyctl.show_yesno; +local read_password = prosodyctl.read_password; local prosodyctl_timeout = (config.get("*", "core", "prosodyctl_timeout") or 5) * 2; ----------------------- diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 40d21be8..aa1850b2 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -15,18 +15,104 @@ local usermanager = require "core.usermanager"; local signal = require "util.signal"; local set = require "util.set"; local lfs = require "lfs"; +local pcall = pcall; local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep; local io, os = io, os; +local print = print; local tostring, tonumber = tostring, tonumber; local CFG_SOURCEDIR = _G.CFG_SOURCEDIR; +local _G = _G; local prosody = prosody; module "prosodyctl" +-- UI helpers +function show_message(msg, ...) + print(msg:format(...)); +end + +function show_warning(msg, ...) + print(msg:format(...)); +end + +function show_usage(usage, desc) + print("Usage: ".._G.arg[0].." "..usage); + if desc then + print(" "..desc); + end +end + +function getchar(n) + local stty_ret = os.execute("stty raw -echo 2>/dev/null"); + local ok, char; + if stty_ret == 0 then + ok, char = pcall(io.read, n or 1); + os.execute("stty sane"); + else + ok, char = pcall(io.read, "*l"); + if ok then + char = char:sub(1, n or 1); + end + end + if ok then + return char; + end +end + +function getpass() + local stty_ret = os.execute("stty -echo 2>/dev/null"); + if stty_ret ~= 0 then + io.write("\027[08m"); -- ANSI 'hidden' text attribute + end + local ok, pass = pcall(io.read, "*l"); + if stty_ret == 0 then + os.execute("stty sane"); + else + io.write("\027[00m"); + end + io.write("\n"); + if ok then + return pass; + end +end + +function show_yesno(prompt) + io.write(prompt, " "); + local choice = getchar():lower(); + io.write("\n"); + if not choice:match("%a") then + choice = prompt:match("%[.-(%U).-%]$"); + if not choice then return nil; end + end + return (choice == "y"); +end + +function read_password() + local password; + while true do + io.write("Enter new password: "); + password = getpass(); + if not password then + show_message("No password - cancelled"); + return; + end + io.write("Retype new password: "); + if getpass() ~= password then + if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then + return; + end + else + break; + end + end + return password; +end + +-- Server control function adduser(params) local user, host, password = nodeprep(params.user), nameprep(params.host), params.password; if not user then -- cgit v1.2.3 From 78b3e4e756687dac5b747f4a4e9cf83091b9f9de Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 20 Feb 2011 19:16:56 +0500 Subject: util.stanza: Rewrite clone() to be more optimized. --- util/stanza.lua | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/util/stanza.lua b/util/stanza.lua index ca79a728..de83977f 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -320,24 +320,21 @@ function deserialize(stanza) return stanza; end -function clone(stanza) - local lookup_table = {}; - local function _copy(object) - if type(object) ~= "table" then - return object; - elseif lookup_table[object] then - return lookup_table[object]; +local function _clone(stanza) + local attr, tags = {}, {}; + for k,v in pairs(stanza.attr) do attr[k] = v; end + local new = { name = stanza.name, attr = attr, tags = tags }; + for i=1,#stanza do + local child = stanza[i]; + if child.name then + child = _clone(child); + t_insert(tags, child); end - local new_table = {}; - lookup_table[object] = new_table; - for index, value in pairs(object) do - new_table[_copy(index)] = _copy(value); - end - return setmetatable(new_table, getmetatable(object)); + t_insert(new, child); end - - return _copy(stanza) + return setmetatable(new, stanza_mt); end +clone = _clone; function message(attr, body) if not body then -- cgit v1.2.3 From e8e82713fe030d88912c6962e620de54121223de Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 20 Feb 2011 20:01:05 +0500 Subject: util.pluginloader: Return full file path from internal file loader on success, not just the name. --- util/pluginloader.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util/pluginloader.lua b/util/pluginloader.lua index 1aedd630..d9553537 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -20,15 +20,16 @@ local loadstring, pairs = loadstring, pairs; module "pluginloader" local function load_file(name) - local file, err; + local file, err, path; for i=1,#plugin_dir do - file, err = io_open(plugin_dir[i]..name); + path = plugin_dir[i]..name; + file, err = io_open(path); if file then break; end end if not file then return file, err; end local content = file:read("*a"); file:close(); - return content, name; + return content, path; end function load_resource(plugin, resource, loader) -- cgit v1.2.3 From 55f906ce99f7c73cf4ccb4084ddf89ee85f97270 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 20 Feb 2011 20:03:43 +0500 Subject: util.pluginloader: Return file path on success in pluginloader.load_code(). --- util/pluginloader.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util/pluginloader.lua b/util/pluginloader.lua index d9553537..9300b98a 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -62,7 +62,10 @@ end function load_code(plugin, resource) local content, err = load_resource(plugin, resource); if not content then return content, err; end - return loadstring(content, "@"..err); + local path = err; + local f, err = loadstring(content, "@"..path); + if not f then return f, err; end + return f, path; end return _M; -- cgit v1.2.3 From e55a81dfae6b0033938e30cd466e203e1aaa895a Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 20 Feb 2011 20:06:38 +0500 Subject: modulemanager: Added module.path to the plugin API to let plugins determine their load path. --- core/modulemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index e8f76aeb..07a2b1c9 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -117,7 +117,7 @@ function load(host, module_name, config) end local _log = logger.init(host..":"..module_name); - local api_instance = setmetatable({ name = module_name, host = host, config = config, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); + local api_instance = setmetatable({ name = module_name, host = host, path = err, config = config, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); api_instance.environment = pluginenv; -- cgit v1.2.3 From f80ca4e6a8556e45571ca866c0efd06350e1f930 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 20 Feb 2011 20:11:52 +0500 Subject: util.pluginloader: Remove unused support for custom loaders, to simplify further refactoring. --- util/pluginloader.lua | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/util/pluginloader.lua b/util/pluginloader.lua index 9300b98a..3a4e8003 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -32,28 +32,24 @@ local function load_file(name) return content, path; end -function load_resource(plugin, resource, loader) +function load_resource(plugin, resource) local path, name = plugin:match("([^/]*)/?(.*)"); if name == "" then if not resource then resource = "mod_"..plugin..".lua"; end - loader = loader or load_file; - local content, err = loader(plugin.."/"..resource); - if not content then content, err = loader(resource); end - -- TODO add support for packed plugins + local content, err = load_file(plugin.."/"..resource); + if not content then content, err = load_file(resource); end return content, err; else if not resource then resource = "mod_"..name..".lua"; end - loader = loader or load_file; - local content, err = loader(plugin.."/"..resource); - if not content then content, err = loader(path.."/"..resource); end - -- TODO add support for packed plugins + local content, err = load_file(plugin.."/"..resource); + if not content then content, err = load_file(path.."/"..resource); end return content, err; end -- cgit v1.2.3 From 297144b6a75ba8411f73135ca94b0f6a7320efc7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 22 Feb 2011 07:37:52 +0000 Subject: util.stanza: Remove childtags and rename matching_tags -> childtags (they are API-compatible) --- util/stanza.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/stanza.lua b/util/stanza.lua index de83977f..4c2a7143 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -4,7 +4,7 @@ -- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. --- +-- local t_insert = table.insert; -- cgit v1.2.3 From d663facee9479c36b3f883253db7b19c3ae4f92e Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 22 Feb 2011 21:47:38 +0500 Subject: util.pluginloader: Rewritten resource loading to be cleaner, and added support for prosody-modules directory layout. "/" in plugin names is no longer supported. --- util/pluginloader.lua | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/util/pluginloader.lua b/util/pluginloader.lua index 3a4e8003..555e41bf 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -19,40 +19,33 @@ local loadstring, pairs = loadstring, pairs; module "pluginloader" -local function load_file(name) +local function load_file(names) local file, err, path; for i=1,#plugin_dir do - path = plugin_dir[i]..name; - file, err = io_open(path); - if file then break; end + for j=1,#names do + path = plugin_dir[i]..names[j]; + file, err = io_open(path); + if file then + local content = file:read("*a"); + file:close(); + return content, path; + end + end end - if not file then return file, err; end - local content = file:read("*a"); - file:close(); - return content, path; + return file, err; end function load_resource(plugin, resource) - local path, name = plugin:match("([^/]*)/?(.*)"); - if name == "" then - if not resource then - resource = "mod_"..plugin..".lua"; - end + resource = resource or "mod_"..plugin..".lua"; - local content, err = load_file(plugin.."/"..resource); - if not content then content, err = load_file(resource); end - - return content, err; - else - if not resource then - resource = "mod_"..name..".lua"; - end + local names = { + "mod_"..plugin.."/"..plugin.."/"..resource; -- mod_hello/hello/mod_hello.lua + "mod_"..plugin.."/"..resource; -- mod_hello/mod_hello.lua + plugin.."/"..resource; -- hello/mod_hello.lua + resource; -- mod_hello.lua + }; - local content, err = load_file(plugin.."/"..resource); - if not content then content, err = load_file(path.."/"..resource); end - - return content, err; - end + return load_file(names); end function load_code(plugin, resource) -- cgit v1.2.3 From 65c179b10dd6fb410a4aa4747993e4ff68eca3f8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 22 Feb 2011 18:02:23 +0000 Subject: prosody: Change plugin_path -> plugin_paths and make it an array instead of a string --- prosody | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prosody b/prosody index 48977c30..81d87153 100755 --- a/prosody +++ b/prosody @@ -184,7 +184,10 @@ function init_global_state() prosody.hosts = hosts; local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data"; - CFG_PLUGINDIR = config.get("*", "core", "plugin_path") or CFG_PLUGINDIR or "plugins" + local custom_plugin_paths = config.get("*", "core", "plugin_paths"); + if custom_plugin_paths then + CFG_PLUGINDIR = table.concat(custom_plugin_paths, package.config:sub(3,3)); -- path1;path2;path3 + end prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR, plugins = CFG_PLUGINDIR, data = data_path }; -- cgit v1.2.3 From e5dfde383ea5d729994b38ac221de66db3a93eb8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 22 Feb 2011 18:27:31 +0000 Subject: s2smanager: Drop some log messages to debug level from info/warn --- core/s2smanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 04ea459d..fd9a72d0 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -166,7 +166,7 @@ function new_incoming(conn) return; -- Ok, we're connect[ed|ing] end -- Not connected, need to close session and clean up - (session.log or log)("warn", "Destroying incomplete session %s->%s due to inactivity", + (session.log or log)("debug", "Destroying incomplete session %s->%s due to inactivity", session.from_host or "(unknown)", session.to_host or "(unknown)"); session:close("connection-timeout"); end); @@ -588,7 +588,7 @@ end function destroy_session(session, reason) if session.destroyed then return; end - (session.log or log)("info", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host)); + (session.log or log)("debug", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host)); if session.direction == "outgoing" then hosts[session.from_host].s2sout[session.to_host] = nil; -- cgit v1.2.3 From 2f3b7c048e142c1caec18e2ec65ad3ce57c2274a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 22 Feb 2011 18:29:35 +0000 Subject: mod_tls: Drop 'TLS negotiation started for ...' to debug level from info --- plugins/mod_tls.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index c79227e1..52e3512d 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -47,7 +47,7 @@ module:hook("stanza/urn:ietf:params:xml:ns:xmpp-tls:starttls", function(event) local host = origin.to_host or origin.host; local ssl_ctx = host and hosts[host].ssl_ctx_in or global_ssl_ctx; origin.conn:starttls(ssl_ctx); - origin.log("info", "TLS negotiation started for %s...", origin.type); + origin.log("debug", "TLS negotiation started for %s...", origin.type); origin.secure = false; else origin.log("warn", "Attempt to start TLS, but TLS is not available on this %s connection", origin.type); -- cgit v1.2.3 From 933cd7fc672c2eff37b76043cedfd438afc46ca8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 22 Feb 2011 18:37:29 +0000 Subject: prosodyctl: Support for plugin_paths config option --- prosodyctl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prosodyctl b/prosodyctl index 02f2e136..2005a3ed 100755 --- a/prosodyctl +++ b/prosodyctl @@ -110,6 +110,10 @@ local original_logging_config = config.get("*", "core", "log"); config.set("*", "core", "log", { { levels = { min="info" }, to = "console" } }); local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data"; +local custom_plugin_paths = config.get("*", "core", "plugin_paths"); +if custom_plugin_paths then + CFG_PLUGINDIR = table.concat(custom_plugin_paths, package.config:sub(3,3)); -- path1;path2;path3 +end prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR, plugins = CFG_PLUGINDIR, data = data_path }; -- cgit v1.2.3 From a486ce828ec6431e701b7d4fbb9e3ad1ceda48e3 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 23 Feb 2011 01:22:04 +0500 Subject: mod_auth_cyrus: Print some diagnostic log messages about the available mechanisms. --- plugins/mod_auth_cyrus.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/mod_auth_cyrus.lua b/plugins/mod_auth_cyrus.lua index ed3d5408..2a52c8b5 100644 --- a/plugins/mod_auth_cyrus.lua +++ b/plugins/mod_auth_cyrus.lua @@ -27,6 +27,19 @@ local new_sasl = function(realm) ); end +do -- diagnostic + local realm = module:get_option("sasl_realm") or module.host; + local list; + for mechanism in pairs(new_sasl(realm):mechanisms()) do + list = (not(list) and mechanism) or (list..", "..mechanism); + end + if not list then + module:log("error", "No Cyrus SASL mechanisms available"); + else + module:log("debug", "Available Cyrus SASL mechanisms: %s", list); + end +end + function new_default_provider(host) local provider = { name = "cyrus" }; log("debug", "initializing default authentication provider for host '%s'", host); -- cgit v1.2.3 From b3cc9f42dfe336960903dc0f6a52e3606186543e Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 23 Feb 2011 01:34:46 +0500 Subject: mod_auth_*: Get rid of undocumented and broken 'sasl_realm' config option. --- plugins/mod_auth_anonymous.lua | 3 +-- plugins/mod_auth_cyrus.lua | 6 ++---- plugins/mod_auth_internal_hashed.lua | 3 +-- plugins/mod_auth_internal_plain.lua | 3 +-- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/plugins/mod_auth_anonymous.lua b/plugins/mod_auth_anonymous.lua index 9d0896e5..8d790508 100644 --- a/plugins/mod_auth_anonymous.lua +++ b/plugins/mod_auth_anonymous.lua @@ -34,13 +34,12 @@ function new_default_provider(host) end function provider.get_sasl_handler() - local realm = module:get_option("sasl_realm") or module.host; local anonymous_authentication_profile = { anonymous = function(sasl, username, realm) return true; -- for normal usage you should always return true here end }; - return new_sasl(realm, anonymous_authentication_profile); + return new_sasl(module.host, anonymous_authentication_profile); end return provider; diff --git a/plugins/mod_auth_cyrus.lua b/plugins/mod_auth_cyrus.lua index 2a52c8b5..447fae51 100644 --- a/plugins/mod_auth_cyrus.lua +++ b/plugins/mod_auth_cyrus.lua @@ -28,9 +28,8 @@ local new_sasl = function(realm) end do -- diagnostic - local realm = module:get_option("sasl_realm") or module.host; local list; - for mechanism in pairs(new_sasl(realm):mechanisms()) do + for mechanism in pairs(new_sasl(module.host):mechanisms()) do list = (not(list) and mechanism) or (list..", "..mechanism); end if not list then @@ -68,8 +67,7 @@ function new_default_provider(host) end function provider.get_sasl_handler() - local realm = module:get_option("sasl_realm") or module.host; - local handler = new_sasl(realm); + local handler = new_sasl(module.host); if require_provisioning then function handler.require_provisioning(username) return usermanager_user_exists(username, module.host); diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 3ce8c076..ee810426 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -140,7 +140,6 @@ function new_hashpass_provider(host) end function provider.get_sasl_handler() - local realm = module:get_option("sasl_realm") or module.host; local testpass_authentication_profile = { plain_test = function(sasl, username, password, realm) local prepped_username = nodeprep(username); @@ -175,7 +174,7 @@ function new_hashpass_provider(host) return stored_key, server_key, iteration_count, salt, true; end }; - return new_sasl(realm, testpass_authentication_profile); + return new_sasl(module.host, testpass_authentication_profile); end return provider; diff --git a/plugins/mod_auth_internal_plain.lua b/plugins/mod_auth_internal_plain.lua index ccaf8381..784553ea 100644 --- a/plugins/mod_auth_internal_plain.lua +++ b/plugins/mod_auth_internal_plain.lua @@ -68,7 +68,6 @@ function new_default_provider(host) end function provider.get_sasl_handler() - local realm = module:get_option("sasl_realm") or module.host; local getpass_authentication_profile = { plain = function(sasl, username, realm) local prepped_username = nodeprep(username); @@ -83,7 +82,7 @@ function new_default_provider(host) return password, true; end }; - return new_sasl(realm, getpass_authentication_profile); + return new_sasl(module.host, getpass_authentication_profile); end return provider; -- cgit v1.2.3 From fc6149a695b65875580ff0a474af0748fc519d86 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 23 Feb 2011 02:03:55 +0500 Subject: net.httpserver: Catch errors thrown in HTTP handlers. --- net/httpserver.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/httpserver.lua b/net/httpserver.lua index 33ced072..6d6408f7 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -17,6 +17,8 @@ local listener; local t_insert, t_concat = table.insert, table.concat; local tonumber, tostring, pairs, ipairs, type = tonumber, tostring, pairs, ipairs, type; +local xpcall = xpcall; +local debug_traceback = debug.traceback; local urlencode = function (s) return s and (s:gsub("%W", function (c) return ("%%%02x"):format(c:byte()); end)); end @@ -86,6 +88,12 @@ local function call_callback(request, err) callback = (request.server and request.server.handlers[base]) or default_handler; end if callback then + local _callback = callback; + function callback(a, b, c) + local status, result = xpcall(function() _callback(a, b, c) end, debug_traceback); + if status then return result; end + log("error", "Error in HTTP server handler: %s", result); + end if err then log("debug", "Request error: "..err); if not callback(nil, err, request) then -- cgit v1.2.3 From 0a2b87128fab3220d4d257692287df12fb040df2 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 23 Feb 2011 02:16:19 +0500 Subject: tools/migration/*: Initial commit of a new migration tool. Currently supports Prosody files and Prosody SQL as input and output. --- tools/migration/config.lua | 26 ++++++ tools/migration/main.lua | 27 ++++++ tools/migration/mtools.lua | 56 ++++++++++++ tools/migration/prosody_files.lua | 133 +++++++++++++++++++++++++++++ tools/migration/prosody_sql.lua | 174 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 416 insertions(+) create mode 100644 tools/migration/config.lua create mode 100644 tools/migration/main.lua create mode 100644 tools/migration/mtools.lua create mode 100644 tools/migration/prosody_files.lua create mode 100644 tools/migration/prosody_sql.lua diff --git a/tools/migration/config.lua b/tools/migration/config.lua new file mode 100644 index 00000000..199b8207 --- /dev/null +++ b/tools/migration/config.lua @@ -0,0 +1,26 @@ +input { + type = "prosody_sql"; + driver = "SQLite3"; + database = "out.sqlite"; +} +output { + type = "prosody_files"; + path = "out"; +} + +--[[ + +input { + path = "../../data"; + type = "prosody_files"; + driver = "SQLite3"; + database = "../../prosody.sqlite"; +} +output { + type = "prosody_sql"; + driver = "SQLite3"; + database = "out.sqlite"; + path = "out"; +} + +]] diff --git a/tools/migration/main.lua b/tools/migration/main.lua new file mode 100644 index 00000000..42e9babe --- /dev/null +++ b/tools/migration/main.lua @@ -0,0 +1,27 @@ + + + +local function loadfilein(file, env) return loadin and loadin(env, io.open(file):read("*a")) or setfenv(loadfile(file), env); end +config = {}; +local config_env = setmetatable({}, { __index = function(t, k) return function(tbl) config[k] = tbl; end; end }); +loadfilein("config.lua", config_env)(); + +package.path = "../../?.lua;"..package.path +package.cpath = "../../?.dll;"..package.cpath + + +assert(config.input, "no input specified") +assert(config.output, "no output specified") +local itype = assert(config.input.type, "no input.type specified"); +local otype = assert(config.output.type, "no output.type specified"); +local reader = require(itype).reader(config.input); +local writer = require(otype).writer(config.output); + +local json = require "util.json"; + +for x in reader do + --print(json.encode(x)) + writer(x); +end +writer(nil); -- close + diff --git a/tools/migration/mtools.lua b/tools/migration/mtools.lua new file mode 100644 index 00000000..d2bd1e7a --- /dev/null +++ b/tools/migration/mtools.lua @@ -0,0 +1,56 @@ + + +local print = print; +local t_insert = table.insert; +local t_sort = table.sort; + +module "mtools" + +function sorted(params) + + local reader = params.reader; -- iterator to get items from + local sorter = params.sorter; -- sorting function + local filter = params.filter; -- filter function + + local cache = {}; + for item in reader do + if filter then item = filter(item); end + if item then t_insert(cache, item); end + end + if sorter then + t_sort(cache, sorter); + end + local i = 0; + return function() + i = i + 1; + return cache[i]; + end; + +end + +function merged(reader, merger) + + local item1 = reader(); + local merged = { item1 }; + return function() + while true do + if not item1 then return nil; end + local item2 = reader(); + if not item2 then item1 = nil; return merged; end + if merger(item1, item2) then + --print("merged") + item1 = item2; + t_insert(merged, item1); + else + --print("unmerged", merged) + item1 = item2; + local tmp = merged; + merged = { item1 }; + return tmp; + end + end + end; + +end + +return _M; diff --git a/tools/migration/prosody_files.lua b/tools/migration/prosody_files.lua new file mode 100644 index 00000000..d9c2dd45 --- /dev/null +++ b/tools/migration/prosody_files.lua @@ -0,0 +1,133 @@ + +local print = print; +local assert = assert; +local setmetatable = setmetatable; +local tonumber = tonumber; +local char = string.char; +local coroutine = coroutine; +local lfs = require "lfs"; +local loadfile = loadfile; +local setfenv = setfenv; +local pcall = pcall; +local mtools = require "mtools"; +local next = next; +local pairs = pairs; +local json = require "util.json"; + +prosody = {}; +local dm = require "util.datamanager" + +module "prosody_files" + +local function is_dir(path) return lfs.attributes(path, "mode") == "directory"; end +local function is_file(path) return lfs.attributes(path, "mode") == "file"; end +local function clean_path(path) + return path:gsub("\\", "/"):gsub("//+", "/"); +end +local encode, decode; do + local urlcodes = setmetatable({}, { __index = function (t, k) t[k] = char(tonumber("0x"..k)); return t[k]; end }); + decode = function (s) return s and (s:gsub("+", " "):gsub("%%([a-fA-F0-9][a-fA-F0-9])", urlcodes)); end + encode = function (s) return s and (s:gsub("%W", function (c) return format("%%%02x", c:byte()); end)); end +end +local function decode_dir(x) + if x:gsub("%%%x%x", ""):gsub("[a-zA-Z0-9]", "") == "" then + return decode(x); + end +end +local function decode_file(x) + if x:match(".%.dat$") and x:gsub("%.dat$", ""):gsub("%%%x%x", ""):gsub("[a-zA-Z0-9]", "") == "" then + return decode(x:gsub("%.dat$", "")); + end +end +local function prosody_dir(path, ondir, onfile, ...) + for x in lfs.dir(path) do + local xpath = path.."/"..x; + if decode_dir(x) and is_dir(xpath) then + ondir(xpath, x, ...); + elseif decode_file(x) and is_file(xpath) then + onfile(xpath, x, ...); + end + end +end + +local function handle_root_file(path, name) + --print("root file: ", decode_file(name)) + coroutine.yield { user = nil, host = nil, store = decode_file(name) }; +end +local function handle_host_file(path, name, host) + --print("host file: ", decode_dir(host).."/"..decode_file(name)) + coroutine.yield { user = nil, host = decode_dir(host), store = decode_file(name) }; +end +local function handle_store_file(path, name, store, host) + --print("store file: ", decode_file(name).."@"..decode_dir(host).."/"..decode_dir(store)) + coroutine.yield { user = decode_file(name), host = decode_dir(host), store = decode_dir(store) }; +end +local function handle_host_store(path, name, host) + prosody_dir(path, function() end, handle_store_file, name, host); +end +local function handle_host_dir(path, name) + prosody_dir(path, handle_host_store, handle_host_file, name); +end +local function handle_root_dir(path) + prosody_dir(path, handle_host_dir, handle_root_file); +end + +local function decode_user(item) + local userdata = { + user = item[1].user; + host = item[1].host; + stores = {}; + }; + for i=1,#item do -- loop over stores + local result = {}; + local store = item[i]; + userdata.stores[store.store] = store.data; + store.user = nil; store.host = nil; store.store = nil; + end + return userdata; +end + +function reader(input) + local path = clean_path(assert(input.path, "no input.path specified")); + assert(is_dir(path), "input.path is not a directory"); + local iter = coroutine.wrap(function()handle_root_dir(path);end); + -- get per-user stores, sorted + local iter = mtools.sorted { + reader = function() + local x = iter(); + if x then + dm.set_data_path(path); + x.data = assert(dm.load(x.user, x.host, x.store)); + return x; + end + end; + sorter = function(a, b) + local a_host, a_user, a_store = a.host or "", a.user or "", a.store or ""; + local b_host, b_user, b_store = b.host or "", b.user or "", b.store or ""; + return a_host > b_host or (a_host==b_host and a_user > b_user) or (a_host==b_host and a_user==b_user and a_store > b_store); + end; + }; + -- merge stores to get users + iter = mtools.merged(iter, function(a, b) + return (a.host == b.host and a.user == b.user); + end); + + return function() + local x = iter(); + return x and decode_user(x); + end +end + +function writer(output) + local path = clean_path(assert(output.path, "no output.path specified")); + assert(is_dir(path), "output.path is not a directory"); + return function(item) + if not item then return; end -- end of input + dm.set_data_path(path); + for store, data in pairs(item.stores) do + assert(dm.store(item.user, item.host, store, data)); + end + end +end + +return _M; diff --git a/tools/migration/prosody_sql.lua b/tools/migration/prosody_sql.lua new file mode 100644 index 00000000..56990f4a --- /dev/null +++ b/tools/migration/prosody_sql.lua @@ -0,0 +1,174 @@ + +local assert = assert; +local DBI = require "DBI"; +local print = print; +local type = type; +local next = next; +local pairs = pairs; +local t_sort = table.sort; +local json = require "util.json"; +local mtools = require "mtools"; +local tostring = tostring; +local tonumber = tonumber; + +module "prosody_sql" + +local function create_table(connection, params) + 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("`", "\""); + end + + local stmt = connection:prepare(create_sql); + if stmt then + local ok = stmt:execute(); + local commit_ok = connection:commit(); + if ok and commit_ok then + 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 stmt, err = connection:prepare(index_sql); + local ok, commit_ok, commit_err; + if stmt then + ok, err = assert(stmt:execute()); + commit_ok, commit_err = assert(connection:commit()); + end + end + 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.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 function decode_user(item) + local userdata = { + user = item[1][1].user; + host = item[1][1].host; + stores = {}; + }; + for i=1,#item do -- loop over stores + local result = {}; + local store = item[i]; + for i=1,#store do -- loop over store data + local row = store[i]; + local k = row.key; + local v = deserialize(row.type, row.value); + 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 + userdata.stores[store[1].store] = result; + end + end + return userdata; +end + +function reader(input) + local dbh = assert(DBI.Connect( + assert(input.driver, "no input.driver specified"), + assert(input.database, "no input.database specified"), + input.username, input.password, + input.host, input.port + )); + assert(dbh:ping()); + local stmt = assert(dbh:prepare("SELECT * FROM prosody")); + assert(stmt:execute()); + local keys = {"host", "user", "store", "key", "type", "value"}; + local f,s,val = stmt:rows(true); + -- get SQL rows, sorted + local iter = mtools.sorted { + reader = function() val = f(s, val); return val; end; + filter = function(x) + for i=1,#keys do + if not x[keys[i]] then return false; end -- TODO log error, missing field + end + if x.host == "" then x.host = nil; end + if x.user == "" then x.user = nil; end + if x.store == "" then x.store = nil; end + return x; + end; + sorter = function(a, b) + local a_host, a_user, a_store = a.host or "", a.user or "", a.store or ""; + local b_host, b_user, b_store = b.host or "", b.user or "", b.store or ""; + return a_host > b_host or (a_host==b_host and a_user > b_user) or (a_host==b_host and a_user==b_user and a_store > b_store); + end; + }; + -- merge rows to get stores + iter = mtools.merged(iter, function(a, b) + return (a.host == b.host and a.user == b.user and a.store == b.store); + end); + -- merge stores to get users + iter = mtools.merged(iter, function(a, b) + return (a[1].host == b[1].host and a[1].user == b[1].user); + end); + return function() + local x = iter(); + return x and decode_user(x); + end; +end + +function writer(output, iter) + local dbh = assert(DBI.Connect( + assert(output.driver, "no output.driver specified"), + assert(output.database, "no output.database specified"), + output.username, output.password, + output.host, output.port + )); + assert(dbh:ping()); + create_table(dbh, output); + local stmt = assert(dbh:prepare("SELECT * FROM prosody")); + assert(stmt:execute()); + local stmt = assert(dbh:prepare("DELETE FROM prosody")); + assert(stmt:execute()); + local insert = assert(dbh:prepare("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)")); + + return function(item) + if not item then assert(dbh:commit()) return dbh:close(); end -- end of input + local host = item.host or ""; + local user = item.user or ""; + for store, data in pairs(item.stores) do + -- TODO transactions + local extradata = {}; + for key, value in pairs(data) do + if type(key) == "string" and key ~= "" then + local t, value = assert(serialize(value)); + local ok, err = assert(insert:execute(host, user, store, key, t, value)); + else + extradata[key] = value; + end + end + if next(extradata) ~= nil then + local t, extradata = assert(serialize(extradata)); + local ok, err = assert(insert:execute(host, user, store, "", t, extradata)); + end + end + end; +end + + +return _M; -- cgit v1.2.3 From aded42ab978d3cf76e638fbf69ad2c332c4cefe9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 22 Feb 2011 21:19:00 +0000 Subject: tests/test_sasl.lua: Convert literal UTF-8/Latin1 chars to escape codes for weak text editors --- tests/test_sasl.lua | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/test_sasl.lua b/tests/test_sasl.lua index 8bd1a2ff..271fa69a 100644 --- a/tests/test_sasl.lua +++ b/tests/test_sasl.lua @@ -6,13 +6,6 @@ -- COPYING file in the source package for more information. -- - ---- WARNING! --- --- This file contains a mix of encodings below. --- Many editors will unquestioningly convert these for you. --- Please be careful :( (I recommend Scite) ---------------------------------- - local gmatch = string.gmatch; local t_concat, t_insert = table.concat, table.insert; local to_byte, to_char = string.byte, string.char; @@ -41,5 +34,5 @@ function latin1toutf8() assert_utf8("", "") assert_utf8("test", "test") assert_utf8(nil, nil) - assert_utf8("foobar.råkat.se", "foobar.rÃ¥kat.se") + assert_utf8("foobar.r\229kat.se", "foobar.r\195\165kat.se") end -- cgit v1.2.3 From e3d06ad37002b110f1c37ae46a80f3e707be4216 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 23 Feb 2011 00:31:12 +0000 Subject: net.httpserver: Fix HTTP after commit c299726d2b4e and add a 500 error response if a request handler fails to make a response to the client --- net/httpserver.lua | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/net/httpserver.lua b/net/httpserver.lua index 6d6408f7..36c04c3f 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -89,10 +89,20 @@ local function call_callback(request, err) end if callback then local _callback = callback; - function callback(a, b, c) - local status, result = xpcall(function() _callback(a, b, c) end, debug_traceback); - if status then return result; end + function callback(method, body, request) + local ok, result = xpcall(function() return _callback(method, body, request) end, debug_traceback); + if ok then return result; end log("error", "Error in HTTP server handler: %s", result); + -- TODO: When we support pipelining, request.destroyed + -- won't be the right flag - we just want to see if there + -- has been a response to this request yet. + if not request.destroyed then + return { + status = "500 Internal Server Error"; + headers = { ["Content-Type"] = "text/plain" }; + body = "There was an error processing your request. See the error log for more details."; + }; + end end if err then log("debug", "Request error: "..err); -- cgit v1.2.3 From 6bac77b06f0e115e95ea2e4dbb51db1cfe94d954 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 23 Feb 2011 07:26:54 +0500 Subject: net.httpserver: Removed an unused function. --- net/httpserver.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/httpserver.lua b/net/httpserver.lua index 36c04c3f..e0fd3f86 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -30,10 +30,6 @@ module "httpserver" local default_handler; -local function expectbody(reqt) - return reqt.method == "POST"; -end - local function send_response(request, response) -- Write status line local resp; -- cgit v1.2.3 From ba01859e1ce26869d0871f891a686e3c89ceaf44 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 23 Feb 2011 18:31:48 +0000 Subject: tools/migration/*.lua: Convert to unix line endings --- tools/migration/config.lua | 52 +++--- tools/migration/main.lua | 54 +++--- tools/migration/mtools.lua | 112 ++++++------ tools/migration/prosody_files.lua | 266 ++++++++++++++--------------- tools/migration/prosody_sql.lua | 348 +++++++++++++++++++------------------- 5 files changed, 416 insertions(+), 416 deletions(-) diff --git a/tools/migration/config.lua b/tools/migration/config.lua index 199b8207..3f2057f0 100644 --- a/tools/migration/config.lua +++ b/tools/migration/config.lua @@ -1,26 +1,26 @@ -input { - type = "prosody_sql"; - driver = "SQLite3"; - database = "out.sqlite"; -} -output { - type = "prosody_files"; - path = "out"; -} - ---[[ - -input { - path = "../../data"; - type = "prosody_files"; - driver = "SQLite3"; - database = "../../prosody.sqlite"; -} -output { - type = "prosody_sql"; - driver = "SQLite3"; - database = "out.sqlite"; - path = "out"; -} - -]] +input { + type = "prosody_sql"; + driver = "SQLite3"; + database = "out.sqlite"; +} +output { + type = "prosody_files"; + path = "out"; +} + +--[[ + +input { + path = "../../data"; + type = "prosody_files"; + driver = "SQLite3"; + database = "../../prosody.sqlite"; +} +output { + type = "prosody_sql"; + driver = "SQLite3"; + database = "out.sqlite"; + path = "out"; +} + +]] diff --git a/tools/migration/main.lua b/tools/migration/main.lua index 42e9babe..a88da21e 100644 --- a/tools/migration/main.lua +++ b/tools/migration/main.lua @@ -1,27 +1,27 @@ - - - -local function loadfilein(file, env) return loadin and loadin(env, io.open(file):read("*a")) or setfenv(loadfile(file), env); end -config = {}; -local config_env = setmetatable({}, { __index = function(t, k) return function(tbl) config[k] = tbl; end; end }); -loadfilein("config.lua", config_env)(); - -package.path = "../../?.lua;"..package.path -package.cpath = "../../?.dll;"..package.cpath - - -assert(config.input, "no input specified") -assert(config.output, "no output specified") -local itype = assert(config.input.type, "no input.type specified"); -local otype = assert(config.output.type, "no output.type specified"); -local reader = require(itype).reader(config.input); -local writer = require(otype).writer(config.output); - -local json = require "util.json"; - -for x in reader do - --print(json.encode(x)) - writer(x); -end -writer(nil); -- close - + + + +local function loadfilein(file, env) return loadin and loadin(env, io.open(file):read("*a")) or setfenv(loadfile(file), env); end +config = {}; +local config_env = setmetatable({}, { __index = function(t, k) return function(tbl) config[k] = tbl; end; end }); +loadfilein("config.lua", config_env)(); + +package.path = "../../?.lua;"..package.path +package.cpath = "../../?.dll;"..package.cpath + + +assert(config.input, "no input specified") +assert(config.output, "no output specified") +local itype = assert(config.input.type, "no input.type specified"); +local otype = assert(config.output.type, "no output.type specified"); +local reader = require(itype).reader(config.input); +local writer = require(otype).writer(config.output); + +local json = require "util.json"; + +for x in reader do + --print(json.encode(x)) + writer(x); +end +writer(nil); -- close + diff --git a/tools/migration/mtools.lua b/tools/migration/mtools.lua index d2bd1e7a..e7b774bb 100644 --- a/tools/migration/mtools.lua +++ b/tools/migration/mtools.lua @@ -1,56 +1,56 @@ - - -local print = print; -local t_insert = table.insert; -local t_sort = table.sort; - -module "mtools" - -function sorted(params) - - local reader = params.reader; -- iterator to get items from - local sorter = params.sorter; -- sorting function - local filter = params.filter; -- filter function - - local cache = {}; - for item in reader do - if filter then item = filter(item); end - if item then t_insert(cache, item); end - end - if sorter then - t_sort(cache, sorter); - end - local i = 0; - return function() - i = i + 1; - return cache[i]; - end; - -end - -function merged(reader, merger) - - local item1 = reader(); - local merged = { item1 }; - return function() - while true do - if not item1 then return nil; end - local item2 = reader(); - if not item2 then item1 = nil; return merged; end - if merger(item1, item2) then - --print("merged") - item1 = item2; - t_insert(merged, item1); - else - --print("unmerged", merged) - item1 = item2; - local tmp = merged; - merged = { item1 }; - return tmp; - end - end - end; - -end - -return _M; + + +local print = print; +local t_insert = table.insert; +local t_sort = table.sort; + +module "mtools" + +function sorted(params) + + local reader = params.reader; -- iterator to get items from + local sorter = params.sorter; -- sorting function + local filter = params.filter; -- filter function + + local cache = {}; + for item in reader do + if filter then item = filter(item); end + if item then t_insert(cache, item); end + end + if sorter then + t_sort(cache, sorter); + end + local i = 0; + return function() + i = i + 1; + return cache[i]; + end; + +end + +function merged(reader, merger) + + local item1 = reader(); + local merged = { item1 }; + return function() + while true do + if not item1 then return nil; end + local item2 = reader(); + if not item2 then item1 = nil; return merged; end + if merger(item1, item2) then + --print("merged") + item1 = item2; + t_insert(merged, item1); + else + --print("unmerged", merged) + item1 = item2; + local tmp = merged; + merged = { item1 }; + return tmp; + end + end + end; + +end + +return _M; diff --git a/tools/migration/prosody_files.lua b/tools/migration/prosody_files.lua index d9c2dd45..df67c240 100644 --- a/tools/migration/prosody_files.lua +++ b/tools/migration/prosody_files.lua @@ -1,133 +1,133 @@ - -local print = print; -local assert = assert; -local setmetatable = setmetatable; -local tonumber = tonumber; -local char = string.char; -local coroutine = coroutine; -local lfs = require "lfs"; -local loadfile = loadfile; -local setfenv = setfenv; -local pcall = pcall; -local mtools = require "mtools"; -local next = next; -local pairs = pairs; -local json = require "util.json"; - -prosody = {}; -local dm = require "util.datamanager" - -module "prosody_files" - -local function is_dir(path) return lfs.attributes(path, "mode") == "directory"; end -local function is_file(path) return lfs.attributes(path, "mode") == "file"; end -local function clean_path(path) - return path:gsub("\\", "/"):gsub("//+", "/"); -end -local encode, decode; do - local urlcodes = setmetatable({}, { __index = function (t, k) t[k] = char(tonumber("0x"..k)); return t[k]; end }); - decode = function (s) return s and (s:gsub("+", " "):gsub("%%([a-fA-F0-9][a-fA-F0-9])", urlcodes)); end - encode = function (s) return s and (s:gsub("%W", function (c) return format("%%%02x", c:byte()); end)); end -end -local function decode_dir(x) - if x:gsub("%%%x%x", ""):gsub("[a-zA-Z0-9]", "") == "" then - return decode(x); - end -end -local function decode_file(x) - if x:match(".%.dat$") and x:gsub("%.dat$", ""):gsub("%%%x%x", ""):gsub("[a-zA-Z0-9]", "") == "" then - return decode(x:gsub("%.dat$", "")); - end -end -local function prosody_dir(path, ondir, onfile, ...) - for x in lfs.dir(path) do - local xpath = path.."/"..x; - if decode_dir(x) and is_dir(xpath) then - ondir(xpath, x, ...); - elseif decode_file(x) and is_file(xpath) then - onfile(xpath, x, ...); - end - end -end - -local function handle_root_file(path, name) - --print("root file: ", decode_file(name)) - coroutine.yield { user = nil, host = nil, store = decode_file(name) }; -end -local function handle_host_file(path, name, host) - --print("host file: ", decode_dir(host).."/"..decode_file(name)) - coroutine.yield { user = nil, host = decode_dir(host), store = decode_file(name) }; -end -local function handle_store_file(path, name, store, host) - --print("store file: ", decode_file(name).."@"..decode_dir(host).."/"..decode_dir(store)) - coroutine.yield { user = decode_file(name), host = decode_dir(host), store = decode_dir(store) }; -end -local function handle_host_store(path, name, host) - prosody_dir(path, function() end, handle_store_file, name, host); -end -local function handle_host_dir(path, name) - prosody_dir(path, handle_host_store, handle_host_file, name); -end -local function handle_root_dir(path) - prosody_dir(path, handle_host_dir, handle_root_file); -end - -local function decode_user(item) - local userdata = { - user = item[1].user; - host = item[1].host; - stores = {}; - }; - for i=1,#item do -- loop over stores - local result = {}; - local store = item[i]; - userdata.stores[store.store] = store.data; - store.user = nil; store.host = nil; store.store = nil; - end - return userdata; -end - -function reader(input) - local path = clean_path(assert(input.path, "no input.path specified")); - assert(is_dir(path), "input.path is not a directory"); - local iter = coroutine.wrap(function()handle_root_dir(path);end); - -- get per-user stores, sorted - local iter = mtools.sorted { - reader = function() - local x = iter(); - if x then - dm.set_data_path(path); - x.data = assert(dm.load(x.user, x.host, x.store)); - return x; - end - end; - sorter = function(a, b) - local a_host, a_user, a_store = a.host or "", a.user or "", a.store or ""; - local b_host, b_user, b_store = b.host or "", b.user or "", b.store or ""; - return a_host > b_host or (a_host==b_host and a_user > b_user) or (a_host==b_host and a_user==b_user and a_store > b_store); - end; - }; - -- merge stores to get users - iter = mtools.merged(iter, function(a, b) - return (a.host == b.host and a.user == b.user); - end); - - return function() - local x = iter(); - return x and decode_user(x); - end -end - -function writer(output) - local path = clean_path(assert(output.path, "no output.path specified")); - assert(is_dir(path), "output.path is not a directory"); - return function(item) - if not item then return; end -- end of input - dm.set_data_path(path); - for store, data in pairs(item.stores) do - assert(dm.store(item.user, item.host, store, data)); - end - end -end - -return _M; + +local print = print; +local assert = assert; +local setmetatable = setmetatable; +local tonumber = tonumber; +local char = string.char; +local coroutine = coroutine; +local lfs = require "lfs"; +local loadfile = loadfile; +local setfenv = setfenv; +local pcall = pcall; +local mtools = require "mtools"; +local next = next; +local pairs = pairs; +local json = require "util.json"; + +prosody = {}; +local dm = require "util.datamanager" + +module "prosody_files" + +local function is_dir(path) return lfs.attributes(path, "mode") == "directory"; end +local function is_file(path) return lfs.attributes(path, "mode") == "file"; end +local function clean_path(path) + return path:gsub("\\", "/"):gsub("//+", "/"); +end +local encode, decode; do + local urlcodes = setmetatable({}, { __index = function (t, k) t[k] = char(tonumber("0x"..k)); return t[k]; end }); + decode = function (s) return s and (s:gsub("+", " "):gsub("%%([a-fA-F0-9][a-fA-F0-9])", urlcodes)); end + encode = function (s) return s and (s:gsub("%W", function (c) return format("%%%02x", c:byte()); end)); end +end +local function decode_dir(x) + if x:gsub("%%%x%x", ""):gsub("[a-zA-Z0-9]", "") == "" then + return decode(x); + end +end +local function decode_file(x) + if x:match(".%.dat$") and x:gsub("%.dat$", ""):gsub("%%%x%x", ""):gsub("[a-zA-Z0-9]", "") == "" then + return decode(x:gsub("%.dat$", "")); + end +end +local function prosody_dir(path, ondir, onfile, ...) + for x in lfs.dir(path) do + local xpath = path.."/"..x; + if decode_dir(x) and is_dir(xpath) then + ondir(xpath, x, ...); + elseif decode_file(x) and is_file(xpath) then + onfile(xpath, x, ...); + end + end +end + +local function handle_root_file(path, name) + --print("root file: ", decode_file(name)) + coroutine.yield { user = nil, host = nil, store = decode_file(name) }; +end +local function handle_host_file(path, name, host) + --print("host file: ", decode_dir(host).."/"..decode_file(name)) + coroutine.yield { user = nil, host = decode_dir(host), store = decode_file(name) }; +end +local function handle_store_file(path, name, store, host) + --print("store file: ", decode_file(name).."@"..decode_dir(host).."/"..decode_dir(store)) + coroutine.yield { user = decode_file(name), host = decode_dir(host), store = decode_dir(store) }; +end +local function handle_host_store(path, name, host) + prosody_dir(path, function() end, handle_store_file, name, host); +end +local function handle_host_dir(path, name) + prosody_dir(path, handle_host_store, handle_host_file, name); +end +local function handle_root_dir(path) + prosody_dir(path, handle_host_dir, handle_root_file); +end + +local function decode_user(item) + local userdata = { + user = item[1].user; + host = item[1].host; + stores = {}; + }; + for i=1,#item do -- loop over stores + local result = {}; + local store = item[i]; + userdata.stores[store.store] = store.data; + store.user = nil; store.host = nil; store.store = nil; + end + return userdata; +end + +function reader(input) + local path = clean_path(assert(input.path, "no input.path specified")); + assert(is_dir(path), "input.path is not a directory"); + local iter = coroutine.wrap(function()handle_root_dir(path);end); + -- get per-user stores, sorted + local iter = mtools.sorted { + reader = function() + local x = iter(); + if x then + dm.set_data_path(path); + x.data = assert(dm.load(x.user, x.host, x.store)); + return x; + end + end; + sorter = function(a, b) + local a_host, a_user, a_store = a.host or "", a.user or "", a.store or ""; + local b_host, b_user, b_store = b.host or "", b.user or "", b.store or ""; + return a_host > b_host or (a_host==b_host and a_user > b_user) or (a_host==b_host and a_user==b_user and a_store > b_store); + end; + }; + -- merge stores to get users + iter = mtools.merged(iter, function(a, b) + return (a.host == b.host and a.user == b.user); + end); + + return function() + local x = iter(); + return x and decode_user(x); + end +end + +function writer(output) + local path = clean_path(assert(output.path, "no output.path specified")); + assert(is_dir(path), "output.path is not a directory"); + return function(item) + if not item then return; end -- end of input + dm.set_data_path(path); + for store, data in pairs(item.stores) do + assert(dm.store(item.user, item.host, store, data)); + end + end +end + +return _M; diff --git a/tools/migration/prosody_sql.lua b/tools/migration/prosody_sql.lua index 56990f4a..ff33652a 100644 --- a/tools/migration/prosody_sql.lua +++ b/tools/migration/prosody_sql.lua @@ -1,174 +1,174 @@ - -local assert = assert; -local DBI = require "DBI"; -local print = print; -local type = type; -local next = next; -local pairs = pairs; -local t_sort = table.sort; -local json = require "util.json"; -local mtools = require "mtools"; -local tostring = tostring; -local tonumber = tonumber; - -module "prosody_sql" - -local function create_table(connection, params) - 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("`", "\""); - end - - local stmt = connection:prepare(create_sql); - if stmt then - local ok = stmt:execute(); - local commit_ok = connection:commit(); - if ok and commit_ok then - 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 stmt, err = connection:prepare(index_sql); - local ok, commit_ok, commit_err; - if stmt then - ok, err = assert(stmt:execute()); - commit_ok, commit_err = assert(connection:commit()); - end - end - 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.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 function decode_user(item) - local userdata = { - user = item[1][1].user; - host = item[1][1].host; - stores = {}; - }; - for i=1,#item do -- loop over stores - local result = {}; - local store = item[i]; - for i=1,#store do -- loop over store data - local row = store[i]; - local k = row.key; - local v = deserialize(row.type, row.value); - 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 - userdata.stores[store[1].store] = result; - end - end - return userdata; -end - -function reader(input) - local dbh = assert(DBI.Connect( - assert(input.driver, "no input.driver specified"), - assert(input.database, "no input.database specified"), - input.username, input.password, - input.host, input.port - )); - assert(dbh:ping()); - local stmt = assert(dbh:prepare("SELECT * FROM prosody")); - assert(stmt:execute()); - local keys = {"host", "user", "store", "key", "type", "value"}; - local f,s,val = stmt:rows(true); - -- get SQL rows, sorted - local iter = mtools.sorted { - reader = function() val = f(s, val); return val; end; - filter = function(x) - for i=1,#keys do - if not x[keys[i]] then return false; end -- TODO log error, missing field - end - if x.host == "" then x.host = nil; end - if x.user == "" then x.user = nil; end - if x.store == "" then x.store = nil; end - return x; - end; - sorter = function(a, b) - local a_host, a_user, a_store = a.host or "", a.user or "", a.store or ""; - local b_host, b_user, b_store = b.host or "", b.user or "", b.store or ""; - return a_host > b_host or (a_host==b_host and a_user > b_user) or (a_host==b_host and a_user==b_user and a_store > b_store); - end; - }; - -- merge rows to get stores - iter = mtools.merged(iter, function(a, b) - return (a.host == b.host and a.user == b.user and a.store == b.store); - end); - -- merge stores to get users - iter = mtools.merged(iter, function(a, b) - return (a[1].host == b[1].host and a[1].user == b[1].user); - end); - return function() - local x = iter(); - return x and decode_user(x); - end; -end - -function writer(output, iter) - local dbh = assert(DBI.Connect( - assert(output.driver, "no output.driver specified"), - assert(output.database, "no output.database specified"), - output.username, output.password, - output.host, output.port - )); - assert(dbh:ping()); - create_table(dbh, output); - local stmt = assert(dbh:prepare("SELECT * FROM prosody")); - assert(stmt:execute()); - local stmt = assert(dbh:prepare("DELETE FROM prosody")); - assert(stmt:execute()); - local insert = assert(dbh:prepare("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)")); - - return function(item) - if not item then assert(dbh:commit()) return dbh:close(); end -- end of input - local host = item.host or ""; - local user = item.user or ""; - for store, data in pairs(item.stores) do - -- TODO transactions - local extradata = {}; - for key, value in pairs(data) do - if type(key) == "string" and key ~= "" then - local t, value = assert(serialize(value)); - local ok, err = assert(insert:execute(host, user, store, key, t, value)); - else - extradata[key] = value; - end - end - if next(extradata) ~= nil then - local t, extradata = assert(serialize(extradata)); - local ok, err = assert(insert:execute(host, user, store, "", t, extradata)); - end - end - end; -end - - -return _M; + +local assert = assert; +local DBI = require "DBI"; +local print = print; +local type = type; +local next = next; +local pairs = pairs; +local t_sort = table.sort; +local json = require "util.json"; +local mtools = require "mtools"; +local tostring = tostring; +local tonumber = tonumber; + +module "prosody_sql" + +local function create_table(connection, params) + 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("`", "\""); + end + + local stmt = connection:prepare(create_sql); + if stmt then + local ok = stmt:execute(); + local commit_ok = connection:commit(); + if ok and commit_ok then + 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 stmt, err = connection:prepare(index_sql); + local ok, commit_ok, commit_err; + if stmt then + ok, err = assert(stmt:execute()); + commit_ok, commit_err = assert(connection:commit()); + end + end + 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.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 function decode_user(item) + local userdata = { + user = item[1][1].user; + host = item[1][1].host; + stores = {}; + }; + for i=1,#item do -- loop over stores + local result = {}; + local store = item[i]; + for i=1,#store do -- loop over store data + local row = store[i]; + local k = row.key; + local v = deserialize(row.type, row.value); + 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 + userdata.stores[store[1].store] = result; + end + end + return userdata; +end + +function reader(input) + local dbh = assert(DBI.Connect( + assert(input.driver, "no input.driver specified"), + assert(input.database, "no input.database specified"), + input.username, input.password, + input.host, input.port + )); + assert(dbh:ping()); + local stmt = assert(dbh:prepare("SELECT * FROM prosody")); + assert(stmt:execute()); + local keys = {"host", "user", "store", "key", "type", "value"}; + local f,s,val = stmt:rows(true); + -- get SQL rows, sorted + local iter = mtools.sorted { + reader = function() val = f(s, val); return val; end; + filter = function(x) + for i=1,#keys do + if not x[keys[i]] then return false; end -- TODO log error, missing field + end + if x.host == "" then x.host = nil; end + if x.user == "" then x.user = nil; end + if x.store == "" then x.store = nil; end + return x; + end; + sorter = function(a, b) + local a_host, a_user, a_store = a.host or "", a.user or "", a.store or ""; + local b_host, b_user, b_store = b.host or "", b.user or "", b.store or ""; + return a_host > b_host or (a_host==b_host and a_user > b_user) or (a_host==b_host and a_user==b_user and a_store > b_store); + end; + }; + -- merge rows to get stores + iter = mtools.merged(iter, function(a, b) + return (a.host == b.host and a.user == b.user and a.store == b.store); + end); + -- merge stores to get users + iter = mtools.merged(iter, function(a, b) + return (a[1].host == b[1].host and a[1].user == b[1].user); + end); + return function() + local x = iter(); + return x and decode_user(x); + end; +end + +function writer(output, iter) + local dbh = assert(DBI.Connect( + assert(output.driver, "no output.driver specified"), + assert(output.database, "no output.database specified"), + output.username, output.password, + output.host, output.port + )); + assert(dbh:ping()); + create_table(dbh, output); + local stmt = assert(dbh:prepare("SELECT * FROM prosody")); + assert(stmt:execute()); + local stmt = assert(dbh:prepare("DELETE FROM prosody")); + assert(stmt:execute()); + local insert = assert(dbh:prepare("INSERT INTO `prosody` (`host`,`user`,`store`,`key`,`type`,`value`) VALUES (?,?,?,?,?,?)")); + + return function(item) + if not item then assert(dbh:commit()) return dbh:close(); end -- end of input + local host = item.host or ""; + local user = item.user or ""; + for store, data in pairs(item.stores) do + -- TODO transactions + local extradata = {}; + for key, value in pairs(data) do + if type(key) == "string" and key ~= "" then + local t, value = assert(serialize(value)); + local ok, err = assert(insert:execute(host, user, store, key, t, value)); + else + extradata[key] = value; + end + end + if next(extradata) ~= nil then + local t, extradata = assert(serialize(extradata)); + local ok, err = assert(insert:execute(host, user, store, "", t, extradata)); + end + end + end; +end + + +return _M; -- cgit v1.2.3 From 1363993367e468526666d1255df469b4150506bd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 24 Feb 2011 01:35:33 +0000 Subject: prosody, prosodyctl: Use plugin_paths in addition to, not instead of, the default plugin path --- prosody | 6 ++++-- prosodyctl | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/prosody b/prosody index 81d87153..0aba4bb6 100755 --- a/prosody +++ b/prosody @@ -186,10 +186,12 @@ function init_global_state() local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data"; local custom_plugin_paths = config.get("*", "core", "plugin_paths"); if custom_plugin_paths then - CFG_PLUGINDIR = table.concat(custom_plugin_paths, package.config:sub(3,3)); -- path1;path2;path3 + local path_sep = package.config:sub(3,3); + -- path1;path2;path3;defaultpath... + CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); end prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR, - plugins = CFG_PLUGINDIR, data = data_path }; + plugins = CFG_PLUGINDIR or "plugins", data = data_path }; prosody.arg = _G.arg; diff --git a/prosodyctl b/prosodyctl index 2005a3ed..8fdf3488 100755 --- a/prosodyctl +++ b/prosodyctl @@ -112,10 +112,12 @@ config.set("*", "core", "log", { { levels = { min="info" }, to = "console" } }); local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data"; local custom_plugin_paths = config.get("*", "core", "plugin_paths"); if custom_plugin_paths then - CFG_PLUGINDIR = table.concat(custom_plugin_paths, package.config:sub(3,3)); -- path1;path2;path3 + local path_sep = package.config:sub(3,3); + -- path1;path2;path3;defaultpath... + CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); end prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR, - plugins = CFG_PLUGINDIR, data = data_path }; + plugins = CFG_PLUGINDIR or "plugins", data = data_path }; require "core.loggingmanager" -- cgit v1.2.3 From 03db98294d38720bd61c178af931e7d7d711696d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 24 Feb 2011 02:04:38 +0000 Subject: util.stanza: Whitespace fix after merge (complicated) --- util/stanza.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/stanza.lua b/util/stanza.lua index 4c2a7143..de83977f 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -4,7 +4,7 @@ -- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. --- +-- local t_insert = table.insert; -- cgit v1.2.3