Merge 0.9->0.10
authorMatthew Wild <mwild1@gmail.com>
Thu, 26 Mar 2015 16:48:39 +0000
changeset 6605 61b6a4fc65f1
parent 6590 54306208f30b (current diff)
parent 6604 debd9a7be528 (diff)
child 6606 e702ae7aa3d9
child 6608 03a43bf3ecd2
Merge 0.9->0.10
core/sessionmanager.lua
net/http/server.lua
plugins/mod_http.lua
plugins/mod_s2s/mod_s2s.lua
tests/test.lua
util-src/encodings.c
--- a/.hgtags	Wed Jan 21 02:55:27 2015 +0100
+++ b/.hgtags	Thu Mar 26 16:48:39 2015 +0000
@@ -55,3 +55,4 @@
 8dee696c33cc5f7463c8b9e9fe806b9abd24c115 0.9.5
 e4b998ffc92249ea96716ab878f961f03769339d 0.9.6
 9030b056bd4a5b8402c9b1e1cd65dd35f046032f 0.9.7
+b1c84d220c409b7b17cd41e850576db253406b0a 0.9.8
--- a/core/sessionmanager.lua	Wed Jan 21 02:55:27 2015 +0100
+++ b/core/sessionmanager.lua	Thu Mar 26 16:48:39 2015 +0000
@@ -114,7 +114,7 @@
 -- returns nil, err_type, err, err_message on failure
 function bind_resource(session, resource)
 	if not session.username then return nil, "auth", "not-authorized", "Cannot bind resource before authentication"; end
-	if session.resource then return nil, "cancel", "already-bound", "Cannot bind multiple resources on a single connection"; end
+	if session.resource then return nil, "cancel", "not-allowed", "Cannot bind multiple resources on a single connection"; end
 	-- We don't support binding multiple resources
 
 	local event_payload = { session = session, resource = resource };
--- a/net/http/server.lua	Wed Jan 21 02:55:27 2015 +0100
+++ b/net/http/server.lua	Thu Mar 26 16:48:39 2015 +0000
@@ -189,7 +189,6 @@
 		persistent = persistent;
 		conn = conn;
 		send = _M.send_response;
-		done = _M.finish_response;
 		finish_cb = finish_cb;
 	};
 	conn._http_open_response = response;
@@ -209,7 +208,7 @@
 			err_code, err = 400, "Missing or invalid 'Host' header";
 		end
 	end
-
+	
 	if err then
 		response.status_code = err_code;
 		response:send(events.fire_event("http-error", { code = err_code, message = err }));
@@ -218,7 +217,7 @@
 
 	local event = request.method.." "..host..request.path:match("[^?]*");
 	local payload = { request = request, response = response };
-	log("debug", event);
+	log("debug", "Firing event: %s", event);
 	local result = events.fire_event(event, payload);
 	if result ~= nil then
 		if result ~= true then
@@ -251,30 +250,24 @@
 	response.status_code = 404;
 	response:send(events.fire_event("http-error", { code = 404 }));
 end
-local function prepare_header(response)
+function _M.send_response(response, body)
+	if response.finished then return; end
+	response.finished = true;
+	response.conn._http_open_response = nil;
+	
 	local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
 	local headers = response.headers;
+	body = body or response.body or "";
+	headers.content_length = #body;
+
 	local output = { status_line };
 	for k,v in pairs(headers) do
 		t_insert(output, headerfix[k]..v);
 	end
 	t_insert(output, "\r\n\r\n");
-	return output;
-end
-_M.prepare_header = prepare_header;
-function _M.send_response(response, body)
-	if response.finished then return; end
-	body = body or response.body or "";
-	response.headers.content_length = #body;
-	local output = prepare_header(response);
 	t_insert(output, body);
+
 	response.conn:write(t_concat(output));
-	response:done();
-end
-function _M.finish_response(response)
-	if response.finished then return; end
-	response.finished = true;
-	response.conn._http_open_response = nil;
 	if response.on_destroy then
 		response:on_destroy();
 		response.on_destroy = nil;
--- a/plugins/mod_http.lua	Wed Jan 21 02:55:27 2015 +0100
+++ b/plugins/mod_http.lua	Thu Mar 26 16:48:39 2015 +0000
@@ -74,6 +74,8 @@
 			return url_build(url);
 		end
 	end
+	module:log("warn", "No http ports enabled, can't generate an external URL");
+	return "http://disabled.invalid/";
 end
 
 function module.add_host(module)
@@ -118,6 +120,12 @@
 				module:log("error", "Invalid route in %s, %q. See http://prosody.im/doc/developers/http#routes", app_name, key);
 			end
 		end
+		local services = portmanager.get_active_services();
+		if services:get("https") or services:get("http") then
+			module:log("debug", "Serving '%s' at %s", app_name, module:http_url(app_name, app_path));
+		else
+			module:log("warn", "Not listening on any ports, '%s' will be unreachable", app_name);
+		end
 	end
 
 	local function http_app_removed(event)
--- a/plugins/mod_s2s/mod_s2s.lua	Wed Jan 21 02:55:27 2015 +0100
+++ b/plugins/mod_s2s/mod_s2s.lua	Thu Mar 26 16:48:39 2015 +0000
@@ -499,6 +499,12 @@
 	if not from or (hosts[from] and hosts[from].modules.dialback) then
 		attr["xmlns:db"] = 'jabber:server:dialback';
 	end
+	if not from then
+		attr.from = '';
+	end
+	if not to then
+		attr.to = '';
+	end
 end
 
 -- Session initialization logic shared by incoming and outgoing
--- a/tests/test.lua	Wed Jan 21 02:55:27 2015 +0100
+++ b/tests/test.lua	Thu Mar 26 16:48:39 2015 +0000
@@ -22,6 +22,7 @@
 	dotest "util.sasl.scram"
 
 	dosingletest("test_sasl.lua", "latin1toutf8");
+	dosingletest("test_utf8.lua", "valid");
 end
 
 local verbosity = tonumber(arg[1]) or 2;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_utf8.lua	Thu Mar 26 16:48:39 2015 +0000
@@ -0,0 +1,19 @@
+package.cpath = "../?.so"
+package.path = "../?.lua";
+
+function valid()
+	local encodings = require "util.encodings";
+	local utf8 = assert(encodings.utf8, "no encodings.utf8 module");
+	
+	for line in io.lines("utf8_sequences.txt") do
+		local data = line:match(":%s*([^#]+)"):gsub("%s+", ""):gsub("..", function (c) return string.char(tonumber(c, 16)); end)
+		local expect = line:match("(%S+):");
+		if expect ~= "pass" and expect ~= "fail" then
+			error("unknown expectation: "..line:match("^[^:]+"));
+		end
+		local prefix, style = "  ", valid_style;
+		local valid = utf8.valid(data);
+		assert_equal(valid, utf8.valid(data.." "));
+		assert_equal(valid, expect == "pass", line);
+	end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/utf8_sequences.txt	Thu Mar 26 16:48:39 2015 +0000
@@ -0,0 +1,52 @@
+Should pass: 41 42 43               # Simple ASCII - abc
+Should pass: 41 42 c3 87            # "ABÇ"
+Should pass: 41 42 e1 b8 88         # "ABḈ"
+Should pass: 41 42 f0 9d 9c 8d      # "AB𝜍"
+Should pass: F4 8F BF BF            # Last valid sequence (U+10FFFF)
+Should fail: F4 90 80 80            # First invalid sequence (U+110000)
+Should fail: 80 81 82 83            # Invalid sequence (invalid start byte)
+Should fail: C2 C3                  # Invalid sequence (invalid continuation byte)
+Should fail: C0 43                  # Overlong sequence
+Should fail: F5 80 80 80            # U+140000 (out of range)
+Should fail: ED A0 80               # U+D800 (forbidden by RFC 3629)
+Should fail: ED BF BF               # U+DFFF (forbidden by RFC 3629)
+Should pass: ED 9F BF               # U+D7FF (U+D800 minus 1: allowed)
+Should pass: EE 80 80               # U+E000 (U+D7FF plus 1: allowed)
+Should fail: C0                     # Invalid start byte
+Should fail: C1                     # Invalid start byte
+Should fail: C2                     # Incomplete sequence
+Should fail: F8 88 80 80 80         # 6-byte sequence
+Should pass: 7F                     # Last valid 1-byte sequence (U+00007F)
+Should pass: DF BF                  # Last valid 2-byte sequence (U+0007FF)
+Should pass: EF BF BF               # Last valid 3-byte sequence (U+00FFFF)
+Should pass: 00                     # First valid 1-byte sequence (U+000000)
+Should pass: C2 80                  # First valid 2-byte sequence (U+000080)
+Should pass: E0 A0 80               # First valid 3-byte sequence (U+000800)
+Should pass: F0 90 80 80            # First valid 4-byte sequence (U+000800)
+Should fail: F8 88 80 80 80         # First 5-byte sequence - invalid per RFC 3629
+Should fail: FC 84 80 80 80 80      # First 6-byte sequence - invalid per RFC 3629
+Should pass: EF BF BD               # U+00FFFD (replacement character)
+Should fail: 80                     # First continuation byte
+Should fail: BF                     # Last continuation byte
+Should fail: 80 BF                  # 2 continuation bytes
+Should fail: 80 BF 80               # 3 continuation bytes
+Should fail: 80 BF 80 BF            # 4 continuation bytes
+Should fail: 80 BF 80 BF 80         # 5 continuation bytes
+Should fail: 80 BF 80 BF 80 BF      # 6 continuation bytes
+Should fail: 80 BF 80 BF 80 BF 80   # 7 continuation bytes
+Should fail: FE                     # Impossible byte
+Should fail: FF                     # Impossible byte
+Should fail: FE FE FF FF            # Impossible bytes
+Should fail: C0 AF                  # Overlong "/"
+Should fail: E0 80 AF               # Overlong "/"
+Should fail: F0 80 80 AF            # Overlong "/"
+Should fail: F8 80 80 80 AF         # Overlong "/"
+Should fail: FC 80 80 80 80 AF      # Overlong "/"
+Should fail: C0 80 AF               # Overlong "/" (invalid)
+Should fail: C1 BF                  # Overlong
+Should fail: E0 9F BF               # Overlong
+Should fail: F0 8F BF BF            # Overlong
+Should fail: F8 87 BF BF BF         # Overlong
+Should fail: FC 83 BF BF BF BF      # Overlong
+Should pass: EF BF BE               # U+FFFE (invalid unicode, valid UTF-8)
+Should pass: EF BF BF               # U+FFFF (invalid unicode, valid UTF-8)
--- a/util-src/encodings.c	Wed Jan 21 02:55:27 2015 +0100
+++ b/util-src/encodings.c	Thu Mar 26 16:48:39 2015 +0000
@@ -1,6 +1,7 @@
 /* Prosody IM
 -- Copyright (C) 2008-2010 Matthew Wild
 -- Copyright (C) 2008-2010 Waqas Hussain
+-- Copyright (C) 1994-2015 Lua.org, PUC-Rio.
 -- 
 -- This project is MIT/X11 licensed. Please see the
 -- COPYING file in the source package for more information.
@@ -120,6 +121,88 @@
 	{ NULL,		NULL	}
 };
 
+/******************* UTF-8 ********************/
+
+/*
+ * Adapted from Lua 5.3
+ * Needed because libidn does not validate that input is valid UTF-8
+ */
+
+#define MAXUNICODE	0x10FFFF
+
+/*
+ * Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
+ */
+static const char *utf8_decode (const char *o, int *val) {
+	static unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF};
+	const unsigned char *s = (const unsigned char *)o;
+	unsigned int c = s[0];
+	unsigned int res = 0;  /* final result */
+	if (c < 0x80)  /* ascii? */
+		res = c;
+	else {
+		int count = 0;  /* to count number of continuation bytes */
+		while (c & 0x40) {  /* still have continuation bytes? */
+			int cc = s[++count];  /* read next byte */
+			if ((cc & 0xC0) != 0x80)  /* not a continuation byte? */
+				return NULL;  /* invalid byte sequence */
+			res = (res << 6) | (cc & 0x3F);  /* add lower 6 bits from cont. byte */
+			c <<= 1;  /* to test next bit */
+		}
+		res |= ((c & 0x7F) << (count * 5));  /* add first byte */
+		if (count > 3 || res > MAXUNICODE || res <= limits[count] || (0xd800 <= res && res <= 0xdfff) )
+			return NULL;  /* invalid byte sequence */
+		s += count;  /* skip continuation bytes read */
+	}
+	if (val) *val = res;
+	return (const char *)s + 1;  /* +1 to include first byte */
+}
+
+/*
+ * Check that a string is valid UTF-8
+ * Returns NULL if not
+ */
+const char* check_utf8 (lua_State *L, int idx, size_t *l) {
+	size_t pos, len;
+	const char *s = luaL_checklstring(L, 1, &len);
+	pos = 0;
+	while (pos <= len) {
+		const char *s1 = utf8_decode(s + pos, NULL);
+		if (s1 == NULL) {  /* conversion error? */
+			return NULL;
+		}
+		pos = s1 - s;
+	}
+	if(l != NULL) {
+		*l = len;
+	}
+	return s;
+}
+
+static int Lutf8_valid(lua_State *L) {
+	lua_pushboolean(L, check_utf8(L, 1, NULL) != NULL);
+	return 1;
+}
+
+static int Lutf8_length(lua_State *L) {
+	size_t len;
+	if(!check_utf8(L, 1, &len)) {
+		lua_pushnil(L);
+		lua_pushliteral(L, "invalid utf8");
+		return 2;
+	}
+	lua_pushinteger(L, len);
+	return 1;
+}
+
+static const luaL_Reg Reg_utf8[] =
+{
+	{ "valid",	Lutf8_valid	},
+	{ "length",	Lutf8_length	},
+	{ NULL,		NULL	}
+};
+
+
 /***************** STRINGPREP *****************/
 #ifdef USE_STRINGPREP_ICU
 
@@ -216,8 +299,8 @@
 		lua_pushnil(L);
 		return 1;
 	}
-	s = lua_tolstring(L, 1, &len);
-	if (len >= 1024) {
+	s = check_utf8(L, 1, &len);
+	if (s == NULL || len >= 1024 || len != strlen(s)) {
 		lua_pushnil(L);
 		return 1; /* TODO return error message */
 	}
@@ -324,7 +407,11 @@
 static int Lidna_to_ascii(lua_State *L)		/** idna.to_ascii(s) */
 {
 	size_t len;
-	const char *s = luaL_checklstring(L, 1, &len);
+	const char *s = check_utf8(L, 1, &len);
+	if (s == NULL || len != strlen(s)) {
+		lua_pushnil(L);
+		return 1; /* TODO return error message */
+	}
 	char* output = NULL;
 	int ret = idna_to_ascii_8z(s, &output, IDNA_USE_STD3_ASCII_RULES);
 	if (ret == IDNA_SUCCESS) {
@@ -365,26 +452,40 @@
 
 /***************** end *****************/
 
+static const luaL_Reg Reg[] =
+{
+	{ NULL,		NULL	}
+};
+
 LUALIB_API int luaopen_util_encodings(lua_State *L)
 {
 #ifdef USE_STRINGPREP_ICU
 	init_icu();
 #endif
-	lua_newtable(L);
+	luaL_register(L, "encodings", Reg);
 
+	lua_pushliteral(L, "base64");
 	lua_newtable(L);
 	luaL_register(L, NULL, Reg_base64);
-	lua_setfield(L, -2, "base64");
+	lua_settable(L,-3);
 
+	lua_pushliteral(L, "stringprep");
 	lua_newtable(L);
 	luaL_register(L, NULL, Reg_stringprep);
-	lua_setfield(L, -2, "stringprep");
+	lua_settable(L,-3);
 
+	lua_pushliteral(L, "idna");
 	lua_newtable(L);
 	luaL_register(L, NULL, Reg_idna);
-	lua_setfield(L, -2, "idna");
+	lua_settable(L,-3);
 
+	lua_pushliteral(L, "utf8");
+	lua_newtable(L);
+	luaL_register(L, NULL, Reg_utf8);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "version");			/** version */
 	lua_pushliteral(L, "-3.14");
-	lua_setfield(L, -2, "version");
+	lua_settable(L,-3);
 	return 1;
 }