aboutsummaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/dataforms.lua12
-rw-r--r--util/datamanager.lua14
-rw-r--r--util/dependencies.lua149
-rw-r--r--util/events.lua8
-rw-r--r--util/hmac.lua49
-rw-r--r--util/jid.lua13
-rw-r--r--util/pluginloader.lua38
-rw-r--r--util/prosodyctl.lua15
-rw-r--r--util/sasl.lua14
-rw-r--r--util/sasl/digest-md5.lua10
-rw-r--r--util/sasl/plain.lua21
-rw-r--r--util/sasl/scram.lua31
-rw-r--r--util/sasl_cyrus.lua125
-rw-r--r--util/serialization.lua31
-rw-r--r--util/stanza.lua64
-rw-r--r--util/timer.lua69
16 files changed, 474 insertions, 189 deletions
diff --git a/util/dataforms.lua b/util/dataforms.lua
index 5626172e..56671347 100644
--- a/util/dataforms.lua
+++ b/util/dataforms.lua
@@ -23,8 +23,8 @@ function new(layout)
return setmetatable(layout, form_mt);
end
-function form_t.form(layout, data)
- local form = st.stanza("x", { xmlns = xmlns_forms, type = "form" });
+function form_t.form(layout, data, formtype)
+ local form = st.stanza("x", { xmlns = xmlns_forms, type = formtype or "form" });
if layout.title then
form:tag("title"):text(layout.title):up();
end
@@ -93,7 +93,13 @@ function form_t.data(layout, stanza)
local data = {};
for field_tag in stanza:childtags() do
- local field_type = field_tag.attr.type;
+ local field_type;
+ for n, field in ipairs(layout) do
+ if field.name == field_tag.attr.var then
+ field_type = field.type;
+ break;
+ end
+ end
local reader = field_readers[field_type];
if reader then
diff --git a/util/datamanager.lua b/util/datamanager.lua
index 4d07d6cc..a2da0aa3 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -15,19 +15,25 @@ local loadfile, setfenv, pcall = loadfile, setfenv, pcall;
local log = require "util.logger".init("datamanager");
local io_open = io.open;
local os_remove = os.remove;
-local io_popen = io.popen;
local tostring, tonumber = tostring, tonumber;
local error = error;
local next = next;
local t_insert = table.insert;
local append = require "util.serialization".append;
local path_separator = "/"; if os.getenv("WINDIR") then path_separator = "\\" end
+local raw_mkdir;
+
+if prosody.platform == "posix" then
+ raw_mkdir = require "util.pposix".mkdir; -- Doesn't trample on umask
+else
+ raw_mkdir = require "lfs".mkdir;
+end
module "datamanager"
---- utils -----
local encode, decode;
-do
+do
local urlcodes = setmetatable({}, { __index = function (t, k) t[k] = char(tonumber("0x"..k)); return t[k]; end });
decode = function (s)
@@ -43,7 +49,7 @@ local _mkdir = {};
local function mkdir(path)
path = path:gsub("/", path_separator); -- TODO as an optimization, do this during path creation rather than here
if not _mkdir[path] then
- local x = io_popen("mkdir \""..path.."\" 2>&1"):read("*a");
+ raw_mkdir(path);
_mkdir[path] = true;
end
return path;
@@ -88,7 +94,7 @@ end
function getpath(username, host, datastore, ext, create)
ext = ext or "dat";
- host = host and encode(host);
+ host = (host and encode(host)) or "_global";
username = username and encode(username);
if username then
if create then mkdir(mkdir(mkdir(data_path).."/"..host).."/"..datastore); end
diff --git a/util/dependencies.lua b/util/dependencies.lua
index 5b07072f..baa0cee2 100644
--- a/util/dependencies.lua
+++ b/util/dependencies.lua
@@ -6,19 +6,27 @@
-- COPYING file in the source package for more information.
--
+module("dependencies", package.seeall)
-local fatal;
+function softreq(...) local ok, lib = pcall(require, ...); if ok then return lib; else return nil, lib; end end
-local function softreq(...) local ok, lib = pcall(require, ...); if ok then return lib; else return nil, lib; end end
+-- Required to be able to find packages installed with luarocks
+if not softreq "luarocks.loader" then -- LuaRocks 2.x
+ softreq "luarocks.require"; -- LuaRocks <1.x
+end
-local function missingdep(name, sources, msg)
+function missingdep(name, sources, msg)
print("");
print("**************************");
print("Prosody was unable to find "..tostring(name));
print("This package can be obtained in the following ways:");
print("");
- for k,v in pairs(sources) do
- print("", k, v);
+ local longest_platform = 0;
+ for platform in pairs(sources) do
+ longest_platform = math.max(longest_platform, #platform);
+ end
+ for platform, source in pairs(sources) do
+ print("", platform..":"..(" "):rep(4+longest_platform-#platform)..source);
end
print("");
print(msg or (name.." is required for Prosody to run, so we will now exit."));
@@ -27,62 +35,91 @@ local function missingdep(name, sources, msg)
print("");
end
-local lxp = softreq "lxp"
-
-if not lxp then
- missingdep("luaexpat", { ["Ubuntu 8.04 (Hardy)"] = "sudo apt-get install liblua5.1-expat0"; ["luarocks"] = "luarocks install luaexpat"; });
- fatal = true;
-end
-
-local socket = softreq "socket"
-
-if not socket then
- missingdep("luasocket", { ["Ubuntu 8.04 (Hardy)"] = "sudo apt-get install liblua5.1-socket2"; ["luarocks"] = "luarocks install luasocket"; });
- fatal = true;
-end
+function check_dependencies()
+ local fatal;
-local ssl = softreq "ssl"
-
-if not ssl then
- if config.get("*", "core", "run_without_ssl") then
- log("warn", "Running without SSL support because run_without_ssl is defined in the config");
- else
- missingdep("LuaSec", { ["Source"] = "http://www.inf.puc-rio.br/~brunoos/luasec/" }, "SSL/TLS support will not be available");
+ local lxp = softreq "lxp"
+
+ if not lxp then
+ missingdep("luaexpat", {
+ ["Debian/Ubuntu"] = "sudo apt-get install liblua5.1-expat0";
+ ["luarocks"] = "luarocks install luaexpat";
+ ["Source"] = "http://www.keplerproject.org/luaexpat/";
+ });
+ fatal = true;
end
-end
-
-local encodings, err = softreq "util.encodings"
-if not encodings then
- if err:match("not found") then
- missingdep("util.encodings", { ["Windows"] = "Make sure you have encodings.dll from the Prosody distribution in util/";
- ["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/encodings.so";
- });
+
+ local socket = softreq "socket"
+
+ if not socket then
+ missingdep("luasocket", {
+ ["Debian/Ubuntu"] = "sudo apt-get install liblua5.1-socket2";
+ ["luarocks"] = "luarocks install luasocket";
+ ["Source"] = "http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/";
+ });
+ fatal = true;
+ end
+
+ local lfs, err = softreq "lfs"
+ if not lfs then
+ missingdep("luafilesystem", {
+ ["luarocks"] = "luarocks install luafilesystem";
+ ["Debian/Ubuntu"] = "sudo apt-get install liblua5.1-filesystem0";
+ ["Source"] = "http://www.keplerproject.org/luafilesystem/";
+ });
+ fatal = true;
+ end
+
+ local ssl = softreq "ssl"
+
+ if not ssl then
+ missingdep("LuaSec", {
+ ["Debian/Ubuntu"] = "http://prosody.im/download/start#debian_and_ubuntu";
+ ["luarocks"] = "luarocks install luasec";
+ ["Source"] = "http://www.inf.puc-rio.br/~brunoos/luasec/";
+ }, "SSL/TLS support will not be available");
else
- print "***********************************"
- print("util/encodings couldn't be loaded. Check that you have a recent version of libidn");
- print ""
- print("The full error was:");
- print(err)
- print "***********************************"
+ local major, minor, veryminor, patched = ssl._VERSION:match("(%d+)%.(%d+)%.?(%d*)(M?)");
+ if not major or ((tonumber(major) == 0 and (tonumber(minor) or 0) <= 3 and (tonumber(veryminor) or 0) <= 2) and patched ~= "M") then
+ log("error", "This version of LuaSec contains a known bug that causes disconnects, see http://prosody.im/doc/depends");
+ end
+ end
+
+ local encodings, err = softreq "util.encodings"
+ if not encodings then
+ if err:match("not found") then
+ missingdep("util.encodings", { ["Windows"] = "Make sure you have encodings.dll from the Prosody distribution in util/";
+ ["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/encodings.so";
+ });
+ else
+ print "***********************************"
+ print("util/encodings couldn't be loaded. Check that you have a recent version of libidn");
+ print ""
+ print("The full error was:");
+ print(err)
+ print "***********************************"
+ end
+ fatal = true;
end
- fatal = true;
-end
-local hashes, err = softreq "util.hashes"
-if not hashes then
- if err:match("not found") then
- missingdep("util.hashes", { ["Windows"] = "Make sure you have hashes.dll from the Prosody distribution in util/";
- ["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/hashes.so";
- });
- else
- print "***********************************"
- print("util/hashes couldn't be loaded. Check that you have a recent version of OpenSSL (libcrypto in particular)");
- print ""
- print("The full error was:");
- print(err)
- print "***********************************"
+ local hashes, err = softreq "util.hashes"
+ if not hashes then
+ if err:match("not found") then
+ missingdep("util.hashes", { ["Windows"] = "Make sure you have hashes.dll from the Prosody distribution in util/";
+ ["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/hashes.so";
+ });
+ else
+ print "***********************************"
+ print("util/hashes couldn't be loaded. Check that you have a recent version of OpenSSL (libcrypto in particular)");
+ print ""
+ print("The full error was:");
+ print(err)
+ print "***********************************"
+ end
+ fatal = true;
end
- fatal = true;
+ return not fatal;
end
-if fatal then os.exit(1); end
+
+return _M;
diff --git a/util/events.lua b/util/events.lua
index a1edd496..ef8fc30a 100644
--- a/util/events.lua
+++ b/util/events.lua
@@ -47,13 +47,13 @@ function new()
_rebuild_index(event);
end
end;
- local function add_plugin(plugin)
- for event, handler in pairs(plugin) do
+ local function add_handlers(handlers)
+ for event, handler in pairs(handlers) do
add_handler(event, handler);
end
end;
- local function remove_plugin(plugin)
- for event, handler in pairs(plugin) do
+ local function remove_handlers(handlers)
+ for event, handler in pairs(handlers) do
remove_handler(event, handler);
end
end;
diff --git a/util/hmac.lua b/util/hmac.lua
index ffd69d91..18c559b2 100644
--- a/util/hmac.lua
+++ b/util/hmac.lua
@@ -7,20 +7,27 @@
--
local hashes = require "util.hashes"
-local xor = require "bit".bxor
-local t_insert, t_concat = table.insert, table.concat;
local s_char = string.char;
+local s_gsub = string.gsub;
+local s_rep = string.rep;
module "hmac"
-local function arraystr(array)
- local t = {}
- for i = 1,#array do
- t_insert(t, s_char(array[i]))
- end
-
- return t_concat(t)
+local xor_map = {0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;1;0;3;2;5;4;7;6;9;8;11;10;13;12;15;14;2;3;0;1;6;7;4;5;10;11;8;9;14;15;12;13;3;2;1;0;7;6;5;4;11;10;9;8;15;14;13;12;4;5;6;7;0;1;2;3;12;13;14;15;8;9;10;11;5;4;7;6;1;0;3;2;13;12;15;14;9;8;11;10;6;7;4;5;2;3;0;1;14;15;12;13;10;11;8;9;7;6;5;4;3;2;1;0;15;14;13;12;11;10;9;8;8;9;10;11;12;13;14;15;0;1;2;3;4;5;6;7;9;8;11;10;13;12;15;14;1;0;3;2;5;4;7;6;10;11;8;9;14;15;12;13;2;3;0;1;6;7;4;5;11;10;9;8;15;14;13;12;3;2;1;0;7;6;5;4;12;13;14;15;8;9;10;11;4;5;6;7;0;1;2;3;13;12;15;14;9;8;11;10;5;4;7;6;1;0;3;2;14;15;12;13;10;11;8;9;6;7;4;5;2;3;0;1;15;14;13;12;11;10;9;8;7;6;5;4;3;2;1;0;};
+local function xor(x, y)
+ local lowx, lowy = x % 16, y % 16;
+ local hix, hiy = (x - lowx) / 16, (y - lowy) / 16;
+ local lowr, hir = xor_map[lowx * 16 + lowy + 1], xor_map[hix * 16 + hiy + 1];
+ local r = hir * 16 + lowr;
+ return r;
+end
+local opadc, ipadc = s_char(0x5c), s_char(0x36);
+local ipad_map = {};
+local opad_map = {};
+for i=0,255 do
+ ipad_map[s_char(i)] = s_char(xor(0x36, i));
+ opad_map[s_char(i)] = s_char(xor(0x5c, i));
end
--[[
@@ -36,31 +43,15 @@ hex
return raw hash or hexadecimal string
--]]
function hmac(key, message, hash, blocksize, hex)
- local opad = {}
- local ipad = {}
-
- for i = 1,blocksize do
- opad[i] = 0x5c
- ipad[i] = 0x36
- end
-
if #key > blocksize then
key = hash(key)
end
- for i = 1,#key do
- ipad[i] = xor(ipad[i],key:sub(i,i):byte())
- opad[i] = xor(opad[i],key:sub(i,i):byte())
- end
-
- opad = arraystr(opad)
- ipad = arraystr(ipad)
+ local padding = blocksize - #key;
+ local ipad = s_gsub(key, ".", ipad_map)..s_rep(ipadc, padding);
+ local opad = s_gsub(key, ".", opad_map)..s_rep(opadc, padding);
- if hex then
- return hash(opad..hash(ipad..message), true)
- else
- return hash(opad..hash(ipad..message))
- end
+ return hash(opad..hash(ipad..message), hex)
end
function md5(key, message, hex)
diff --git a/util/jid.lua b/util/jid.lua
index ccc8309c..b43247cc 100644
--- a/util/jid.lua
+++ b/util/jid.lua
@@ -65,4 +65,17 @@ function prep(jid)
return host;
end
+function join(node, host, resource)
+ if node and host and resource then
+ return node.."@"..host.."/"..resource;
+ elseif node and host then
+ return node.."@"..host;
+ elseif host and resource then
+ return host.."/"..resource;
+ elseif host then
+ return host;
+ end
+ return nil; -- Invalid JID
+end
+
return _M;
diff --git a/util/pluginloader.lua b/util/pluginloader.lua
index 696af34f..8c22c204 100644
--- a/util/pluginloader.lua
+++ b/util/pluginloader.lua
@@ -9,11 +9,19 @@
local plugin_dir = CFG_PLUGINDIR or "./plugins/";
-local io_open = io.open;
-local loadstring = loadstring;
+local io_open, os_time = io.open, os.time;
+local loadstring, pairs = loadstring, pairs;
+
+local datamanager = require "util.datamanager";
module "pluginloader"
+local function load_from_datastore(name)
+ local content = datamanager.load(name, nil, "plugins");
+ if not content or not content[1] then return nil, "Resource not found"; end
+ return content[1], name;
+end
+
local function load_file(name)
local file, err = io_open(plugin_dir..name);
if not file then return file, err; end
@@ -22,16 +30,36 @@ local function load_file(name)
return content, name;
end
-function load_resource(plugin, resource)
+function load_resource(plugin, resource, loader)
if not resource then
resource = "mod_"..plugin..".lua";
end
- local content, err = load_file(plugin.."/"..resource);
- if not content then content, err = load_file(resource); 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
+
+ if not content and loader == load_file then
+ return load_resource(plugin, resource, load_from_datastore);
+ end
+
return content, err;
end
+function store_resource(plugin, resource, content, metadata)
+ if not resource then
+ resource = "mod_"..plugin..".lua";
+ end
+ local store = { content };
+ if metadata then
+ for k,v in pairs(metadata) do
+ store[k] = v;
+ end
+ end
+ datamanager.store(plugin.."/"..resource, nil, "plugins", store);
+end
+
function load_code(plugin, resource)
local content, err = load_resource(plugin, resource);
if not content then return content, err; end
diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua
index b24e194d..0776fc76 100644
--- a/util/prosodyctl.lua
+++ b/util/prosodyctl.lua
@@ -12,6 +12,7 @@ local encodings = require "util.encodings";
local stringprep = encodings.stringprep;
local usermanager = require "core.usermanager";
local signal = require "util.signal";
+local lfs = require "lfs";
local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep;
@@ -64,11 +65,17 @@ function getpid()
return false, "no-pidfile";
end
- local file, err = io.open(pidfile);
+ local file, err = io.open(pidfile, "r+");
if not file then
return false, "pidfile-read-failed", err;
end
+ local locked, err = lfs.lock(file, "w");
+ if locked then
+ file:close();
+ return false, "pidfile-not-locked";
+ end
+
local pid = tonumber(file:read("*a"));
file:close();
@@ -82,7 +89,7 @@ end
function isrunning()
local ok, pid, err = _M.getpid();
if not ok then
- if pid == "pidfile-read-failed" then
+ if pid == "pidfile-read-failed" or pid == "pidfile-not-locked" then
-- Report as not running, since we can't open the pidfile
-- (it probably doesn't exist)
return true, false;
@@ -102,10 +109,8 @@ function start()
end
if not CFG_SOURCEDIR then
os.execute("./prosody");
- elseif CFG_SOURCEDIR:match("^/usr/local") then
- os.execute("/usr/local/bin/prosody");
else
- os.execute("prosody");
+ os.execute(CFG_SOURCEDIR.."/../../bin/prosody");
end
return true;
end
diff --git a/util/sasl.lua b/util/sasl.lua
index 7b7db024..9c8fff78 100644
--- a/util/sasl.lua
+++ b/util/sasl.lua
@@ -83,13 +83,19 @@ end
-- create a new SASL object which can be used to authenticate clients
function new(realm, profile, forbidden)
- sasl_i = {profile = profile};
+ local sasl_i = {profile = profile};
sasl_i.realm = realm;
- s = setmetatable(sasl_i, method);
- s:forbidden(sasl_i, forbidden)
+ local s = setmetatable(sasl_i, method);
+ if forbidden == nil then forbidden = {} end
+ s:forbidden(forbidden)
return s;
end
+-- get a fresh clone with the same realm, profiles and forbidden mechanisms
+function method:clean_clone()
+ return new(self.realm, self.profile, self:forbidden())
+end
+
-- set the forbidden mechanisms
function method:forbidden( restrict )
if restrict then
@@ -107,7 +113,7 @@ function method:mechanisms()
for backend, f in pairs(self.profile) do
if backend_mechanism[backend] then
for _, mechanism in ipairs(backend_mechanism[backend]) do
- if not sasl_i.restrict:contains(mechanism) then
+ if not self.restrict:contains(mechanism) then
mechanisms[mechanism] = true;
end
end
diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua
index 1429a5c6..5b8f5c8a 100644
--- a/util/sasl/digest-md5.lua
+++ b/util/sasl/digest-md5.lua
@@ -28,10 +28,6 @@ module "digest-md5"
--=========================
--SASL DIGEST-MD5 according to RFC 2831
-local function digest_response()
-
- return response, A1, A2
-end
local function digest(self, message)
--TODO complete support for authzid
@@ -101,7 +97,8 @@ local function digest(self, message)
end
local function parse(data)
local message = {}
- for k, v in s_gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder
+ -- COMPAT: %z in the pattern to work around jwchat bug (sends "charset=utf-8\0")
+ for k, v in s_gmatch(data, [[([%w%-]+)="?([^",%z]*)"?,?]]) do -- FIXME The hacky regex makes me shudder
message[k] = v;
end
return message;
@@ -169,13 +166,14 @@ local function digest(self, message)
--TODO maybe realm support
self.username = response["username"];
+ local Y, state;
if self.profile.plain then
local password, state = self.profile.plain(response["username"], self.realm)
if state == nil then return "failure", "not-authorized"
elseif state == false then return "failure", "account-disabled" end
Y = md5(response["username"]..":"..response["realm"]..":"..password);
elseif self.profile["digest-md5"] then
- local Y, state = self.profile["digest-md5"](response["username"], self.realm, response["realm"], response["charset"])
+ Y, state = self.profile["digest-md5"](response["username"], self.realm, response["realm"], response["charset"])
if state == nil then return "failure", "not-authorized"
elseif state == false then return "failure", "account-disabled" end
elseif self.profile["digest-md5-test"] then
diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua
index 46a86bb9..ae5c777a 100644
--- a/util/sasl/plain.lua
+++ b/util/sasl/plain.lua
@@ -17,22 +17,23 @@ local log = require "util.logger".init("sasl");
module "plain"
---=========================
---SASL PLAIN according to RFC 4616
+-- ================================
+-- SASL PLAIN according to RFC 4616
local function plain(self, message)
- local response = message
- local authorization = s_match(response, "([^%z]+)")
- local authentication = s_match(response, "%z([^%z]+)%z")
- local password = s_match(response, "%z[^%z]+%z([^%z]+)")
+ if not message then
+ return "failure", "malformed-request";
+ end
+
+ local authorization, authentication, password = s_match(message, "^([^%z]*)%z([^%z]+)%z([^%z]+)");
- if authentication == nil or password == nil then
+ if not authorization then
return "failure", "malformed-request";
end
-
+
-- SASLprep password and authentication
authentication = saslprep(authentication);
password = saslprep(password);
-
+
if (not password) or (password == "") or (not authentication) or (authentication == "") then
log("debug", "Username or password violates SASLprep.");
return "failure", "malformed-request", "Invalid username or password.";
@@ -63,4 +64,4 @@ function init(registerMechanism)
registerMechanism("PLAIN", {"plain", "plain_test"}, plain);
end
-return _M; \ No newline at end of file
+return _M;
diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua
index 4413e2a6..4f800529 100644
--- a/util/sasl/scram.lua
+++ b/util/sasl/scram.lua
@@ -21,6 +21,9 @@ local sha1 = require "util.hashes".sha1;
local generate_uuid = require "util.uuid".generate;
local saslprep = require "util.encodings".stringprep.saslprep;
local log = require "util.logger".init("sasl");
+local t_concat = table.concat;
+local char = string.char;
+local byte = string.byte;
module "scram"
@@ -36,17 +39,19 @@ local function bp( b )
return result
end
+local xor_map = {0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;1;0;3;2;5;4;7;6;9;8;11;10;13;12;15;14;2;3;0;1;6;7;4;5;10;11;8;9;14;15;12;13;3;2;1;0;7;6;5;4;11;10;9;8;15;14;13;12;4;5;6;7;0;1;2;3;12;13;14;15;8;9;10;11;5;4;7;6;1;0;3;2;13;12;15;14;9;8;11;10;6;7;4;5;2;3;0;1;14;15;12;13;10;11;8;9;7;6;5;4;3;2;1;0;15;14;13;12;11;10;9;8;8;9;10;11;12;13;14;15;0;1;2;3;4;5;6;7;9;8;11;10;13;12;15;14;1;0;3;2;5;4;7;6;10;11;8;9;14;15;12;13;2;3;0;1;6;7;4;5;11;10;9;8;15;14;13;12;3;2;1;0;7;6;5;4;12;13;14;15;8;9;10;11;4;5;6;7;0;1;2;3;13;12;15;14;9;8;11;10;5;4;7;6;1;0;3;2;14;15;12;13;10;11;8;9;6;7;4;5;2;3;0;1;15;14;13;12;11;10;9;8;7;6;5;4;3;2;1;0;};
+
+local result = {};
local function binaryXOR( a, b )
- if a:len() > b:len() then
- b = string.rep("\0", a:len() - b:len())..b
- elseif string.len(a) < string.len(b) then
- a = string.rep("\0", b:len() - a:len())..a
- end
- local result = ""
- for i=1, a:len() do
- result = result..string.char(xor(a:byte(i), b:byte(i)))
+ for i=1, #a do
+ local x, y = byte(a, i), byte(b, i);
+ local lowx, lowy = x % 16, y % 16;
+ local hix, hiy = (x - lowx) / 16, (y - lowy) / 16;
+ local lowr, hir = xor_map[lowx * 16 + lowy + 1], xor_map[hix * 16 + hiy + 1];
+ local r = hir * 16 + lowr;
+ result[i] = char(r)
end
- return result
+ return t_concat(result);
end
-- hash algorithm independent Hi(PBKDF2) implementation
@@ -54,7 +59,7 @@ local function Hi(hmac, str, salt, i)
local Ust = hmac(str, salt.."\0\0\0\1");
local res = Ust;
for n=1,i-1 do
- Und = hmac(str, Ust)
+ local Und = hmac(str, Ust)
res = binaryXOR(res, Und)
Ust = Und
end
@@ -70,8 +75,8 @@ local function validate_username(username)
end
-- replace =2D with , and =3D with =
- username:gsub("=2D", ",");
- username:gsub("=3D", "=");
+ username = username:gsub("=2D", ",");
+ username = username:gsub("=3D", "=");
-- apply SASLprep
username = saslprep(username);
@@ -116,7 +121,7 @@ local function scram_sha_1(self, message)
return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message.";
end
- local password;
+ local password, state;
if self.profile.plain then
password, state = self.profile.plain(self.state.name, self.realm)
if state == nil then return "failure", "not-authorized"
diff --git a/util/sasl_cyrus.lua b/util/sasl_cyrus.lua
new file mode 100644
index 00000000..b42bee07
--- /dev/null
+++ b/util/sasl_cyrus.lua
@@ -0,0 +1,125 @@
+-- sasl.lua v0.4
+-- Copyright (C) 2008-2009 Tobias Markmann
+--
+-- All rights reserved.
+--
+-- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+--
+-- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+-- * Neither the name of Tobias Markmann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+local cyrussasl = require "cyrussasl";
+local log = require "util.logger".init("sasl_cyrus");
+local array = require "util.array";
+
+local tostring = tostring;
+local pairs, ipairs = pairs, ipairs;
+local t_insert, t_concat = table.insert, table.concat;
+local s_match = string.match;
+local setmetatable = setmetatable
+
+local keys = keys;
+
+local print = print
+local pcall = pcall
+local s_match, s_gmatch = string.match, string.gmatch
+
+module "sasl_cyrus"
+
+local method = {};
+method.__index = method;
+local initialized = false;
+
+local function init(service_name)
+ if not initialized then
+ local st, errmsg = pcall(cyrussasl.server_init, service_name);
+ if st then
+ initialized = true;
+ else
+ log("error", "Failed to initialize CyrusSASL: %s", errmsg);
+ end
+ end
+end
+
+-- create a new SASL object which can be used to authenticate clients
+function new(realm, service_name)
+ local sasl_i = {};
+
+ init(service_name);
+
+ sasl_i.realm = realm;
+ sasl_i.service_name = service_name;
+ sasl_i.cyrus = cyrussasl.server_new(service_name, nil, nil, nil, nil)
+ if sasl_i.cyrus == 0 then
+ log("error", "got NULL return value from server_new")
+ return nil;
+ end
+ cyrussasl.setssf(sasl_i.cyrus, 0, 0xffffffff)
+ local s = setmetatable(sasl_i, method);
+ return s;
+end
+
+-- get a fresh clone with the same realm, profiles and forbidden mechanisms
+function method:clean_clone()
+ return new(self.realm, self.service_name)
+end
+
+-- set the forbidden mechanisms
+function method:forbidden( restrict )
+ log("debug", "Called method:forbidden. NOT IMPLEMENTED.")
+ return {}
+end
+
+-- get a list of possible SASL mechanims to use
+function method:mechanisms()
+ local mechanisms = {}
+ local cyrus_mechs = cyrussasl.listmech(self.cyrus, nil, "", " ", "")
+ for w in s_gmatch(cyrus_mechs, "[^ ]+") do
+ mechanisms[w] = true;
+ end
+ self.mechs = mechanisms
+ return array.collect(keys(mechanisms));
+end
+
+-- select a mechanism to use
+function method:select(mechanism)
+ self.mechanism = mechanism;
+ return self.mechs[mechanism];
+end
+
+-- feed new messages to process into the library
+function method:process(message)
+ local err;
+ local data;
+
+ if self.mechanism then
+ err, data = cyrussasl.server_start(self.cyrus, self.mechanism, message or "")
+ else
+ err, data = cyrussasl.server_step(self.cyrus, message or "")
+ end
+
+ self.username = cyrussasl.get_username(self.cyrus)
+
+ if (err == 0) then -- SASL_OK
+ return "success", data
+ elseif (err == 1) then -- SASL_CONTINUE
+ return "challenge", data
+ elseif (err == -4) then -- SASL_NOMECH
+ log("debug", "SASL mechanism not available from remote end")
+ return "failure",
+ "undefined-condition",
+ "SASL mechanism not available"
+ elseif (err == -13) then -- SASL_BADAUTH
+ return "failure", "not-authorized"
+ else
+ log("debug", "Got SASL error condition %d", err)
+ return "failure",
+ "undefined-condition",
+ cyrussasl.get_message( self.cyrus )
+ end
+end
+
+return _M;
diff --git a/util/serialization.lua b/util/serialization.lua
index c2bbbb8d..7071d3f7 100644
--- a/util/serialization.lua
+++ b/util/serialization.lua
@@ -13,6 +13,7 @@ local t_insert = table.insert;
local t_concat = table.concat;
local error = error;
local pairs = pairs;
+local next = next;
local debug_traceback = debug.traceback;
local log = require "util.logger".init("serialization");
@@ -34,21 +35,25 @@ local function _simplesave(o, ind, t, func)
elseif type(o) == "string" then
func(t, (("%q"):format(o):gsub("\\\n", "\\n")));
elseif type(o) == "table" then
- func(t, "{\n");
- for k,v in pairs(o) do
- func(t, indent(ind));
- func(t, "[");
- func(t, basicSerialize(k));
- func(t, "] = ");
- if ind == 0 then
- _simplesave(v, 0, t, func);
- else
- _simplesave(v, ind+1, t, func);
+ if next(o) ~= nil then
+ func(t, "{\n");
+ for k,v in pairs(o) do
+ func(t, indent(ind));
+ func(t, "[");
+ func(t, basicSerialize(k));
+ func(t, "] = ");
+ if ind == 0 then
+ _simplesave(v, 0, t, func);
+ else
+ _simplesave(v, ind+1, t, func);
+ end
+ func(t, ";\n");
end
- func(t, ",\n");
+ func(t, indent(ind-1));
+ func(t, "}");
+ else
+ func(t, "{}");
end
- func(t, indent(ind-1));
- func(t, "}");
elseif type(o) == "boolean" then
func(t, (o and "true" or "false"));
else
diff --git a/util/stanza.lua b/util/stanza.lua
index d295d5cc..065888d0 100644
--- a/util/stanza.lua
+++ b/util/stanza.lua
@@ -38,6 +38,8 @@ if do_pretty_printing then
end
end
+local xmlns_stanzas = "urn:ietf:params:xml:ns:xmpp-stanzas";
+
module "stanza"
stanza_mt = { __type = "stanza" };
@@ -65,7 +67,7 @@ end
function stanza_mt:text(text)
(self.last_add[#self.last_add] or self):add_direct_child(text);
- return self;
+ return self;
end
function stanza_mt:up()
@@ -93,14 +95,25 @@ function stanza_mt:add_child(child)
return self;
end
+function stanza_mt:get_child(name, xmlns)
+ for _, child in ipairs(self.tags) do
+ if (not name or child.name == name)
+ and ((not xmlns and self.attr.xmlns == child.attr.xmlns)
+ or child.attr.xmlns == xmlns) then
+
+ return child;
+ end
+ end
+end
+
function stanza_mt:child_with_name(name)
- for _, child in ipairs(self.tags) do
+ for _, child in ipairs(self.tags) do
if child.name == name then return child; end
end
end
function stanza_mt:child_with_ns(ns)
- for _, child in ipairs(self.tags) do
+ for _, child in ipairs(self.tags) do
if child.attr.xmlns == ns then return child; end
end
end
@@ -112,7 +125,6 @@ function stanza_mt:children()
local v = a[i]
if v then return v; end
end, self, i;
-
end
function stanza_mt:childtags()
local i = 0;
@@ -121,7 +133,6 @@ function stanza_mt:childtags()
local v = self.tags[i]
if v then return v; end
end, self.tags[1], i;
-
end
local xml_escape
@@ -180,6 +191,30 @@ function stanza_mt.get_text(t)
end
end
+function stanza_mt.get_error(stanza)
+ local type, condition, text;
+
+ local error_tag = stanza:get_child("error");
+ if not error_tag then
+ return nil, nil, nil;
+ end
+ type = error_tag.attr.type;
+
+ for child in error_tag:children() do
+ if child.attr.xmlns == xmlns_stanzas then
+ if not text and child.name == "text" then
+ text = child:get_text();
+ elseif not condition then
+ condition = child.name;
+ end
+ if condition and text then
+ break;
+ end
+ end
+ end
+ return type, condition or "undefined-condition", text or "";
+end
+
function stanza_mt.__add(s1, s2)
return s1:add_direct_child(s2);
end
@@ -280,13 +315,16 @@ function reply(orig)
return stanza(orig.name, orig.attr and { to = orig.attr.from, from = orig.attr.to, id = orig.attr.id, type = ((orig.name == "iq" and "result") or orig.attr.type) });
end
-function error_reply(orig, type, condition, message)
- local t = reply(orig);
- t.attr.type = "error";
- t:tag("error", {type = type})
- :tag(condition, {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
- if (message) then t:tag("text"):text(message):up(); end
- return t; -- stanza ready for adding app-specific errors
+do
+ local xmpp_stanzas_attr = { xmlns = xmlns_stanzas };
+ function error_reply(orig, type, condition, message)
+ local t = reply(orig);
+ t.attr.type = "error";
+ t:tag("error", {type = type}) --COMPAT: Some day xmlns:stanzas goes here
+ :tag(condition, xmpp_stanzas_attr):up();
+ if (message) then t:tag("text", xmpp_stanzas_attr):text(message):up(); end
+ return t; -- stanza ready for adding app-specific errors
+ end
end
function presence(attr)
@@ -306,7 +344,7 @@ if do_pretty_printing then
function stanza_mt.pretty_print(t)
local children_text = "";
for n, child in ipairs(t) do
- if type(child) == "string" then
+ if type(child) == "string" then
children_text = children_text .. xml_escape(child);
else
children_text = children_text .. child:pretty_print();
diff --git a/util/timer.lua b/util/timer.lua
index c0c7f25a..c52d9c68 100644
--- a/util/timer.lua
+++ b/util/timer.lua
@@ -8,6 +8,9 @@
local ns_addtimer = require "net.server".addtimer;
+local event = require "net.server".event;
+local event_base = require "net.server".event_base;
+
local get_time = os.time;
local t_insert = table.insert;
local t_remove = table.remove;
@@ -19,33 +22,51 @@ local new_data = {};
module "timer"
-local function _add_task(delay, func)
- local current_time = get_time();
- delay = delay + current_time;
- if delay >= current_time then
- t_insert(new_data, {delay, func});
- else func(); end
-end
-
-add_task = _add_task;
-
-ns_addtimer(function()
- local current_time = get_time();
- if #new_data > 0 then
- for _, d in pairs(new_data) do
- t_insert(data, d);
+local _add_task;
+if not event then
+ function _add_task(delay, func)
+ local current_time = get_time();
+ delay = delay + current_time;
+ if delay >= current_time then
+ t_insert(new_data, {delay, func});
+ else
+ func();
end
- new_data = {};
end
-
- for i, d in pairs(data) do
- local t, func = d[1], d[2];
- if t <= current_time then
- data[i] = nil;
- local r = func(current_time);
- if type(r) == "number" then _add_task(r, func); end
+
+ ns_addtimer(function()
+ local current_time = get_time();
+ if #new_data > 0 then
+ for _, d in pairs(new_data) do
+ t_insert(data, d);
+ end
+ new_data = {};
end
+
+ for i, d in pairs(data) do
+ local t, func = d[1], d[2];
+ if t <= current_time then
+ data[i] = nil;
+ local r = func(current_time);
+ if type(r) == "number" then _add_task(r, func); end
+ end
+ end
+ end);
+else
+ local EVENT_LEAVE = (event.core and event.core.LEAVE) or -1;
+ function _add_task(delay, func)
+ event_base:addevent(nil, 0, function ()
+ local ret = func();
+ if ret then
+ return 0, ret;
+ else
+ return EVENT_LEAVE;
+ end
+ end
+ , delay);
end
-end);
+end
+
+add_task = _add_task;
return _M;