From 3e9c86d509060611bdebd819a2fe61960b213e19 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:07:22 +0100
Subject: util.datamanager: Factor out code for appending bytes to a file

---
 util/datamanager.lua | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index 4b722851..13aed78f 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -209,32 +209,40 @@ local function store(username, host, datastore, data)
 	return true;
 end
 
-local function list_append(username, host, datastore, data)
-	if not data then return; end
-	if callback(username, host, datastore) == false then return true; end
-	-- save the datastore
-	local f, msg = io_open(getpath(username, host, datastore, "list", true), "r+");
+local function append(username, host, datastore, ext, data)
+	local f, msg = io_open(getpath(username, host, datastore, ext, true), "r+");
 	if not f then
-		f, msg = io_open(getpath(username, host, datastore, "list", true), "w");
+		f, msg = io_open(getpath(username, host, datastore, ext, true), "w");
 	end
 	if not f then
-		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
-		return;
+		return nil, msg;
 	end
-	local data = "item(" ..  serialize(data) .. ");\n";
 	local pos = f:seek("end");
 	local ok, msg = fallocate(f, pos, #data);
 	f:seek("set", pos);
 	if ok then
 		f:write(data);
 	else
-		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
 		return ok, msg;
 	end
 	f:close();
 	return true;
 end
 
+local function list_append(username, host, datastore, data)
+	if not data then return; end
+	if callback(username, host, datastore) == false then return true; end
+	-- save the datastore
+
+	local data = "item(" ..  serialize(data) .. ");\n";
+	local ok, msg = append(username, host, datastore, "list", data);
+	if not ok then
+		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
+		return ok, msg;
+	end
+	return true;
+end
+
 local function list_store(username, host, datastore, data)
 	if not data then
 		data = {};
-- 
cgit v1.2.3


From 2ccee846f29ab3ce03c52a21da77f8a33279ca99 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:11:48 +0100
Subject: util.datamanager: Overwrite 'data' variable instead of shadownig it
 [luacheck]

---
 util/datamanager.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index 13aed78f..1993d6a3 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -234,7 +234,7 @@ local function list_append(username, host, datastore, data)
 	if callback(username, host, datastore) == false then return true; end
 	-- save the datastore
 
-	local data = "item(" ..  serialize(data) .. ");\n";
+	data = "item(" ..  serialize(data) .. ");\n";
 	local ok, msg = append(username, host, datastore, "list", data);
 	if not ok then
 		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
-- 
cgit v1.2.3


From 8ecd4052a5f14f556df22a5c1ea34e7ce2314297 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:13:37 +0100
Subject: util.datamanager: In append() collect status when closing file handle
 as it may fail (eg the implied flush)

---
 util/datamanager.lua | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index 1993d6a3..4a371d2c 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -210,9 +210,10 @@ local function store(username, host, datastore, data)
 end
 
 local function append(username, host, datastore, ext, data)
-	local f, msg = io_open(getpath(username, host, datastore, ext, true), "r+");
+	local filename = getpath(username, host, datastore, ext, true);
+	local f, msg = io_open(filename, "r+");
 	if not f then
-		f, msg = io_open(getpath(username, host, datastore, ext, true), "w");
+		f, msg = io_open(filename, "w");
 	end
 	if not f then
 		return nil, msg;
@@ -225,7 +226,12 @@ local function append(username, host, datastore, ext, data)
 	else
 		return ok, msg;
 	end
-	f:close();
+
+	ok, msg = f:close();
+	if not ok then
+		return ok, msg;
+	end
+
 	return true;
 end
 
-- 
cgit v1.2.3


From 74caca930a0fffb2975a1043d2d5033a9c9647ef Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:18:24 +0100
Subject: util.datamanager: Skip past second check if first attemtp to open
 file succeeds

---
 util/datamanager.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index 4a371d2c..b7c514ad 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -214,9 +214,9 @@ local function append(username, host, datastore, ext, data)
 	local f, msg = io_open(filename, "r+");
 	if not f then
 		f, msg = io_open(filename, "w");
-	end
-	if not f then
-		return nil, msg;
+		if not f then
+			return nil, msg;
+		end
 	end
 	local pos = f:seek("end");
 	local ok, msg = fallocate(f, pos, #data);
-- 
cgit v1.2.3


From 74500e9b505da0bee52b7f03d3372b90a09c5b50 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:22:54 +0100
Subject: util.datamanager: No shadowing of variable [luacheck]

---
 util/datamanager.lua | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index b7c514ad..786abd95 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -211,6 +211,8 @@ end
 
 local function append(username, host, datastore, ext, data)
 	local filename = getpath(username, host, datastore, ext, true);
+
+	local ok;
 	local f, msg = io_open(filename, "r+");
 	if not f then
 		f, msg = io_open(filename, "w");
@@ -219,7 +221,7 @@ local function append(username, host, datastore, ext, data)
 		end
 	end
 	local pos = f:seek("end");
-	local ok, msg = fallocate(f, pos, #data);
+	ok, msg = fallocate(f, pos, #data);
 	f:seek("set", pos);
 	if ok then
 		f:write(data);
-- 
cgit v1.2.3


From be1b771edf344615e31f310259b2793c95c1d217 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:24:36 +0100
Subject: util.datamanager: Handle potential issues from fallocate

---
 util/datamanager.lua | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index 786abd95..3016feed 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -222,7 +222,16 @@ local function append(username, host, datastore, ext, data)
 	end
 	local pos = f:seek("end");
 	ok, msg = fallocate(f, pos, #data);
-	f:seek("set", pos);
+	if not ok then
+		log("warn", "fallocate() failed: %s", tostring(msg));
+		-- This doesn't work on every file system
+	end
+
+	if f:seek() ~= pos then
+		log("debug", "fallocate() changed file position");
+		f:seek("set", pos);
+	end
+
 	if ok then
 		f:write(data);
 	else
-- 
cgit v1.2.3


From 4b380b1cbfd38a79277c7359b015fbe134c23176 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:26:04 +0100
Subject: util.datamanager: Handle potential error from :write() call

---
 util/datamanager.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index 3016feed..ce4c5b5a 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -232,9 +232,9 @@ local function append(username, host, datastore, ext, data)
 		f:seek("set", pos);
 	end
 
-	if ok then
-		f:write(data);
-	else
+	ok, msg = f:write(data);
+	if not ok then
+		f:close();
 		return ok, msg;
 	end
 
-- 
cgit v1.2.3


From 1510dd113d86a62405cb61b370fbd716450f9d58 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:29:55 +0100
Subject: util.datamanager: Add some comments about the append function

---
 util/datamanager.lua | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index ce4c5b5a..510c0161 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -209,17 +209,20 @@ local function store(username, host, datastore, data)
 	return true;
 end
 
+-- Append a blob of data to a file
 local function append(username, host, datastore, ext, data)
 	local filename = getpath(username, host, datastore, ext, true);
 
 	local ok;
 	local f, msg = io_open(filename, "r+");
 	if not f then
+		-- File did probably not exist, let's create it
 		f, msg = io_open(filename, "w");
 		if not f then
 			return nil, msg;
 		end
 	end
+
 	local pos = f:seek("end");
 	ok, msg = fallocate(f, pos, #data);
 	if not ok then
-- 
cgit v1.2.3


From 206330cea1c32c1ffd94f983dd95b38c688098dd Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:30:39 +0100
Subject: util.datamanager: Make sure only strings are passed as data to
 append()

---
 util/datamanager.lua | 1 +
 1 file changed, 1 insertion(+)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index 510c0161..e57d5fdc 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -211,6 +211,7 @@ end
 
 -- Append a blob of data to a file
 local function append(username, host, datastore, ext, data)
+	if type(data) ~= "string" then return; end
 	local filename = getpath(username, host, datastore, ext, true);
 
 	local ok;
-- 
cgit v1.2.3


From 7816dfc838c7f9478fc97cc1c8bb42aa5ae03a5d Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:31:19 +0100
Subject: util.datamanager: Return extra location info

---
 util/datamanager.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index e57d5fdc..c8dbaae9 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -220,7 +220,7 @@ local function append(username, host, datastore, ext, data)
 		-- File did probably not exist, let's create it
 		f, msg = io_open(filename, "w");
 		if not f then
-			return nil, msg;
+			return nil, msg, "open";
 		end
 	end
 
@@ -239,7 +239,7 @@ local function append(username, host, datastore, ext, data)
 	ok, msg = f:write(data);
 	if not ok then
 		f:close();
-		return ok, msg;
+		return ok, msg, "write";
 	end
 
 	ok, msg = f:close();
@@ -247,7 +247,7 @@ local function append(username, host, datastore, ext, data)
 		return ok, msg;
 	end
 
-	return true;
+	return true, pos;
 end
 
 local function list_append(username, host, datastore, data)
-- 
cgit v1.2.3


From 30b54f4ad71fb58310f2b0b3bf7d0392b61a4ff6 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 11 Dec 2015 20:31:55 +0100
Subject: util.datamanager: Add append to public api

---
 util/datamanager.lua | 1 +
 1 file changed, 1 insertion(+)

diff --git a/util/datamanager.lua b/util/datamanager.lua
index c8dbaae9..83f3dd13 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -402,6 +402,7 @@ return {
 	getpath = getpath;
 	load = load;
 	store = store;
+	append_raw = append;
 	list_append = list_append;
 	list_store = list_store;
 	list_load = list_load;
-- 
cgit v1.2.3


From 14cc4c9287df4e7c48128b9e53f0f48d2fab75d0 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sun, 13 Dec 2015 21:21:09 +0100
Subject: util.sslconfig: More descriptive variable names and also comments

---
 util/sslconfig.lua | 87 +++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 56 insertions(+), 31 deletions(-)

diff --git a/util/sslconfig.lua b/util/sslconfig.lua
index 71f27c94..c849aa28 100644
--- a/util/sslconfig.lua
+++ b/util/sslconfig.lua
@@ -1,3 +1,5 @@
+-- util to easily merge multiple sets of LuaSec context options
+
 local type = type;
 local pairs = pairs;
 local rawset = rawset;
@@ -11,70 +13,93 @@ local handlers = { };
 local finalisers = { };
 local id = function (v) return v end
 
-function handlers.options(a, k, b)
-	local o = a[k] or { };
-	if type(b) ~= "table" then b = { b } end
-	for key, value in pairs(b) do
+-- All "handlers" behave like extended rawset(table, key, value) with extra
+-- processing usually merging the new value with the old in some reasonable
+-- way
+-- If a field does not have a defined handler then a new value simply
+-- replaces the old.
+
+
+-- Convert either a list or a set into a special type of set where each
+-- item is either positive or negative in order for a later set of options
+-- to be able to remove options from this set by filtering out the negative ones
+function handlers.options(config, field, new)
+	local options = config[field] or { };
+	if type(new) ~= "table" then new = { new } end
+	for key, value in pairs(new) do
 		if value == true or value == false then
-			o[key] = value;
-		else
-			o[value] = true;
+			options[key] = value;
+		else -- list item
+			options[value] = true;
 		end
 	end
-	a[k] = o;
+	config[field] = options;
 end
 
 handlers.verify = handlers.options;
 handlers.verifyext = handlers.options;
 
-function finalisers.options(a)
-	local o = {};
-	for opt, enable in pairs(a) do
+-- finalisers take something produced by handlers and return what luasec
+-- expects it to be
+
+-- Produce a list of "positive" options from the set
+function finalisers.options(options)
+	local output = {};
+	for opt, enable in pairs(options) do
 		if enable then
-			o[#o+1] = opt;
+			output[#output+1] = opt;
 		end
 	end
-	return o;
+	return output;
 end
 
 finalisers.verify = finalisers.options;
 finalisers.verifyext = finalisers.options;
 
-function finalisers.ciphers(a)
-	if type(a) == "table" then
-		return t_concat(a, ":");
+-- We allow ciphers to be a list
+
+function finalisers.ciphers(cipherlist)
+	if type(cipherlist) == "table" then
+		return t_concat(cipherlist, ":");
 	end
-	return a;
+	return cipherlist;
 end
 
+-- protocol = "x" should enable only that protocol
+-- protocol = "x+" should enable x and later versions
+
 local protocols = { "sslv2", "sslv3", "tlsv1", "tlsv1_1", "tlsv1_2" };
 for i = 1, #protocols do protocols[protocols[i] .. "+"] = i - 1; end
 
-local function protocol(a)
-	local min_protocol = protocols[a.protocol];
+-- this interacts with ssl.options as well to add no_x
+local function protocol(config)
+	local min_protocol = protocols[config.protocol];
 	if min_protocol then
-		a.protocol = "sslv23";
+		config.protocol = "sslv23";
 		for i = 1, min_protocol do
-			t_insert(a.options, "no_"..protocols[i]);
+			t_insert(config.options, "no_"..protocols[i]);
 		end
 	end
 end
 
-local function apply(a, b)
-	if type(b) == "table" then
-		for k,v in pairs(b) do
-			(handlers[k] or rawset)(a, k, v);
+-- Merge options from 'new' config into 'config'
+local function apply(config, new)
+	if type(new) == "table" then
+		for field, value in pairs(new) do
+			(handlers[field] or rawset)(config, field, value);
 		end
 	end
 end
 
-local function final(a)
-	local f = { };
-	for k,v in pairs(a) do
-		f[k] = (finalisers[k] or id)(v);
+-- Finalize the config into the form LuaSec expects
+local function final(config)
+	local output = { };
+	for field, value in pairs(config) do
+		output[field] = (finalisers[field] or id)(value);
 	end
-	protocol(f);
-	return f;
+	-- Need to handle protocols last because it adds to the options list
+	protocol(output);
+	return output;
 end
 
 local sslopts_mt = {
-- 
cgit v1.2.3


From 275bfb45fa0972832ffc760d512845d4b0f9be44 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Tue, 15 Dec 2015 13:15:30 +0100
Subject: mod_storage_sql: Fix use of SQLite3 in-memory database (no path
 expansion)

---
 plugins/mod_storage_sql.lua | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua
index adf9f9b6..57d964e2 100644
--- a/plugins/mod_storage_sql.lua
+++ b/plugins/mod_storage_sql.lua
@@ -417,7 +417,9 @@ end
 
 local function normalize_params(params)
 	if params.driver == "SQLite3" then
-		params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite");
+		if params.database ~= ":memory:" then
+			params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite");
+		end
 	end
 	assert(params.driver and params.database, "Configuration error: Both the SQL driver and the database need to be specified");
 	return params;
-- 
cgit v1.2.3