return false
end
- local function check_specific_setting(rule_name, rule, ip, client_ip, from, rcpt,
- user, auth_user, hostname, matched)
+ local function check_specific_setting(rule_name, rule, data, matched)
local res = false
- if rule.authenticated then
- if auth_user then
- res = true
- matched[#matched + 1] = 'authenticated'
- end
- if not res then
- return nil
- end
+ local function ip_valid(ip)
+ return ip:is_valid()
end
- if rule['local'] then
- if not ip or not ip:is_valid() then
- return nil
+ local function not_empty(s)
+ return #s > 0
+ end
+
+ local function generic_check(value, to_check, check_func, what, valid_func)
+ if not to_check then return true end
+
+ if type(value) == 'function' then
+ value = value()
end
- if ip:is_local() then
- matched[#matched + 1] = 'local'
- res = true
+ if value then
+ if valid_func then
+ if not valid_func(value) then
+ return false
+ end
+ end
+
+ if not check_func then
+ check_func = function(a, b) return a == b end
+ end
+
+ local ret = fun.any(function(d)
+ return check_func(value, d)
+ end, to_check)
+ if ret then
+ res = true
+ matched[#matched + 1] = what
+ else
+ return false
+ end
else
- return nil
+ return false
end
+
+ return true
end
- if rule.ip then
- if not ip or not ip:is_valid() then
- return nil
- end
- for _, ip_check in ipairs(rule.ip) do
- res = check_ip_setting(ip_check, ip)
- if res then
- matched[#matched + 1] = 'ip'
- break
- end
- end
- if not res then
- return nil
- end
+ if not generic_check(data.ip, rule.ip,
+ check_ip_setting, 'ip', ip_valid) then
+ return nil
end
- if rule.client_ip then
- if not client_ip or not client_ip:is_valid() then
- return nil
- end
- for _, ip_check in ipairs(rule.client_ip) do
- res = check_ip_setting(ip_check, client_ip)
- if res then
- matched[#matched + 1] = 'client_ip'
- break
- end
- end
- if not res then
- return nil
- end
+ if not generic_check(data.client_ip, rule.client_ip,
+ check_ip_setting, 'client_ip', ip_valid) then
+ return nil
end
- if rule.from then
- if not from then
- return nil
- end
- for _, from_check in ipairs(rule.from) do
- res = check_addr_setting(from_check, from)
- if res then
- matched[#matched + 1] = 'from'
- break
- end
- end
- if not res then
- return nil
- end
+ if not generic_check(data.from, rule.from,
+ check_addr_setting, 'from') then
+ return nil
end
- if rule.rcpt then
- if not rcpt then
- return nil
- end
- for _, rcpt_check in ipairs(rule.rcpt) do
- res = check_addr_setting(rcpt_check, rcpt)
+ if not generic_check(data.from_mime, rule.from_mime,
+ check_addr_setting, 'from_mime') then
+ return nil
+ end
- if res then
- matched[#matched + 1] = 'rcpt'
- break
- end
- end
- if not res then
- return nil
- end
+ if not generic_check(data.rcpt, rule.rcpt,
+ check_addr_setting, 'rcpt') then
+ return nil
end
- if rule.user then
- if not user then
- return nil
- end
- for _, user_check in ipairs(rule.user) do
- res = check_addr_setting(user_check, user)
- if res then
- matched[#matched + 1] = 'user'
- break
- end
+ if not generic_check(data.rcpt_mime, rule.rcpt_mime,
+ check_addr_setting, 'rcpt_mime') then
+ return nil
+ end
+
+ if not generic_check(data.user, rule.user,
+ check_addr_setting, 'user') then
+ return nil
+ end
+
+ if not generic_check(data.hostname, rule.hostname,
+ check_addr_setting, 'hostname', not_empty) then
+ return nil
+ end
+
+ -- Non generic checks
+
+ if rule.authenticated then
+ if data.user[1] then
+ res = true
+ matched[#matched + 1] = 'authenticated'
end
if not res then
return nil
end
end
- if rule.hostname then
- if #hostname == 0 then
+ if rule['local'] then
+ if not data.ip or not data.ip:is_valid() then
return nil
end
- for _, hname_check in ipairs(rule.hostname) do
- res = check_addr_setting(hname_check, hostname)
- if res then
- matched[#matched + 1] = 'hostname'
- break
- end
- end
- if not res then
+
+ if data.ip:is_local() then
+ matched[#matched + 1] = 'local'
+ res = true
+ else
return nil
end
end
end
lua_util.debugm(N, task, "check for settings")
- local ip = task:get_from_ip()
- local client_ip = task:get_client_ip()
- local from = task:get_from()
- local rcpt = task:get_recipients()
+ local data = {
+ ip = task:get_from_ip(),
+ client_ip = task:get_client_ip(),
+ from = task:get_from(1),
+ from_mime = task:get_from(2),
+ rcpt = task:get_recipients(1),
+ rcpt_mime = task:get_recipients(2),
+ hostname = task:get_hostname() or '',
+ user = {}
+ }
+
local uname = task:get_user()
- local hostname = task:get_hostname() or ''
- local user = {}
if uname then
- user[1] = {}
+ data.user[1] = {}
local localpart, domainpart = string.gmatch(uname, "(.+)@(.+)")()
if localpart then
- user[1]["user"] = localpart
- user[1]["domain"] = domainpart
- user[1]["addr"] = uname
+ data.user[1]["user"] = localpart
+ data.user[1]["domain"] = domainpart
+ data.user[1]["addr"] = uname
else
- user[1]["user"] = uname
- user[1]["addr"] = uname
+ data.user[1]["user"] = uname
+ data.user[1]["addr"] = uname
end
end
-- Match rules according their order
if not applied and settings[pri] then
for _,s in ipairs(settings[pri]) do
local matched = {}
- local rule = check_specific_setting(s.name, s.rule,
- ip, client_ip, from, rcpt, user, uname, hostname, matched)
+ local result = check_specific_setting(s.name, s.rule, data, matched)
-- Can use xor here but more complicated for reading
- if (rule and not s.rule.inverse) or (not rule and s.rule.inverse) then
+ if (result and not s.rule.inverse) or (not result and s.rule.inverse) then
rspamd_logger.infox(task, "<%s> apply settings according to rule %s (%s matched)",
task:get_message_id(), s.name, table.concat(matched, ','))
if s.rule['apply'] then