# HG changeset patch # User Matthew Wild # Date 1427388543 0 # Node ID e702ae7aa3d9c4e0b9d56deeccdc1083528d9651 # Parent 93423244dc5b35557d8a5040894fc4c91724b655# Parent 61b6a4fc65f1c932477ba3b056f7ff859651cc0e Merge 0.10->trunk diff -r 93423244dc5b -r e702ae7aa3d9 .hgtags --- a/.hgtags Tue Mar 24 13:41:18 2015 +0000 +++ b/.hgtags Thu Mar 26 16:49:03 2015 +0000 @@ -55,3 +55,4 @@ 8dee696c33cc5f7463c8b9e9fe806b9abd24c115 0.9.5 e4b998ffc92249ea96716ab878f961f03769339d 0.9.6 9030b056bd4a5b8402c9b1e1cd65dd35f046032f 0.9.7 +b1c84d220c409b7b17cd41e850576db253406b0a 0.9.8 diff -r 93423244dc5b -r e702ae7aa3d9 core/sessionmanager.lua --- a/core/sessionmanager.lua Tue Mar 24 13:41:18 2015 +0000 +++ b/core/sessionmanager.lua Thu Mar 26 16:49:03 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 }; diff -r 93423244dc5b -r e702ae7aa3d9 net/http/server.lua --- a/net/http/server.lua Tue Mar 24 13:41:18 2015 +0000 +++ b/net/http/server.lua Thu Mar 26 16:49:03 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; diff -r 93423244dc5b -r e702ae7aa3d9 plugins/mod_http.lua --- a/plugins/mod_http.lua Tue Mar 24 13:41:18 2015 +0000 +++ b/plugins/mod_http.lua Thu Mar 26 16:49:03 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) diff -r 93423244dc5b -r e702ae7aa3d9 plugins/mod_s2s/mod_s2s.lua --- a/plugins/mod_s2s/mod_s2s.lua Tue Mar 24 13:41:18 2015 +0000 +++ b/plugins/mod_s2s/mod_s2s.lua Thu Mar 26 16:49:03 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 diff -r 93423244dc5b -r e702ae7aa3d9 tests/test.lua --- a/tests/test.lua Tue Mar 24 13:41:18 2015 +0000 +++ b/tests/test.lua Thu Mar 26 16:49:03 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; diff -r 93423244dc5b -r e702ae7aa3d9 tests/test_utf8.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_utf8.lua Thu Mar 26 16:49:03 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 diff -r 93423244dc5b -r e702ae7aa3d9 tests/utf8_sequences.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/utf8_sequences.txt Thu Mar 26 16:49:03 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) diff -r 93423244dc5b -r e702ae7aa3d9 util-src/encodings.c --- a/util-src/encodings.c Tue Mar 24 13:41:18 2015 +0000 +++ b/util-src/encodings.c Thu Mar 26 16:49:03 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; }