Merge 0.10->trunk
authorKim Alvefur <zash@zash.se>
Thu, 18 Aug 2016 17:36:46 +0200
changeset 7608 2481ebc7f715
parent 7607 36408ef6c8c9 (current diff)
parent 7585 e080b8b4f3cb (diff)
child 7609 f40f1d9b7872
Merge 0.10->trunk
plugins/mod_http.lua
--- a/net/http/codes.lua	Thu Aug 18 17:23:06 2016 +0200
+++ b/net/http/codes.lua	Thu Aug 18 17:36:46 2016 +0200
@@ -55,6 +55,7 @@
 	[428] = "Precondition Required";
 	[429] = "Too Many Requests";
 	[431] = "Request Header Fields Too Large";
+	[451] = "Unavailable For Legal Reasons";
 
 	[500] = "Internal Server Error";
 	[501] = "Not Implemented";
@@ -70,4 +71,4 @@
 };
 
 for k,v in pairs(response_codes) do response_codes[k] = k.." "..v; end
-return setmetatable(response_codes, { __index = function(t, k) return k.." Unassigned"; end })
+return setmetatable(response_codes, { __index = function(_, k) return k.." Unassigned"; end })
--- a/net/http/parser.lua	Thu Aug 18 17:23:06 2016 +0200
+++ b/net/http/parser.lua	Thu Aug 18 17:36:46 2016 +0200
@@ -1,5 +1,6 @@
 local tonumber = tonumber;
 local assert = assert;
+local t_insert, t_concat = table.insert, table.concat;
 local url_parse = require "socket.url".parse;
 local urldecode = require "util.http".urldecode;
 
@@ -27,7 +28,9 @@
 function httpstream.new(success_cb, error_cb, parser_type, options_cb)
 	local client = true;
 	if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end
-	local buf = "";
+	local buf, buflen, buftable = {}, 0, true;
+	local bodylimit = tonumber(options_cb and options_cb().body_size_limit) or 10*1024*1024;
+	local buflimit = tonumber(options_cb and options_cb().buffer_size_limit) or bodylimit * 2;
 	local chunked, chunk_size, chunk_start;
 	local state = nil;
 	local packet;
@@ -35,9 +38,10 @@
 	local have_body;
 	local error;
 	return {
-		feed = function(self, data)
+		feed = function(_, data)
 			if error then return nil, "parse has failed"; end
 			if not data then -- EOF
+				if buftable then buf, buftable = t_concat(buf), false; end
 				if state and client and not len then -- reading client body until EOF
 					packet.body = buf;
 					success_cb(packet);
@@ -46,9 +50,17 @@
 				end
 				return;
 			end
-			buf = buf..data;
-			while #buf > 0 do
+			if buftable then
+				t_insert(buf, data);
+			else
+				buf = { buf, data };
+				buftable = true;
+			end
+			buflen = buflen + #data;
+			if buflen > buflimit then error = true; return error_cb("max-buffer-size-exceeded"); end
+			while buflen > 0 do
 				if state == nil then -- read request
+					if buftable then buf, buftable = t_concat(buf), false; end
 					local index = buf:find("\r\n\r\n", nil, true);
 					if not index then return; end -- not enough data
 					local method, path, httpversion, status_code, reason_phrase;
@@ -79,6 +91,7 @@
 					if not first_line then error = true; return error_cb("invalid-status-line"); end
 					chunked = have_body and headers["transfer-encoding"] == "chunked";
 					len = tonumber(headers["content-length"]); -- TODO check for invalid len
+					if len and len > bodylimit then error = true; return error_cb("content-length-limit-exceeded"); end
 					if client then
 						-- FIXME handle '100 Continue' response (by skipping it)
 						if not have_body then len = 0; end
@@ -115,11 +128,13 @@
 						};
 					end
 					buf = buf:sub(index + 4);
+					buflen = #buf;
 					state = true;
 				end
 				if state then -- read body
 					if client then
 						if chunked then
+							if buftable then buf, buftable = t_concat(buf), false; end
 							if not buf:find("\r\n", nil, true) then
 								return;
 							end -- not enough data
@@ -132,25 +147,29 @@
 								state, chunk_size = nil, nil;
 								buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped
 								success_cb(packet);
-							elseif #buf - chunk_start - 2 >= chunk_size then -- we have a chunk
+							elseif buflen - chunk_start - 2 >= chunk_size then -- we have a chunk
 								packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1));
 								buf = buf:sub(chunk_start + chunk_size + 2);
 								chunk_size, chunk_start = nil, nil;
 							else -- Partial chunk remaining
 								break;
 							end
-						elseif len and #buf >= len then
+						elseif len and buflen >= len then
+							if buftable then buf, buftable = t_concat(buf), false; end
 							if packet.code == 101 then
-								packet.body, buf = buf, "";
+								packet.body, buf, buflen, buftable = buf, {}, 0, true;
 							else
 								packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
+								buflen = #buf;
 							end
 							state = nil; success_cb(packet);
 						else
 							break;
 						end
-					elseif #buf >= len then
+					elseif buflen >= len then
+						if buftable then buf, buftable = t_concat(buf), false; end
 						packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
+						buflen = #buf;
 						state = nil; success_cb(packet);
 					else
 						break;
--- a/net/http/server.lua	Thu Aug 18 17:23:06 2016 +0200
+++ b/net/http/server.lua	Thu Aug 18 17:36:46 2016 +0200
@@ -22,6 +22,7 @@
 local listener = {};
 local hosts = {};
 local default_host;
+local options = {};
 
 local function is_wildcard_event(event)
 	return event:sub(-2, -1) == "/*";
@@ -31,7 +32,7 @@
 end
 
 local _handlers = events._handlers;
-local recent_wildcard_events = cache.new(10000, function (key, value)
+local recent_wildcard_events = cache.new(10000, function (key, value) -- luacheck: ignore 212/value
 	rawset(_handlers, key, nil);
 end);
 
@@ -133,7 +134,10 @@
 		sessions[conn] = nil;
 		conn:close();
 	end
-	sessions[conn] = parser_new(success_cb, error_cb);
+	local function options_cb()
+		return options;
+	end
+	sessions[conn] = parser_new(success_cb, error_cb, "server", options_cb);
 end
 
 function listener.ondisconnect(conn)
@@ -170,7 +174,7 @@
 	end
 });
 
-function _M.hijack_response(response, listener)
+function _M.hijack_response(response, listener) -- luacheck: ignore
 	error("TODO");
 end
 function handle_request(conn, request, finish_cb)
@@ -350,6 +354,9 @@
 function _M.fire_event(event, ...)
 	return events.fire_event(event, ...);
 end
+function _M.set_option(name, value)
+	options[name] = value;
+end
 
 _M.listener = listener;
 _M.codes = codes;
--- a/plugins/mod_http.lua	Thu Aug 18 17:23:06 2016 +0200
+++ b/plugins/mod_http.lua	Thu Aug 18 17:36:46 2016 +0200
@@ -18,6 +18,9 @@
 
 server.set_default_host(module:get_option_string("http_default_host"));
 
+server.set_option("body_size_limit", module:get_option_number("http_max_content_size"));
+server.set_option("buffer_size_limit", module:get_option_number("http_max_buffer_size"));
+
 local function normalize_path(path)
 	if path:sub(-1,-1) == "/" then path = path:sub(1, -2); end
 	if path:sub(1,1) ~= "/" then path = "/"..path; end
--- a/plugins/mod_register.lua	Thu Aug 18 17:23:06 2016 +0200
+++ b/plugins/mod_register.lua	Thu Aug 18 17:36:46 2016 +0200
@@ -226,7 +226,7 @@
 						session.send(st.error_reply(stanza, "cancel", "not-acceptable", "You are not allowed to register an account."));
 						return true;
 					elseif min_seconds_between_registrations and not whitelisted_ips[session.ip] then
-						if check_throttle(session.ip) then
+						if not check_throttle(session.ip) then
 							session.send(st.error_reply(stanza, "wait", "not-acceptable"));
 							return true;
 						end
--- a/tools/ejabberdsql2prosody.lua	Thu Aug 18 17:23:06 2016 +0200
+++ b/tools/ejabberdsql2prosody.lua	Thu Aug 18 17:36:46 2016 +0200
@@ -42,10 +42,6 @@
 	if expected and ch ~= expected then error("expected: "..expected.."; got: "..(ch or "nil").." on line "..line); end
 	return ch;
 end
-local function pushback(ch)
-	if last then error(); end
-	last = ch;
-end
 local function peek()
 	if not last then last = read(); end
 	return last;
@@ -176,9 +172,9 @@
 ------
 end
 
-local arg, host = ...;
+local arg, hostname = ...;
 local help = "/? -? ? /h -h /help -help --help";
-if not(arg and host) or help:find(arg, 1, true) then
+if not(arg and hostname) or help:find(arg, 1, true) then
 	print([[ejabberd SQL DB dump importer for Prosody
 
   Usage: ejabberdsql2prosody.lua filename.txt hostname
@@ -201,8 +197,8 @@
 	--["vcard_search"] = {};
 }
 local NULL = {};
-local t = parseFile(arg);
-for name, data in pairs(t) do
+local parsed = parseFile(arg);
+for name, data in pairs(parsed) do
 	local m = map[name];
 	if m then
 		if #data > 0 and #data[1] ~= #m then
@@ -219,10 +215,10 @@
 end
 --print(serialize(t));
 
-for i, row in ipairs(t["users"] or NULL) do
+for _, row in ipairs(parsed["users"] or NULL) do
 	local node, password = row.username, row.password;
-	local ret, err = dm.store(node, host, "accounts", {password = password});
-	print("["..(err or "success").."] accounts: "..node.."@"..host);
+	local ret, err = dm.store(node, hostname, "accounts", {password = password});
+	print("["..(err or "success").."] accounts: "..node.."@"..hostname);
 end
 
 function roster(node, host, jid, item)
@@ -258,7 +254,7 @@
 	local ret, err = dm.list_append(node, host, "offline", st.preserialize(stanza));
 	print("["..(err or "success").."] offline: " ..node.."@"..host.." - "..os.date("!%Y-%m-%dT%H:%M:%SZ", t));
 end
-for i, row in ipairs(t["rosterusers"] or NULL) do
+for _, row in ipairs(parsed["rosterusers"] or NULL) do
 	local node, contact = row.username, row.jid;
 	local name = row.nick;
 	if name == "" then name = nil; end
@@ -278,42 +274,42 @@
 	elseif ask == "O" then
 		ask = "subscribe";
 	elseif ask == "I" then
-		roster_pending(node, host, contact);
+		roster_pending(node, hostname, contact);
 		ask = nil;
 	elseif ask == "B" then
-		roster_pending(node, host, contact);
+		roster_pending(node, hostname, contact);
 		ask = "subscribe";
 	else error("Unknown ask type: "..ask); end
 	local item = {name = name, ask = ask, subscription = subscription, groups = {}};
-	roster(node, host, contact, item);
+	roster(node, hostname, contact, item);
 end
-for i, row in ipairs(t["rostergroups"] or NULL) do
-	roster_group(row.username, host, row.jid, row.grp);
+for _, row in ipairs(parsed["rostergroups"] or NULL) do
+	roster_group(row.username, hostname, row.jid, row.grp);
 end
-for i, row in ipairs(t["vcard"] or NULL) do
+for _, row in ipairs(parsed["vcard"] or NULL) do
 	local stanza, err = parse_xml(row.vcard);
 	if stanza then
-		local ret, err = dm.store(row.username, host, "vcard", st.preserialize(stanza));
-		print("["..(err or "success").."] vCard: "..row.username.."@"..host);
+		local ret, err = dm.store(row.username, hostname, "vcard", st.preserialize(stanza));
+		print("["..(err or "success").."] vCard: "..row.username.."@"..hostname);
 	else
-		print("[error] vCard XML parse failed: "..row.username.."@"..host);
+		print("[error] vCard XML parse failed: "..row.username.."@"..hostname);
 	end
 end
-for i, row in ipairs(t["private_storage"] or NULL) do
+for _, row in ipairs(parsed["private_storage"] or NULL) do
 	local stanza, err = parse_xml(row.data);
 	if stanza then
-		private_storage(row.username, host, row.namespace, stanza);
+		private_storage(row.username, hostname, row.namespace, stanza);
 	else
-		print("[error] Private XML parse failed: "..row.username.."@"..host);
+		print("[error] Private XML parse failed: "..row.username.."@"..hostname);
 	end
 end
-table.sort(t["spool"] or NULL, function(a,b) return a.seq < b.seq; end); -- sort by sequence number, just in case
+table.sort(parsed["spool"] or NULL, function(a,b) return a.seq < b.seq; end); -- sort by sequence number, just in case
 local time_offset = os.difftime(os.time(os.date("!*t")), os.time(os.date("*t"))) -- to deal with timezones
 local date_parse = function(s)
 	local year, month, day, hour, min, sec = s:match("(....)-?(..)-?(..)T(..):(..):(..)");
 	return os.time({year=year, month=month, day=day, hour=hour, min=min, sec=sec-time_offset});
 end
-for i, row in ipairs(t["spool"] or NULL) do
+for _, row in ipairs(parsed["spool"] or NULL) do
 	local stanza, err = parse_xml(row.xml);
 	if stanza then
 		local last_child = stanza.tags[#stanza.tags];
@@ -321,8 +317,8 @@
 		if last_child.name ~= "x" and last_child.attr.xmlns ~= "jabber:x:delay" then error("Last child of offline message is not a timestamp"); end
 		stanza[#stanza], stanza.tags[#stanza.tags] = nil, nil;
 		local t = date_parse(last_child.attr.stamp);
-		offline_msg(row.username, host, t, stanza);
+		offline_msg(row.username, hostname, t, stanza);
 	else
-		print("[error] Offline message XML parsing failed: "..row.username.."@"..host);
+		print("[error] Offline message XML parsing failed: "..row.username.."@"..hostname);
 	end
 end