# HG changeset patch # User Kim Alvefur # Date 1431034794 -7200 # Node ID d85d5b0bf9773f1c7f67d168a12f375cdf9d8403 # Parent e4867211cddbb017330fa9ba53351c90026f23cd# Parent 2440a75e868f5b0376d38b4df3b511914d469ef5 Merge with Goffi diff -r 2440a75e868f -r d85d5b0bf977 mod_auto_accept_subscriptions/mod_auto_accept_subscriptions.lua --- a/mod_auto_accept_subscriptions/mod_auto_accept_subscriptions.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_auto_accept_subscriptions/mod_auto_accept_subscriptions.lua Thu May 07 23:39:54 2015 +0200 @@ -7,7 +7,7 @@ local to_bare, from_bare = jid.bare(stanza.attr.to), jid.bare(stanza.attr.from); local node, host = jid.split(to_bare); stanza.attr.from, stanza.attr.to = from_bare, to_bare; - module:log("info", "Auto-accepting inbound subscription request from %s to %s", from_bare, to_bare); + module:log("info", "Auto-accepting inbound subscription request from %s to %s", tostring(from_bare), tostring(to_bare)); if not rostermanager.is_contact_subscribed(node, host, from_bare) then core_post_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"}), true); -- acknowledging receipt @@ -35,7 +35,7 @@ end end end - module:log("warn", "Failed to auto-accept subscription request from %s to %s", from_bare, to_bare); + module:log("warn", "Failed to auto-accept subscription request from %s to %s", tostring(from_bare), tostring(to_bare)); end module:hook("presence/bare", function (event) diff -r 2440a75e868f -r d85d5b0bf977 mod_block_registrations/mod_block_registrations.lua --- a/mod_block_registrations/mod_block_registrations.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_block_registrations/mod_block_registrations.lua Thu May 07 23:39:54 2015 +0200 @@ -10,7 +10,7 @@ if block_users:contains(username) then return true; end for pattern in block_patterns do - if username:match(pattern) then + if username:find(pattern) then return true; end end diff -r 2440a75e868f -r d85d5b0bf977 mod_checkcerts/mod_checkcerts.lua --- a/mod_checkcerts/mod_checkcerts.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_checkcerts/mod_checkcerts.lua Thu May 07 23:39:54 2015 +0200 @@ -1,6 +1,6 @@ local ssl = require"ssl"; local datetime_parse = require"util.datetime".parse; -local load_cert = ssl.x509 and ssl.x509.load; +local load_cert = ssl.loadcertificate; local st = require"util.stanza" -- These are in days. diff -r 2440a75e868f -r d85d5b0bf977 mod_http_muc_log/mod_http_muc_log.lua --- a/mod_http_muc_log/mod_http_muc_log.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_http_muc_log/mod_http_muc_log.lua Thu May 07 23:39:54 2015 +0200 @@ -238,7 +238,7 @@ local next_when = find_once(room, { after = last }, 3); if next_when then next_when = datetime.date(next_when); - module:log("debug", "Next message: %s", datetime.datetime(next_when)); + module:log("debug", "Next message: %s", next_when); else next_when = ""; end @@ -247,7 +247,7 @@ local prev_when = find_once(room, { before = first, reverse = true }, 3); if prev_when then prev_when = datetime.date(prev_when); - module:log("debug", "Previous message: %s", datetime.datetime(prev_when)); + module:log("debug", "Previous message: %s", prev_when); else prev_when = ""; end diff -r 2440a75e868f -r d85d5b0bf977 mod_log_slow_events/mod_log_slow_events.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_log_slow_events/mod_log_slow_events.lua Thu May 07 23:39:54 2015 +0200 @@ -0,0 +1,55 @@ +local time = require "socket".gettime; +local base64_decode = require "util.encodings".base64.decode; + +local max_seconds = module:get_option_number("log_slow_events_threshold", 0.5); + +function event_wrapper(handlers, event_name, event_data) + local start = time(); + local ret = handlers(event_name, event_data); + local duration = time()-start; + if duration > max_seconds then + local data = {}; + if event_data then + local function log_data(name, value) + if value then + table.insert(data, ("%s=%q"):format(name, value)); + return true; + end + end + local sess = event_data.origin or event_data.session; + if sess then + log_data("ip", sess.ip); + if not log_data("full_jid", sess.full_jid) then + log_data("username", sess.username); + end + log_data("type", sess.type); + log_data("host", sess.host); + end + local stanza = event_data.stanza; + if stanza then + log_data("stanza", tostring(stanza)); + else + local request = event_data.request; + if request then + log_data("http_method", request.method); + log_data("http_path", request.path); + local auth = request.headers.authorization; + if auth then + local creds = auth:match("^Basic +(.+)$"); + if creds then + local user = string.match(base64_decode(creds) or "", "^([^:]+):"); + log_data("http_user", user); + end + end + end + end + end + module:log("warn", "Slow event '%s' took %0.2fs: %s", event_name, duration, next(data) and table.concat(data, ", ") or "no recognised data"); + end + return ret; +end + +module:wrap_event(false, event_wrapper); +local http_events = require "net.http.server"._events; +module:wrap_object_event(http_events, false, event_wrapper); + diff -r 2440a75e868f -r d85d5b0bf977 mod_mam/mod_mam.lua --- a/mod_mam/mod_mam.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_mam/mod_mam.lua Thu May 07 23:39:54 2015 +0200 @@ -39,10 +39,10 @@ local archive = module:open_store(archive_store, "archive"); if not archive or archive.name == "null" then module:log("error", "Could not open archive storage"); - return + return; elseif not archive.find then module:log("error", "mod_%s does not support archiving, switch to mod_storage_sql2", archive._provided_by); - return + return; end -- Handle prefs. @@ -52,16 +52,18 @@ if stanza.attr.type == "get" then local prefs = prefs_to_stanza(get_prefs(user)); local reply = st.reply(stanza):add_child(prefs); - return origin.send(reply); + origin.send(reply); else -- type == "set" local new_prefs = stanza:get_child("prefs", xmlns_mam); local prefs = prefs_from_stanza(new_prefs); local ok, err = set_prefs(user, prefs); if not ok then - return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err))); + origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err))); + else + origin.send(st.reply(stanza)); end - return origin.send(st.reply(stanza)); end + return true; end); local query_form = dataform { @@ -74,7 +76,8 @@ -- Serve form module:hook("iq-get/self/"..xmlns_mam..":query", function(event) local origin, stanza = event.origin, event.stanza; - return origin.send(st.reply(stanza):add_child(query_form:form())); + origin.send(st.reply(stanza):add_child(query_form:form())); + return true; end); -- Handle archive queries @@ -90,17 +93,18 @@ local err; form, err = query_form:data(form); if err then - return origin.send(st.error_reply(stanza, "modify", "bad-request", select(2, next(err)))) + origin.send(st.error_reply(stanza, "modify", "bad-request", select(2, next(err)))); + return true; end qwith, qstart, qend = form["with"], form["start"], form["end"]; qwith = qwith and jid_bare(qwith); -- dataforms does jidprep end if qstart or qend then -- Validate timestamps - local vstart, vend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend)) + local vstart, vend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend)); if (qstart and not vstart) or (qend and not vend) then origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid timestamp")) - return true + return true; end qstart, qend = vstart, vend; end @@ -120,24 +124,34 @@ local data, err = archive:find(origin.username, { start = qstart; ["end"] = qend; -- Time range with = qwith; - limit = qmax; + limit = qmax + 1; before = before; after = after; reverse = reverse; total = true; }); if not data then - return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); + origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); + return true; end - local count = err; + local total = err; - origin.send(st.reply(stanza)) + origin.send(st.reply(stanza)); local msg_reply_attr = { to = stanza.attr.from, from = stanza.attr.to }; + local results = {}; + -- Wrap it in stuff and deliver - local fwd_st, first, last; + local first, last; + local count = 0; + local complete = "true"; for id, item, when in data do - fwd_st = st.message(msg_reply_attr) + count = count + 1; + if count > qmax then + complete = nil; + break; + end + local fwd_st = st.message(msg_reply_attr) :tag("result", { xmlns = xmlns_mam, queryid = qid, id = id }) :tag("forwarded", { xmlns = xmlns_forward }) :tag("delay", { xmlns = xmlns_delay, stamp = timestamp(when) }):up(); @@ -151,16 +165,27 @@ if not first then first = id; end last = id; - origin.send(fwd_st); + if reverse then + results[count] = fwd_st; + else + origin.send(fwd_st); + end end + if reverse then + for i = #results, 1, -1 do + origin.send(results[i]); + end + end + -- That's all folks! module:log("debug", "Archive query %s completed", tostring(qid)); if reverse then first, last = last, first; end - return origin.send(st.message(msg_reply_attr) - :tag("fin", { xmlns = xmlns_mam, queryid = qid }) + origin.send(st.message(msg_reply_attr) + :tag("fin", { xmlns = xmlns_mam, queryid = qid, complete = complete }) :add_child(rsm.generate { - first = first, last = last, count = count })); + first = first, last = last, count = total })); + return true; end); local function has_in_roster(user, who) @@ -173,15 +198,15 @@ -- TODO Cache this? local prefs = get_prefs(user); local rule = prefs[who]; - module:log("debug", "%s's rule for %s is %s", user, who, tostring(rule)) + module:log("debug", "%s's rule for %s is %s", user, who, tostring(rule)); if rule ~= nil then return rule; else -- Below could be done by a metatable local default = prefs[false]; - module:log("debug", "%s's default rule is %s", user, tostring(default)) + module:log("debug", "%s's default rule is %s", user, tostring(default)); if default == nil then default = global_default_policy; - module:log("debug", "Using global default rule, %s", tostring(default)) + module:log("debug", "Using global default rule, %s", tostring(default)); end if default == "roster" then return has_in_roster(user, who); @@ -238,5 +263,9 @@ module:hook("message/bare", message_handler, 2); module:hook("message/full", message_handler, 2); -module:add_feature(xmlns_mam); +module:add_feature(xmlns_mam); -- COMPAT with XEP-0313 v 0.1 +module:hook("account-disco-info", function(event) + event.reply:tag("feature", {var=xmlns_mam}):up(); +end); + diff -r 2440a75e868f -r d85d5b0bf977 mod_mam_muc/mod_mam_muc.lua --- a/mod_mam_muc/mod_mam_muc.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_mam_muc/mod_mam_muc.lua Thu May 07 23:39:54 2015 +0200 @@ -139,7 +139,8 @@ -- Serve form module:hook("iq-get/bare/"..xmlns_mam..":query", function(event) local origin, stanza = event.origin, event.stanza; - return origin.send(st.reply(stanza):add_child(query_form:form())); + origin.send(st.reply(stanza):add_child(query_form:form())); + return true; end); -- Handle archive queries @@ -172,7 +173,8 @@ local err; form, err = query_form:data(form); if err then - return origin.send(st.error_reply(stanza, "modify", "bad-request", select(2, next(err)))) + origin.send(st.error_reply(stanza, "modify", "bad-request", select(2, next(err)))); + return true; end qstart, qend = form["start"], form["end"]; end @@ -197,7 +199,7 @@ -- Load all the data! local data, err = archive:find(room_node, { start = qstart; ["end"] = qend; -- Time range - limit = qmax; + limit = qmax + 1; before = before; after = after; reverse = reverse; total = true; @@ -207,15 +209,24 @@ if not data then return origin.send(st.error_reply(stanza, "cancel", "internal-server-error")); end - local count = err; + local total = err; origin.send(st.reply(stanza)) local msg_reply_attr = { to = stanza.attr.from, from = stanza.attr.to }; + local results = {}; + -- Wrap it in stuff and deliver - local fwd_st, first, last; + local first, last; + local count = 0; + local complete = "true"; for id, item, when in data do - fwd_st = st.message(msg_reply_attr) + count = count + 1; + if count > qmax then + complete = nil; + break; + end + local fwd_st = st.message(msg_reply_attr) :tag("result", { xmlns = xmlns_mam, queryid = qid, id = id }) :tag("forwarded", { xmlns = xmlns_forward }) :tag("delay", { xmlns = xmlns_delay, stamp = timestamp(when) }):up(); @@ -229,16 +240,27 @@ if not first then first = id; end last = id; - origin.send(fwd_st); + if reverse then + results[count] = fwd_st; + else + origin.send(fwd_st); + end end + if reverse then + for i = #results, 1, -1 do + origin.send(results[i]); + end + end + -- That's all folks! module:log("debug", "Archive query %s completed", tostring(qid)); if reverse then first, last = last, first; end - return origin.send(st.message(msg_reply_attr) - :tag("fin", { xmlns = xmlns_mam, queryid = qid }) + origin.send(st.message(msg_reply_attr) + :tag("fin", { xmlns = xmlns_mam, queryid = qid, complete = complete }) :add_child(rsm.generate { - first = first, last = last, count = count })); + first = first, last = last, count = total })); + return true; end); module:hook("muc-get-history", function (event) @@ -360,3 +382,7 @@ -- And role/affiliation changes? module:add_feature(xmlns_mam); + +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var=xmlns_mam}):up(); +end); diff -r 2440a75e868f -r d85d5b0bf977 mod_measure_cpu/mod_measure_cpu.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_measure_cpu/mod_measure_cpu.lua Thu May 07 23:39:54 2015 +0200 @@ -0,0 +1,33 @@ +module:set_global(); + +local measure = require"core.statsmanager".measure; +local mt = require"util.multitable"; +local get_time = require "socket".gettime; +local get_clock = os.clock; + +local measure_cpu_now = measure("amount", "cpu.percent"); -- Current percentage + +local last_cpu_wall, last_cpu_clock; +module:hook("stats-update", function () + local new_wall, new_clock = get_time(), get_clock(); + local pc = 0; + if last_cpu_wall then + pc = 100/((new_wall-last_cpu_wall)/(new_clock-last_cpu_clock)); + end + last_cpu_wall, last_cpu_clock = new_wall, new_clock; + + measure_cpu_now(pc); +end); + +-- Some metadata for mod_munin +local munin_meta = mt.new(); munin_meta.data = module:shared"munin/meta"; +local key = "global_cpu_amount"; + +munin_meta:set(key, "", "graph_args", "--base 1000 -r --lower-limit 0 --upper-limit 100"); +munin_meta:set(key, "", "graph_title", "Prosody CPU Usage"); +munin_meta:set(key, "", "graph_vlabel", "%"); +munin_meta:set(key, "", "graph_category", "cpu"); + +munin_meta:set(key, "percent", "label", "CPU Usage"); +munin_meta:set(key, "percent", "min", "0"); + diff -r 2440a75e868f -r d85d5b0bf977 mod_munin/mod_munin.lua --- a/mod_munin/mod_munin.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_munin/mod_munin.lua Thu May 07 23:39:54 2015 +0200 @@ -58,7 +58,7 @@ conn:write(s_format("%s %s\n", k, value)); end for _, name, k, value in meta:iter(stat, nil, nil) do - if name ~= "" then + if name ~= "" and not ignore_stats:contains(name) then conn:write(s_format("%s.%s %s\n", name, k, value)); end end @@ -69,7 +69,9 @@ local stat = line:match("%s(%S+)"); if not stat then conn:write("# Unknown service\n.\n"); return end for _, name, value in data:iter(stat, nil) do - conn:write(s_format("%s.value %s\n", name, tostring(value))); + if not ignore_stats:contains(name) then + conn:write(s_format("%s.value %.12f\n", name, value)); + end end conn:write(".\n"); end diff -r 2440a75e868f -r d85d5b0bf977 mod_proctitle/mod_proctitle.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_proctitle/mod_proctitle.lua Thu May 07 23:39:54 2015 +0200 @@ -0,0 +1,10 @@ +-- Changes the process name to 'prosody' rather than 'lua'/'lua5.1' +-- Copyright (C) 2015 Rob Hoelz +-- +-- This file is MIT/X11 licensed. + +-- To use this module, you'll need the proctitle Lua library: +-- https://github.com/hoelzro/lua-proctitle +local proctitle = require 'proctitle'; + +proctitle 'prosody'; diff -r 2440a75e868f -r d85d5b0bf977 mod_s2s_auth_dane/mod_s2s_auth_dane.lua --- a/mod_s2s_auth_dane/mod_s2s_auth_dane.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_s2s_auth_dane/mod_s2s_auth_dane.lua Thu May 07 23:39:54 2015 +0200 @@ -68,15 +68,19 @@ if host_session.dane ~= nil then return end -- Has already done a lookup if host_session.direction == "incoming" then + if not host_session.from_host then + module:log("debug", "Session doesn't have a 'from' host set"); + return; + end -- We don't know what hostname or port to use for Incoming connections -- so we do a SRV lookup and then request TLSA records for each SRV -- Most servers will probably use the same certificate on outgoing -- and incoming connections, so this should work well local name = host_session.from_host and idna_to_ascii(host_session.from_host); if not name then - module:log("error", "Could not convert '%s' to ASCII for DNS lookup", tostring(host_session.from_host)); - return; - end + module:log("warn", "Could not convert '%s' to ASCII for DNS lookup", tostring(host_session.from_host)); + return; + end host_session.dane = dns_lookup(function (answer, err) host_session.dane = false; -- Mark that we already did the lookup @@ -90,7 +94,7 @@ return cb(host_session); end - local n = #answer + local n = answer.n or #answer; if n == 0 then -- No SRV records, we could proceed with the domainname and -- default port but that will currently not work properly since @@ -101,14 +105,30 @@ return cb(host_session); -- No service ... This shouldn't happen? end local srv_hosts = { answer = answer }; - local dane = {}; - host_session.dane = dane; host_session.srv_hosts = srv_hosts; + local dane; for _, record in ipairs(answer) do t_insert(srv_hosts, record.srv); dns_lookup(function(dane_answer) n = n - 1; - if dane_answer.bogus then + -- There are three kinds of answers + -- Insecure, Secure and Bogus + -- + -- We collect Secure answers for later use + -- + -- Insecure (legacy) answers are simply ignored + -- + -- If we get a Bogus (dnssec error) reply, keep the + -- status around. If there were only bogus replies, the + -- connection will be aborted. If there were at least + -- one non-Bogus reply, we proceed. If none of the + -- replies matched, we consider the connection insecure. + + if (dane_answer.bogus or dane_answer.secure) and not dane then + -- The first answer we care about + -- For services with only one SRV record, this will be the only one + dane = dane_answer; + elseif dane_answer.bogus then dane.bogus = dane_answer.bogus; elseif dane_answer.secure then for _, dane_record in ipairs(dane_answer) do @@ -116,15 +136,18 @@ end end if n == 0 then - if #dane > 0 and dane.bogus then - -- Got at least one non-bogus reply, - -- This should trigger a failure if one of them did not match - host_session.log("warn", "Ignoring bogus replies"); - dane.bogus = nil; - end - if #dane == 0 and dane.bogus == nil then - -- Got no usable data - host_session.dane = false; + if dane then + host_session.dane = dane; + if #dane > 0 and dane.bogus then + -- Got at least one non-bogus reply, + -- This should trigger a failure if one of them did not match + host_session.log("warn", "Ignoring bogus replies"); + dane.bogus = nil; + end + if #dane == 0 and dane.bogus == nil then + -- Got no usable data + host_session.dane = false; + end end return cb(host_session); end diff -r 2440a75e868f -r d85d5b0bf977 mod_smacks/mod_smacks.lua --- a/mod_smacks/mod_smacks.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_smacks/mod_smacks.lua Thu May 07 23:39:54 2015 +0200 @@ -124,8 +124,8 @@ local function wrap_session_in(session, resume) if not resume then session.handled_stanza_count = 0; - add_filter(session, "stanzas/in", count_incoming_stanzas, 1000); end + add_filter(session, "stanzas/in", count_incoming_stanzas, 1000); return session; end @@ -328,8 +328,8 @@ original_session.conn = session.conn; original_session.send = session.send; original_session.filter = session.filter; - original_session.send.filter = session.filter; - original_session.data.filter = session.filter; + original_session.filter.session = original_session; + original_session.filters = session.filters; original_session.stream = session.stream; original_session.secure = session.secure; original_session.hibernating = nil; diff -r 2440a75e868f -r d85d5b0bf977 mod_smacks_offline/mod_smacks_offline.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_smacks_offline/mod_smacks_offline.lua Thu May 07 23:39:54 2015 +0200 @@ -0,0 +1,33 @@ +local t_insert = table.insert; + +local mod_smacks = module:depends"smacks" + +local function store_unacked_stanzas(session) + local queue = session.outgoing_stanza_queue; + local replacement_queue = {}; + session.outgoing_stanza_queue = replacement_queue; + + for _, stanza in ipairs(queue) do + if stanza.name == "message" and stanza.attr.xmlns == nil and + ( stanza.attr.type == "chat" or ( stanza.attr.type or "normal" ) == "normal" ) then + module:fire_event("message/offline/handle", { origin = session, stanza = stanza } ) + else + t_insert(replacement_queue, stanza); + end + end +end + +local handle_unacked_stanzas = mod_smacks.handle_unacked_stanzas; + +local host_sessions = prosody.hosts[module.host].sessions; +mod_smacks.handle_unacked_stanzas = function (session) + local sessions = host_sessions[session.username].sessions; + if next(sessions) == session.resource and next(sessions, session.resource) == nil then + store_unacked_stanzas(session) + end + return handle_unacked_stanzas(session); +end + +function module.unload() + mod_smacks.handle_unacked_stanzas = handle_unacked_stanzas; +end diff -r 2440a75e868f -r d85d5b0bf977 mod_statistics/prosodytop.lua --- a/mod_statistics/prosodytop.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_statistics/prosodytop.lua Thu May 07 23:39:54 2015 +0200 @@ -104,7 +104,7 @@ local conn = require "socket".tcp(); assert(conn:connect("localhost", 5782)); - handler = server.wrapclient(conn, "localhost", 5279, stats_listener, "*a"); + handler = server.wrapclient(conn, "localhost", 5782, stats_listener, "*a"); end return { diff -r 2440a75e868f -r d85d5b0bf977 mod_storage_xmlarchive/mod_storage_xmlarchive.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_storage_xmlarchive/mod_storage_xmlarchive.lua Thu May 07 23:39:54 2015 +0200 @@ -0,0 +1,199 @@ +local dm = require "core.storagemanager".olddm; +local hmac_sha256 = require"util.hashes".hmac_sha256; +local st = require"util.stanza"; +local dt = require"util.datetime"; +local new_stream = require "util.xmppstream".new; +local empty = {}; + +local function fallocate(f, offset, len) + -- This assumes that current position == offset + local fake_data = (" "):rep(len); + local ok, msg = f:write(fake_data); + if not ok then + return ok, msg; + end + f:seek("set", offset); + return true; +end; +pcall(function() + local pposix = require "util.pposix"; + fallocate = pposix.fallocate or fallocate; +end); + +local archive = {}; +local archive_mt = { __index = archive }; + +function archive:append(username, _, when, with, data) + if getmetatable(data) ~= st.stanza_mt then + return nil, "unsupported-datatype"; + end + username = username or "@"; + data = tostring(data) .. "\n"; + local day = dt.date(when); + local filename = dm.getpath(username.."@"..day, module.host, self.store, "xml", true); + local ok, err; + local f = io.open(filename, "r+"); + if not f then + f, err = io.open(filename, "w"); + if not f then return nil, err; end + ok, err = dm.list_append(username, module.host, self.store, day); + if not ok then return nil, err; end + end + local offset = f and f:seek("end"); + ok, err = fallocate(f, offset, #data); + if not ok then return nil, err; end + f:seek("set", offset); + ok, err = f:write(data); + if not ok then return nil, err; end + ok, err = f:close(); + if not ok then return nil, err; end + local id = day .. "-" .. hmac_sha256(username.."@"..day.."+"..offset, data, true):sub(-16); + ok, err = dm.list_append(username.."@"..day, module.host, self.store, { id = id, when = when, with = with, offset = offset, length = #data }); + if not ok then return nil, err; end + return id; +end + +function archive:find(username, query) + username = username or "@"; + query = query or empty; + + local result; + local function cb(_, stanza) + if result then + module:log("warn", "Multiple items in chunk"); + end + result = stanza; + end + + local stream_sess = { notopen = true }; + local stream = new_stream(stream_sess, { handlestanza = cb, stream_ns = "jabber:client"}); + local dates = dm.list_load(username, module.host, self.store) or empty; + stream:feed(st.stanza("stream", { xmlns = "jabber:client" }):top_tag()); + stream_sess.notopen = nil; + + local limit = query.limit; + local start_day, step, last_day = 1, 1, #dates; + local count = 0; + local rev = query.reverse; + local in_range = not (query.after or query.before); + if query.after or query.start then + local d = query.after and query.after:sub(1, 10) or dt.date(query.start); + for i = 1, #dates do + if dates[i] == d then + start_day = i; break; + end + end + end + if query.before or query["end"] then + local d = query.before and query.before:sub(1, 10) or dt.date(query["end"]); + for i = #dates, 1, -1 do + if dates[i] == d then + last_day = i; break; + end + end + end + if rev then + start_day, step, last_day = last_day, -step, start_day; + end + local items, xmlfile; + local first_item, last_item; + + return function () + if limit and count >= limit then xmlfile:close() return; end + + for d = start_day, last_day, step do + if d ~= start_day or not items then + module:log("debug", "Load items for %s", dates[d]); + start_day = d; + items = dm.list_load(username .. "@" .. dates[d], module.host, self.store) or empty; + if not rev then + first_item, last_item = 1, #items; + else + first_item, last_item = #items, 1; + end + local ferr; + xmlfile, ferr = io.open(dm.getpath(username .. "@" .. dates[d], module.host, self.store, "xml")); + if not xmlfile then + module:log("error", "Error: %s", ferr); + return; + end + end + + for i = first_item, last_item, step do + module:log("debug", "data[%q][%d]", dates[d], i); + local item = items[i]; + if not item then + module:log("debug", "data[%q][%d] is nil", dates[d], i); + break; + end + if xmlfile and in_range + and (not query.with or item.with == query.with) + and (not query.start or item.when >= query.start) + and (not query["end"] or item.when <= query["end"]) then + count = count + 1; + first_item = i + step; + + xmlfile:seek("set", item.offset); + local data = xmlfile:read(item.length); + local ok, err = stream:feed(data); + if not ok then + module:log("warn", "Parse error: %s", err); + end + if result then + local stanza = result; + result = nil; + return item.id, stanza, item.when, item.with; + end + end + if (rev and item.id == query.after) or + (not rev and item.id == query.before) then + in_range = false; + limit = count; + end + if (rev and item.id == query.before) or + (not rev and item.id == query.after) then + in_range = true; + end + end + end + if xmlfile then + xmlfile:close(); + xmlfile = nil; + end + end +end + +function archive:delete(username, query) + username = username or "@"; + query = query or empty; + if query.with or query.start or query.after then + return nil, "not-implemented"; -- Only trimming the oldest messages + end + local before = query.before or query["end"] or "9999-12-31"; + if type(before) == "number" then before = dt.date(before); else before = before:sub(1, 10); end + local dates = dm.list_load(username, module.host, self.store) or empty; + local remaining_dates = {}; + for d = 1, #dates do + if dates[d] >= before then + table.insert(remaining_dates, dates[d]); + end + end + table.sort(remaining_dates); + local ok, err = dm.list_store(username, module.host, self.store, remaining_dates); + if not ok then return ok, err; end + for d = 1, #dates do + if dates[d] < before then + os.remove(dm.getpath(username .. "@" .. dates[d], module.host, self.store, "list")); + os.remove(dm.getpath(username .. "@" .. dates[d], module.host, self.store, "xml")); + end + end + return true; +end + +local provider = {}; +function provider:open(store, typ) + if typ ~= "archive" then return nil, "unsupported-store"; end + return setmetatable({ store = store }, archive_mt); +end + +module:provides("storage", provider); diff -r 2440a75e868f -r d85d5b0bf977 mod_watchuntrusted/mod_watchuntrusted.lua --- a/mod_watchuntrusted/mod_watchuntrusted.lua Thu May 07 22:40:58 2015 +0200 +++ b/mod_watchuntrusted/mod_watchuntrusted.lua Thu May 07 23:39:54 2015 +0200 @@ -9,8 +9,11 @@ local st = require "util.stanza"; +local notified_about_already = { }; + module:hook_global("s2s-check-certificate", function (event) local session, host = event.session, event.host; + if not host then return end local conn = session.conn:socket(); local local_host = session.direction == "outgoing" and session.from_host or session.to_host; @@ -25,7 +28,8 @@ must_secure = false; end - if must_secure and (session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid") then + if must_secure and (session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid") and not notified_about_already[host] then + notified_about_already[host] = os.time(); local _, errors = conn:getpeerverification(); local error_message = ""; @@ -54,3 +58,10 @@ end end, -0.5); +module:add_timer(14400, function (now) + for host, time in pairs(notified_about_already) do + if time + 86400 > now then + notified_about_already[host] = nil; + end + end +end)