--[[
-Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
+Copyright (c) 2023, Vsevolod Stakhov <vsevolod@rspamd.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
local split_grammar = {}
local spaces_split_grammar
-local space = lpeg.S' \t\n\v\f\r'
+local space = lpeg.S ' \t\n\v\f\r'
local nospace = 1 - space
-local ptrim = space^0 * lpeg.C((space^0 * nospace^1)^0)
+local ptrim = space ^ 0 * lpeg.C((space ^ 0 * nospace ^ 1) ^ 0)
local match = lpeg.match
lupa.configure('{%', '%}', '{=', '=}', '{#', '#}', {
if not sep then
if not spaces_split_grammar then
local _sep = space
- local elem = lpeg.C((1 - _sep)^0)
- local p = lpeg.Ct(elem * (_sep * elem)^0)
+ local elem = lpeg.C((1 - _sep) ^ 0)
+ local p = lpeg.Ct(elem * (_sep * elem) ^ 0)
spaces_split_grammar = p
end
else
_sep = sep -- Assume lpeg object
end
- local elem = lpeg.C((1 - _sep)^0)
- local p = lpeg.Ct(elem * (_sep * elem)^0)
+ local elem = lpeg.C((1 - _sep) ^ 0)
+ local p = lpeg.Ct(elem * (_sep * elem) ^ 0)
gr = p
split_grammar[sep] = gr
end
-- modified version from Robert Jay Gould http://lua-users.org/wiki/SimpleRound
exports.round = function(num, numDecimalPlaces)
- local mult = 10^(numDecimalPlaces or 0)
+ local mult = 10 ^ (numDecimalPlaces or 0)
if num >= 0 then
return math.floor(num * mult + 0.5) / mult
else
exports.template = function(tmpl, keys)
local var_lit = lpeg.P { lpeg.R("az") + lpeg.R("AZ") + lpeg.R("09") + "_" }
- local var = lpeg.P { (lpeg.P("$") / "") * ((var_lit^1) / keys) }
- local var_braced = lpeg.P { (lpeg.P("${") / "") * ((var_lit^1) / keys) * (lpeg.P("}") / "") }
+ local var = lpeg.P { (lpeg.P("$") / "") * ((var_lit ^ 1) / keys) }
+ local var_braced = lpeg.P { (lpeg.P("${") / "") * ((var_lit ^ 1) / keys) * (lpeg.P("}") / "") }
- local template_grammar = lpeg.Cs((var + var_braced + 1)^0)
+ local template_grammar = lpeg.Cs((var + var_braced + 1) ^ 0)
return lpeg.match(template_grammar, tmpl)
end
if cap then
return cap, rspamd_str_split(pluses, '+'), nil
elseif no_dots_user ~= addr.user then
- return no_dots_user,{},nil
+ return no_dots_user, {}, nil
end
return nil
local ua = task:get_request_header('User-Agent') or ''
local pwd = task:get_request_header('Password')
local is_rspamc = false
- if tostring(ua) == 'rspamc' or pwd then is_rspamc = true end
+ if tostring(ua) == 'rspamc' or pwd then
+ is_rspamc = true
+ end
return is_rspamc
end
--]]
exports.flatten = function(t)
local res = {}
- for _,e in fun.iter(t) do
- for _,v in fun.iter(e) do
+ for _, e in fun.iter(t) do
+ for _, v in fun.iter(e) do
res[#res + 1] = v
end
end
local function spairs(t, order, lim)
-- collect the keys
local keys = {}
- for k in pairs(t) do keys[#keys+1] = k end
+ for k in pairs(t) do
+ keys[#keys + 1] = k
+ end
-- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys
if order then
- table.sort(keys, function(a,b) return order(t, a, b) end)
+ table.sort(keys, function(a, b)
+ return order(t, a, b)
+ end)
else
table.sort(keys)
end
exports.spairs = spairs
--[[[
--- @function lua_util.disable_module(modname, how)
+-- @function lua_util.disable_module(modname, how[, reason])
-- Disables a plugin
-- @param {string} modname name of plugin to disable
-- @param {string} how 'redis' to disable redis, 'config' to disable startup
+-- @param {string} reason optional reason for failure
--]]
-
-local function disable_module(modname, how)
+local function disable_module(modname, how, reason)
if rspamd_plugins_state.enabled[modname] then
rspamd_plugins_state.enabled[modname] = nil
end
rspamd_plugins_state.disabled_unconfigured[modname] = {}
elseif how == 'experimental' then
rspamd_plugins_state.disabled_experimental[modname] = {}
+ elseif how == 'failed' then
+ rspamd_plugins_state.disabled_failed[modname] = { reason = reason }
else
- rspamd_plugins_state.disabled_failed[modname] = {}
+ rspamd_plugins_state.disabled_unknown[modname] = {}
end
end
local function nkeys(gen, param, state)
local n = 0
if not param then
- for _,_ in pairs(gen) do n = n + 1 end
+ for _, _ in pairs(gen) do
+ n = n + 1
+ end
else
- for _,_ in fun.iter(gen, param, state) do n = n + 1 end
+ for _, _ in fun.iter(gen, param, state) do
+ n = n + 1
+ end
end
return n
end
local digit = lpeg.R("09")
local parser = {}
- parser.integer =
- (lpeg.S("+-") ^ -1) *
- (digit ^ 1)
- parser.fractional =
- (lpeg.P(".") ) *
+ parser.integer = (lpeg.S("+-") ^ -1) *
(digit ^ 1)
- parser.number =
- (parser.integer *
+ parser.fractional = (lpeg.P(".")) *
+ (digit ^ 1)
+ parser.number = (parser.integer *
(parser.fractional ^ -1)) +
(lpeg.S("+-") * parser.fractional)
parser.time = lpeg.Cf(lpeg.Cc(1) *
(parser.number / tonumber) *
((lpeg.S("smhdwy") / parse_time_suffix) ^ -1),
- function (acc, val) return acc * val end)
+ function(acc, val)
+ return acc * val
+ end)
local t = lpeg.match(parser.time, str)
local digit = lpeg.R("09")
local parser = {}
- parser.integer =
- (lpeg.S("+-") ^ -1) *
- (digit ^ 1)
- parser.fractional =
- (lpeg.P(".") ) *
+ parser.integer = (lpeg.S("+-") ^ -1) *
(digit ^ 1)
- parser.number =
- (parser.integer *
+ parser.fractional = (lpeg.P(".")) *
+ (digit ^ 1)
+ parser.number = (parser.integer *
(parser.fractional ^ -1)) +
(lpeg.S("+-") * parser.fractional)
parser.humanized_number = lpeg.Cf(lpeg.Cc(1) *
(parser.number / tonumber) *
(((lpeg.S("kmg") * (lpeg.P("b") ^ -1)) / parse_suffix) ^ -1),
- function (acc, val) return acc * val end)
+ function(acc, val)
+ return acc * val
+ end)
local t = lpeg.match(parser.humanized_number, str)
local function table_cmp(table1, table2)
local avoid_loops = {}
local function recurse(t1, t2)
- if type(t1) ~= type(t2) then return false end
- if type(t1) ~= "table" then return t1 == t2 end
+ if type(t1) ~= type(t2) then
+ return false
+ end
+ if type(t1) ~= "table" then
+ return t1 == t2
+ end
- if avoid_loops[t1] then return avoid_loops[t1] == t2 end
+ if avoid_loops[t1] then
+ return avoid_loops[t1] == t2
+ end
avoid_loops[t1] = t2
-- Copy keys from t2
local t2keys = {}
local t2tablekeys = {}
for k, _ in pairs(t2) do
- if type(k) == "table" then table.insert(t2tablekeys, k) end
+ if type(k) == "table" then
+ table.insert(t2tablekeys, k)
+ end
t2keys[k] = true
end
-- Let's iterate keys from t1
break
end
end
- if not ok then return false end
+ if not ok then
+ return false
+ end
else
-- t1 has a key which t2 doesn't have, fail.
- if v2 == nil then return false end
+ if v2 == nil then
+ return false
+ end
t2keys[k1] = nil
- if not recurse(v1, v2) then return false end
+ if not recurse(v1, v2) then
+ return false
+ end
end
end
-- if t2 has a key which t1 doesn't have, fail.
- if next(t2keys) then return false end
+ if next(t2keys) then
+ return false
+ end
return true
end
return recurse(table1, table2)
local res = {}
- for k,v in pairs(override) do
+ for k, v in pairs(override) do
if type(v) == 'table' then
if def[k] and type(def[k]) == 'table' then
-- Recursively override elements
end
end
- for k,v in pairs(def) do
+ for k, v in pairs(def) do
if type(res[k]) == 'nil' then
res[k] = v
end
-- tries its best to extract specific number of urls from a task based on
-- their characteristics
--]]
-exports.filter_specific_urls = function (urls, params)
+exports.filter_specific_urls = function(urls, params)
local cache_key
if params.task and not params.no_cache then
end
end
- if not urls then return {} end
+ if not urls then
+ return {}
+ end
- if params.filter then urls = fun.totable(fun.filter(params.filter, urls)) end
+ if params.filter then
+ urls = fun.totable(fun.filter(params.filter, urls))
+ end
-- Filter by tld:
local tlds = {}
end
if not eslds[esld] then
- eslds[esld] = {{str_hash, u, priority}}
+ eslds[esld] = { { str_hash, u, priority } }
neslds = neslds + 1
else
if #eslds[esld] < params.esld_limit then
- table.insert(eslds[esld], {str_hash, u, priority})
+ table.insert(eslds[esld], { str_hash, u, priority })
end
end
local tld = table.concat(fun.totable(fun.tail(parts)), '.')
if not tlds[tld] then
- tlds[tld] = {{str_hash, u, priority}}
+ tlds[tld] = { { str_hash, u, priority } }
ntlds = ntlds + 1
else
- table.insert(tlds[tld], {str_hash, u, priority})
+ table.insert(tlds[tld], { str_hash, u, priority })
end
end
end
- for _,u in ipairs(urls) do
+ for _, u in ipairs(urls) do
process_single_url(u)
end
local limit = params.limit
limit = limit - nres
- if limit < 0 then limit = 0 end
+ if limit < 0 then
+ limit = 0
+ end
if limit == 0 then
res = exports.values(res)
repeat
local item_found = false
- for _,lurls in ipairs(eslds) do
+ for _, lurls in ipairs(eslds) do
if #lurls > 0 then
local last = table.remove(lurls)
insert_url(last[1], last[2])
-- Number of tlds < limit
while limit > 0 do
- for _,lurls in ipairs(tlds) do
+ for _, lurls in ipairs(tlds) do
if #lurls > 0 then
local last = table.remove(lurls)
insert_url(last[1], last[2])
limit = limit - 1
end
- if limit == 0 then break end
+ if limit == 0 then
+ break
+ end
end
end
prefix = prefix
}
end
- for k,v in pairs(default_params) do
- if type(params[k]) == 'nil' and v ~= nil then params[k] = v end
+ for k, v in pairs(default_params) do
+ if type(params[k]) == 'nil' and v ~= nil then
+ params[k] = v
+ end
end
local url_params = {
emails = params.need_emails,
cache_key_suffix = table.concat(params.flags) .. (params.flags_mode or '')
else
cache_key_suffix = string.format('%s%s%s',
- tostring(params.need_emails or false),
- tostring(params.need_images or false),
- tostring(params.need_content or false))
+ tostring(params.need_emails or false),
+ tostring(params.need_images or false),
+ tostring(params.need_content or false))
end
cache_key = string.format('sp_urls_%d%s', params.limit, cache_key_suffix)
end
if getmetatable(orig) then
setmetatable(copy, deepcopy(getmetatable(orig)))
end
- else -- number, string, boolean, etc
+ else
+ -- number, string, boolean, etc
copy = orig
end
return copy
end
end
if log_config.debug_modules then
- for _,m in ipairs(log_config.debug_modules) do
+ for _, m in ipairs(log_config.debug_modules) do
debug_modules[m] = true
logger.infox(config, 'enable debug for Lua module %s', m)
end
end
if #debug_aliases > 0 then
- for alias,mod in pairs(debug_aliases) do
+ for alias, mod in pairs(debug_aliases) do
if debug_modules[mod] then
debug_modules[alias] = true
logger.infox(config, 'enable debug for Lua module %s (%s aliased)',
end
exports.enable_debug_modules = function(...)
- for _,m in ipairs({...}) do
+ for _, m in ipairs({ ... }) do
debug_modules[m] = true
end
end
local loadstring = loadstring or load
if not s or #s == 0 then
- return false,'invalid or empty string'
+ return false, 'invalid or empty string'
end
s = exports.rspamd_str_trim(s)
local ret, res_or_err = pcall(loadstring(inp))
if not ret or type(res_or_err) ~= 'function' then
- return false,res_or_err
+ return false, res_or_err
end
- return ret,res_or_err
+ return ret, res_or_err
end
---[[[
local i = 1
if param then
- for k,_ in fun.iter(gen, param, state) do
+ for k, _ in fun.iter(gen, param, state) do
rawset(keys, i, k)
i = i + 1
end
else
- for k,_ in pairs(gen) do
+ for k, _ in pairs(gen) do
rawset(keys, i, k)
i = i + 1
end
local i = 1
if param then
- for _,v in fun.iter(gen, param, state) do
+ for _, v in fun.iter(gen, param, state) do
rawset(values, i, v)
i = i + 1
end
else
- for _,v in pairs(gen) do
+ for _, v in pairs(gen) do
rawset(values, i, v)
i = i + 1
end
exports.distance_sorted = function(t1, t2)
local ncomp = #t1
local ndiff = 0
- local i,j = 1,1
+ local i, j = 1, 1
if ncomp < #t2 then
ncomp = #t2
end
- for _=1,ncomp do
+ for _ = 1, ncomp do
if j > #t2 then
ndiff = ndiff + ncomp - #t2
if i > j then
local h = cr.create()
if t[1] then
- for _,e in ipairs(t) do
+ for _, e in ipairs(t) do
if type(e) == 'table' then
h:update(table_digest(e))
else
end
end
else
- for k,v in pairs(t) do
+ for k, v in pairs(t) do
h:update(tostring(k))
if type(v) == 'string' then
end
end
end
- return h:base32()
+ return h:base32()
end
exports.table_digest = table_digest
elseif false_t[v] == false then
return false;
else
- return false, string.format( 'cannot convert %q to boolean', v);
+ return false, string.format('cannot convert %q to boolean', v);
end
elseif type(v) == 'number' then
return v ~= 0
else
- return false, string.format( 'cannot convert %q to boolean', v);
+ return false, string.format('cannot convert %q to boolean', v);
end
end
try_section('options')
end
- return {check_local, check_authed}
+ return { check_local, check_authed }
end
---[[[
ip = task:get_from_ip()
end
if not conf then
- conf = {false, false}
+ conf = { false, false }
end
if ((not conf[2] and task:get_user()) or
(not conf[1] and type(ip) == 'userdata' and ip:is_local())) then
-- @param {string} str input string
-- @return {string} original or quoted string
--]]]
-local tspecial = lpeg.S"()<>,;:\\\"/[]?= \t\v"
-local special_match = lpeg.P((1 - tspecial)^0 * tspecial^1)
+local tspecial = lpeg.S "()<>,;:\\\"/[]?= \t\v"
+local special_match = lpeg.P((1 - tspecial) ^ 0 * tspecial ^ 1)
exports.maybe_smtp_quote_value = function(str)
if special_match:match(str) then
return string.format('"%s"', str:gsub('"', '\\"'))
-- @param {string} str string to decode
-- @return {string} hex decoded string (valid hex pairs are decoded, everything else is printed as is)
--]]]
-exports.unhex = function(str) return str:gsub('(..)', hex_table) end
+exports.unhex = function(str)
+ return str:gsub('(..)', hex_table)
+end
local http_upstream_lists = {}
local function http_upstreams_by_url(pool, url)
local rspamd_url = require "rspamd_url"
local cached = http_upstream_lists[url]
- if cached then return cached end
+ if cached then
+ return cached
+ end
local real_url = rspamd_url.create(pool, url)
- if not real_url then return nil end
+ if not real_url then
+ return nil
+ end
local host = real_url:get_host()
local proto = real_url:get_protocol() or 'http'
-- @return A single path string, with components separated by the appropriate separator.
--
---]]]
-local path_sep = package.config:sub(1,1) or '/'
+local path_sep = package.config:sub(1, 1) or '/'
local function join_path(...)
- local components = {...}
+ local components = { ... }
-- Join components using separator
return table.concat(components, path_sep)
local function add_symbol_score(task, rule, mult, params)
if not params then
- params = {tostring(mult)}
+ params = { tostring(mult) }
end
if rule.selector.config.split_symbols then
-- Extracts task score and subtracts score of the rule itself
local function extract_task_score(task, rule)
local lua_verdict = require "lua_verdict"
- local verdict,score = lua_verdict.get_specific_verdict(N, task)
+ local verdict, score = lua_verdict.get_specific_verdict(N, task)
- if not score or verdict == 'passthrough' then return nil end
+ if not score or verdict == 'passthrough' then
+ return nil
+ end
return sub_symbol_score(task, rule, score)
end
if not gr then
local semicolon = lpeg.P(':')
- local domain = lpeg.C((1 - semicolon)^1)
- local res = lpeg.S'+-?~'
+ local domain = lpeg.C((1 - semicolon) ^ 1)
+ local res = lpeg.S '+-?~'
local function res_to_label(ch)
- if ch == '+' then return 'a'
- elseif ch == '-' then return 'r'
+ if ch == '+' then
+ return 'a'
+ elseif ch == '-' then
+ return 'r'
end
return 'u'
end
- gr = domain * semicolon * (lpeg.C(res^1) / res_to_label)
+ gr = domain * semicolon * (lpeg.C(res ^ 1) / res_to_label)
end
if dkim_trace and dkim_trace.options then
- for _,opt in ipairs(dkim_trace.options) do
- local dom,res = lpeg.match(gr, opt)
+ for _, opt in ipairs(dkim_trace.options) do
+ local dom, res = lpeg.match(gr, opt)
if dom and res then
local tld = rspamd_util.get_tld(dom)
end
if requests_left == 0 then
- for k,v in pairs(results) do
+ for k, v in pairs(results) do
-- `k` in results is a prefixed and suffixed tld, so we need to look through
-- all requests to find any request with the matching tld
local sel_tld
- for _,tld in ipairs(dkim_tlds) do
+ for _, tld in ipairs(dkim_tlds) do
if k:find(tld, 1, true) then
sel_tld = tld
break
rep_accepted_abs)
if rep_accepted_abs then
local final_rep = rep_accepted
- if rep_accepted > 1.0 then final_rep = 1.0 end
- if rep_accepted < -1.0 then final_rep = -1.0 end
+ if rep_accepted > 1.0 then
+ final_rep = 1.0
+ end
+ if rep_accepted < -1.0 then
+ final_rep = -1.0
+ end
add_symbol_score(task, rule, final_rep)
-- Store results for future DKIM results adjustments
end
end
- for dom,res in pairs(requests) do
+ for dom, res in pairs(requests) do
-- tld + "." + check_result, e.g. example.com.+ - reputation for valid sigs
local query = string.format('%s.%s', dom, res)
rule.backend.get_token(task, rule, nil, query, tokens_cb, 'string')
local sc = extract_task_score(task, rule)
if sc then
- for dom,res in pairs(requests) do
+ for dom, res in pairs(requests) do
-- tld + "." + check_result, e.g. example.com.+ - reputation for valid sigs
local query = string.format('%s.%s', dom, res)
rule.backend.set_token(task, rule, nil, query, sc)
inbound = true,
max_accept_adjustment = 2.0, -- How to adjust accepted DKIM score
},
- dependencies = {"DKIM_TRACE"},
+ dependencies = { "DKIM_TRACE" },
filter = dkim_reputation_filter, -- used to get scores
postfilter = dkim_reputation_postfilter, -- used to adjust DKIM scores
idempotent = dkim_reputation_idempotent, -- used to set scores
else
domains[dom] = domains[dom] + 1
end
- end, fun.filter(function(u) return not u:is_html_displayed() end,
- task:get_urls(true)))
+ end, fun.filter(function(u)
+ return not u:is_html_displayed()
+ end,
+ task:get_urls(true)))
local results = {}
- for k,v in lua_util.spairs(domains,
- function(t, a, b) return t[a] > t[b] end, rule.selector.config.max_urls) do
+ for k, v in lua_util.spairs(domains,
+ function(t, a, b)
+ return t[a] > t[b]
+ end, rule.selector.config.max_urls) do
if v > 0 then
- table.insert(results, {k,v})
+ table.insert(results, { k, v })
end
end
-- Check the url with maximum hits
local mhits = 0
- for i,res in ipairs(results) do
+ for i, res in ipairs(results) do
local req = requests[i]
if req then
local hits = tonumber(res[1])
if mhits > 0 then
local score = 0
- for i,res in pairs(results) do
+ for i, res in pairs(results) do
local req = requests[i]
if req then
local url_score = generic_reputation_calc(res, rule,
end
end
- for i,req in ipairs(requests) do
+ for i, req in ipairs(requests) do
local function tokens_cb(err, token, values)
indexed_tokens_cb(err, i, values)
end
local sc = extract_task_score(task, rule)
if sc then
- for _,tld in ipairs(requests) do
+ for _, tld in ipairs(requests) do
rule.backend.set_token(task, rule, nil, tld[1], sc)
end
end
if cfg.asn_cc_whitelist then
cfg.asn_cc_whitelist = lua_maps.map_add('reputation',
- 'asn_cc_whitelist',
- 'map',
- 'IP score whitelisted ASNs/countries')
+ 'asn_cc_whitelist',
+ 'map',
+ 'IP score whitelisted ASNs/countries')
end
return true
local ip = task:get_from_ip()
- if not ip or not ip:is_valid() then return end
- if lua_util.is_rspamc_or_controller(task) then return end
+ if not ip or not ip:is_valid() then
+ return
+ end
+ if lua_util.is_rspamc_or_controller(task) then
+ return
+ end
local cfg = rule.selector.config
local asn_score = generic_reputation_calc(asn_stats, rule, cfg.scores.asn, task)
score = score + asn_score
table.insert(description_t, string.format('asn: %s(%.2f)',
- asn, asn_score))
+ asn, asn_score))
end
if country_stats then
local country_score = generic_reputation_calc(country_stats, rule,
cfg.scores.country, task)
score = score + country_score
table.insert(description_t, string.format('country: %s(%.2f)',
- country, country_score))
+ country, country_score))
end
if ip_stats then
local ip_score = generic_reputation_calc(ip_stats, rule, cfg.scores.ip,
- task)
+ task)
score = score + ip_score
table.insert(description_t, string.format('ip: %s(%.2f)',
- tostring(ip), ip_score))
+ tostring(ip), ip_score))
end
if math.abs(score) > 0.001 then
if asn then
rule.backend.get_token(task, rule, cfg.asn_prefix, asn,
- gen_token_callback('asn'), 'string')
+ gen_token_callback('asn'), 'string')
end
if country then
rule.backend.get_token(task, rule, cfg.country_prefix, country,
- gen_token_callback('country'), 'string')
+ gen_token_callback('country'), 'string')
end
rule.backend.get_token(task, rule, cfg.ip_prefix, ip,
- gen_token_callback('ip'), 'ip')
+ gen_token_callback('ip'), 'ip')
end
-- Used to set scores
local function ip_reputation_idempotent(task, rule)
- if not rule.backend.set_token then return end -- Read only backend
+ if not rule.backend.set_token then
+ return
+ end -- Read only backend
local ip = task:get_from_ip()
local cfg = rule.selector.config
- if not ip or not ip:is_valid() then return end
+ if not ip or not ip:is_valid() then
+ return
+ end
- if lua_util.is_rspamc_or_controller(task) then return end
+ if lua_util.is_rspamc_or_controller(task) then
+ return
+ end
if ip:get_version() == 4 and cfg.ipv4_mask then
ip = ip:apply_mask(cfg.ipv4_mask)
local spf_allow = task:has_symbol('R_SPF_ALLOW')
-- Don't care about bad/missing spf
- if not spf_record or not spf_allow then return end
+ if not spf_record or not spf_allow then
+ return
+ end
local cr = require "rspamd_cryptobox_hash"
local hkey = cr.create(spf_record):base32():sub(1, 32)
local spf_record = task:get_mempool():get_variable('spf_record')
local spf_allow = task:has_symbol('R_SPF_ALLOW')
- if not spf_record or not spf_allow or not sc then return end
+ if not spf_record or not spf_allow or not sc then
+ return
+ end
local cr = require "rspamd_cryptobox_hash"
local hkey = cr.create(spf_record):base32():sub(1, 32)
rule.backend.set_token(task, rule, nil, hkey, sc)
end
-
local spf_selector = {
config = {
symbol = 'SPF_REP', -- symbol to be inserted
outbound = true,
inbound = true,
},
- dependencies = {"R_SPF_ALLOW"},
+ dependencies = { "R_SPF_ALLOW" },
filter = spf_reputation_filter, -- used to get scores
idempotent = spf_reputation_idempotent, -- used to set scores
}
if type(selector_res) == 'table' then
fun.each(function(e)
lua_util.debugm(N, task, 'check generic reputation (%s) %s',
- rule['symbol'], e)
+ rule['symbol'], e)
rule.backend.get_token(task, rule, nil, e, tokens_cb, 'string')
end, selector_res)
else
lua_util.debugm(N, task, 'check generic reputation (%s) %s',
- rule['symbol'], selector_res)
+ rule['symbol'], selector_res)
rule.backend.get_token(task, rule, nil, selector_res, tokens_cb, 'string')
end
end
local cfg = rule.selector.config
local selector_res = cfg.selector(task)
- if not selector_res then return end
+ if not selector_res then
+ return
+ end
if sc then
if type(selector_res) == 'table' then
end
end
-
local generic_selector = {
- schema = ts.shape{
+ schema = ts.shape {
lower_bound = ts.number + ts.string / tonumber,
max_score = ts.number:is_optional(),
min_score = ts.number:is_optional(),
idempotent = generic_reputation_idempotent -- used to set scores
}
-
-
local selectors = {
ip = ip_selector,
sender = ip_selector, -- Better name
local function reputation_dns_init(rule, _, _, _)
if not rule.backend.config.list then
rspamd_logger.errx(rspamd_config, "rule %s with DNS backend has no `list` parameter defined",
- rule.symbol)
+ rule.symbol)
return false
end
return true
end
-
local function gen_token_key(prefix, token, rule)
if prefix then
token = prefix .. token
if prefix then
dns_name = string.format('%s.%s.%s', key, prefix,
- rule.backend.config.list)
+ rule.backend.config.list)
else
dns_name = string.format('%s.%s', key, rule.backend.config.list)
end
dns_name, results, err, rule.backend.config.list)
-- Now split tokens to list of values
- if results and results[1] then
+ if results and results[1] then
-- Format: num_messages;sc1;sc2...scn
local dns_tokens = lua_util.rspamd_str_split(results[1], ";")
-- Convert all to numbers excluding any possible non-numbers
dns_tokens = fun.totable(fun.filter(function(e)
return type(e) == 'number'
end,
- fun.map(function(e)
- local n = tonumber(e)
- if n then return n end
- return "BAD"
- end, dns_tokens)))
+ fun.map(function(e)
+ local n = tonumber(e)
+ if n then
+ return n
+ end
+ return "BAD"
+ end, dns_tokens)))
if #dns_tokens < 2 then
rspamd_logger.warnx(task, 'cannot parse response for reputation token %s: %s',
- dns_name, results[1])
+ dns_name, results[1])
continuation_cb(results, dns_name, nil)
else
local cnt = table.remove(dns_tokens, 1)
end
else
rspamd_logger.messagex(task, 'invalid response for reputation token %s: %s',
- dns_name, results[1])
+ dns_name, results[1])
continuation_cb(results, dns_name, nil)
end
end
- task:get_resolver():resolve_a({
+ task:get_resolver():resolve_a({
task = task,
name = dns_name,
callback = dns_cb,
]]
local get_script = lua_util.jinja_template(redis_get_script_tpl,
- {windows = rule.backend.config.buckets})
+ { windows = rule.backend.config.buckets })
rspamd_logger.debugm(N, rspamd_config, 'added extraction script %s', get_script)
rule.backend.script_get = lua_redis.add_redis_script(get_script, our_redis_params)
]]
local set_script = lua_util.jinja_template(redis_adaptive_emea_script_tpl,
- {windows = rule.backend.config.buckets})
+ { windows = rule.backend.config.buckets })
rspamd_logger.debugm(N, rspamd_config, 'added emea update script %s', set_script)
rule.backend.script_set = lua_redis.add_redis_script(set_script, our_redis_params)
continuation_cb(nil, key, data)
else
rspamd_logger.errx(task, 'rule %s - invalid type while getting reputation keys %s: %s',
- rule['symbol'], key, type(data))
+ rule['symbol'], key, type(data))
continuation_cb("invalid type", key, nil)
end
elseif err then
rspamd_logger.errx(task, 'rule %s - got error while getting reputation keys %s: %s',
- rule['symbol'], key, err)
+ rule['symbol'], key, err)
continuation_cb(err, key, nil)
else
rspamd_logger.errx(task, 'rule %s - got error while getting reputation keys %s: %s',
- rule['symbol'], key, "unknown error")
+ rule['symbol'], key, "unknown error")
continuation_cb("unknown error", key, nil)
end
end
local ret = lua_redis.exec_redis_script(rule.backend.script_get,
- {task = task, is_write = false},
+ { task = task, is_write = false },
redis_get_cb,
- {key})
+ { key })
if not ret then
rspamd_logger.errx(task, 'cannot make redis request to check results')
end
local function redis_set_cb(err, data)
if err then
rspamd_logger.errx(task, 'rule %s - got error while setting reputation keys %s: %s',
- rule['symbol'], key, err)
+ rule['symbol'], key, err)
if continuation_cb then
continuation_cb(err, key)
end
lua_util.debugm(N, task, 'rule %s - set values for key %s -> %s',
rule['symbol'], key, sc)
local ret = lua_redis.exec_redis_script(rule.backend.script_set,
- {task = task, is_write = true},
+ { task = task, is_write = true },
redis_set_cb,
- {key, tostring(os.time() * 1000),
- tostring(sc),
- tostring(rule.backend.config.expiry)})
+ { key, tostring(os.time() * 1000),
+ tostring(sc),
+ tostring(rule.backend.config.expiry) })
if not ret then
rspamd_logger.errx(task, 'got error while connecting to redis')
end
schema = lua_redis.generate_schema({
prefix = ts.string,
expiry = ts.number + ts.string / lua_util.parse_time_interval,
- buckets = ts.array_of(ts.shape{
+ buckets = ts.array_of(ts.shape {
time = ts.number + ts.string / lua_util.parse_time_interval,
name = ts.string,
mult = ts.number + ts.string / tonumber
set_token = reputation_redis_set_token,
},
dns = {
- schema = ts.shape{
+ schema = ts.shape {
list = ts.string,
},
config = {
end
local function parse_rule(name, tbl)
- local sel_type,sel_conf = fun.head(tbl.selector)
+ local sel_type, sel_conf = fun.head(tbl.selector)
local selector = selectors[sel_type]
if not selector then
rspamd_logger.errx(rspamd_config, "unknown selector defined for rule %s: %s", name,
sel_type)
- return
+ return false
end
- local bk_type,bk_conf = fun.head(tbl.backend)
+ local bk_type, bk_conf = fun.head(tbl.backend)
local backend = backends[bk_type]
if not backend then
rspamd_logger.errx(rspamd_config, "unknown backend defined for rule %s: %s", name,
- tbl.backend.type)
- return
+ tbl.backend.type)
+ return false
end
-- Allow config override
local rule = {
-- Override default config params
rule.backend.config = lua_util.override_defaults(rule.backend.config, bk_conf)
if backend.schema then
- local checked,schema_err = backend.schema:transform(rule.backend.config)
+ local checked, schema_err = backend.schema:transform(rule.backend.config)
if not checked then
rspamd_logger.errx(rspamd_config, "cannot parse backend config for %s: %s",
sel_type, schema_err)
- return
+ return false
end
rule.backend.config = checked
rule.selector.config = lua_util.override_defaults(rule.selector.config, sel_conf)
if selector.schema then
- local checked,schema_err = selector.schema:transform(rule.selector.config)
+ local checked, schema_err = selector.schema:transform(rule.selector.config)
if not checked then
rspamd_logger.errx(rspamd_config, "cannot parse selector config for %s: %s (%s)",
-- Hack: we assume that it is an ip whitelist :(
local ip = task:get_from_ip()
- if ip and map:get_key(ip) then return true end
+ if ip and map:get_key(ip) then
+ return true
+ end
return false
end
}
rule_type = 'callback'
end
- local id = rspamd_config:register_symbol{
+ local id = rspamd_config:register_symbol {
name = rule.symbol,
type = rule_type,
callback = callback_gen(reputation_filter_cb, rule),
- augmentations = {string.format("timeout=%f", redis_params.timeout or 0.0)},
+ augmentations = { string.format("timeout=%f", redis_params.timeout or 0.0) },
}
if rule.selector.config.split_symbols then
- rspamd_config:register_symbol{
+ rspamd_config:register_symbol {
name = rule.symbol .. '_HAM',
type = 'virtual',
parent = id,
}
- rspamd_config:register_symbol{
+ rspamd_config:register_symbol {
name = rule.symbol .. '_SPAM',
type = 'virtual',
parent = id,
if rule.selector.postfilter then
-- Also register a postfilter
- rspamd_config:register_symbol{
+ rspamd_config:register_symbol {
name = rule.symbol .. '_POST',
type = 'postfilter',
flags = 'nostat,explicit_disable,ignore_passthrough',
callback = callback_gen(reputation_postfilter_cb, rule),
- augmentations = {string.format("timeout=%f", redis_params.timeout or 0.0)},
+ augmentations = { string.format("timeout=%f", redis_params.timeout or 0.0) },
}
end
if rule.selector.idempotent then
-- Has also idempotent component (e.g. saving data to the backend)
- rspamd_config:register_symbol{
+ rspamd_config:register_symbol {
name = rule.symbol .. '_IDEMPOTENT',
type = 'idempotent',
flags = 'explicit_disable,ignore_passthrough',
callback = callback_gen(reputation_idempotent_cb, rule),
- augmentations = {string.format("timeout=%f", redis_params.timeout or 0.0)},
+ augmentations = { string.format("timeout=%f", redis_params.timeout or 0.0) },
}
end
-- Initialization part
if not (opts and type(opts) == 'table') then
- rspamd_logger.infox(rspamd_config, 'Module is unconfigured')
+ rspamd_logger.infox(rspamd_config, 'Module is not configured, disabling it')
return
end
if opts['rules'] then
- for k,v in pairs(opts['rules']) do
+ for k, v in pairs(opts['rules']) do
if not ((v or E).selector) then
rspamd_logger.errx(rspamd_config, "no selector defined for rule %s", k)
else