diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2015-04-20 13:03:48 +0100 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2015-04-20 13:03:48 +0100 |
commit | 385bae9bb516fbef8b139c9c0b231e7e24272ad4 (patch) | |
tree | 864c042c24d93b738538475dc8d3af49ca7638b4 /src/plugins | |
parent | df5b48042651c4ac2b2d94bc469d9b0396e6b62e (diff) | |
download | rspamd-385bae9bb516fbef8b139c9c0b231e7e24272ad4.tar.gz rspamd-385bae9bb516fbef8b139c9c0b231e7e24272ad4.zip |
Rework spamassassin functions:
1) now we support some plugins, so do not refuse to parse them
2) added freemail plugin
3) rework SA functions evaluation
4) add support for eval functions
5) add freemail eval
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/lua/spamassassin.lua | 176 |
1 files changed, 158 insertions, 18 deletions
diff --git a/src/plugins/lua/spamassassin.lua b/src/plugins/lua/spamassassin.lua index 35e49dce1..d93104486 100644 --- a/src/plugins/lua/spamassassin.lua +++ b/src/plugins/lua/spamassassin.lua @@ -31,11 +31,20 @@ local rspamd_logger = require "rspamd_logger" local rspamd_regexp = require "rspamd_regexp" local rspamd_expression = require "rspamd_expression" local rspamd_mempool = require "rspamd_mempool" +local rspamd_trie = require "rspamd_trie" local _ = require "fun" + --local dumper = require 'pl.pretty'.dump + +-- Known plugins +local known_plugins = {'Mail::SpamAssassin::Plugin::FreeMail'} + +-- Internal variables local rules = {} local atoms = {} local metas = {} +local freemail_domains = {} +local freemail_trie local section = rspamd_config:get_key("spamassassin") -- Minimum score to treat symbols as meta @@ -123,6 +132,125 @@ local function handle_header_def(hline, cur_rule) end end + +local function freemail_search(input) + local res = false + local function trie_callback(number, pos) + rspamd_logger.debugx('Matched pattern %1 at pos %2', freemail_domains[number], pos) + res = true + end + + if input then + freemail_trie:match(input, trie_callback, true) + end + + return res +end + +local function gen_eval_rule(arg) + local eval_funcs = { + {'check_freemail_from', function(task, remain) + local from = task:get_from() + if from then + return freemail_search(from[1]['addr']) + end + return false + end}, + {'check_freemail_replyto', + function(task, remain) + return freemail_search(task:get_header('Reply-To')) + end + }, + {'check_freemail_header', + function(task, remain) + -- Remain here contains one or two args: header and regexp to match + local arg = string.match(remain, "^%(%s*['\"]([^%s]+)['\"]%s*%)$") + local re = nil + if not arg then + arg, re = string.match(remain, "^%(%s*['\"]([^%s]+)['\"]%s*,%s*['\"]([^%s]+)['\"]%s*%)$") + end + + if arg then + local h = task:get_header(arg) + if h then + local hdr_freemail = freemail_search(h) + if hdr_freemail and re then + r = rspamd_regexp.create_cached(re) + if r then + r:match(h) + else + rspamd_logger.infox('cannot create regexp %1', re) + return false + end + end + + return hdr_freemail + end + end + + return false + end + }, + } + + for k,f in ipairs(eval_funcs) do + local pat = string.format('^%s', f[1]) + local first,last = string.find(arg, pat) + + if first then + local func_arg = string.sub(arg, last + 1) + return function(task) + return f[2](task, func_arg) + end + end + end +end + +-- Returns parser function or nil +local function maybe_parse_sa_function(line) + local arg + local elts = split(line, '[^:]+') + arg = elts[2] + local func_cache = {} + + rspamd_logger.debugx('trying to parse SA function %1 with args %2', elts[1], elts[2]) + local substitutions = { + {'^exists:', + function(task) -- filter + if task:get_header(arg) then + return true + end + return false + end, + }, + {'^eval:', + function(task) + local func = func_cache[arg] + if not func then + func = gen_eval_rule(arg) + func_cache[arg] = func + end + + if not func then + rspamd_logger.errx('cannot find appropriate eval rule for function %1', arg) + else + return func(task) + end + + return false + end + }, + } + + for k,s in ipairs(substitutions) do + if string.find(line, s[1]) then + return s[2] + end + end + + return nil +end + local function process_sa_conf(f) local cur_rule = {} local valid_rule = false @@ -153,7 +281,14 @@ local function process_sa_conf(f) return else if string.match(l, '^ifplugin') then - skip_to_endif = true + local ls = split(l) + + if not _.any(function(pl) + if pl == ls[2] then return true end + return false + end, known_plugins) then + skip_to_endif = true + end end end @@ -171,7 +306,7 @@ local function process_sa_conf(f) if valid_rule then insert_cur_rule() end - if slash then + if slash and words[4] and (words[4] == '=~' or words[4] == '!~') then cur_rule['type'] = 'header' cur_rule['symbol'] = words[2] @@ -205,20 +340,16 @@ local function process_sa_conf(f) end else -- Maybe we know the function and can convert it - local s,e = string.find(words[3], 'exists:') - if e then - local h = _.foldl(function(acc, s) return acc .. s end, - '', _.drop_n(e, words[3])) - cur_rule['type'] = 'function' - cur_rule['symbol'] = words[2] - cur_rule['header'] = h - cur_rule['function'] = function(task) - if task:get_header(h) then - return true - end - return false - end - valid_rule = true + local args = words_to_re(words, 2) + local func = maybe_parse_sa_function(args) + + if func then + cur_rule['type'] = 'function' + cur_rule['symbol'] = words[2] + cur_rule['function'] = func + valid_rule = true + else + rspamd_logger.infox('unknown function %1', args) end end elseif words[1] == "body" and slash then @@ -261,9 +392,13 @@ local function process_sa_conf(f) cur_rule['meta'] = words_to_re(words, 2) if cur_rule['meta'] and cur_rule['symbol'] then valid_rule = true end elseif words[1] == "describe" and valid_rule then - cur_rule['description'] = words_to_re(words, 1) + cur_rule['description'] = words_to_re(words, 2) elseif words[1] == "score" and valid_rule then cur_rule['score'] = tonumber(words_to_re(words, 2)) + elseif words[1] == 'freemail_domains' then + _.each(function(dom) + table.insert(freemail_domains, '@' .. dom) + end, _.drop_n(1, words)) end end)() end @@ -304,6 +439,11 @@ local function add_sole_meta(sym, rule) rules[sym] = r end +if freemail_domains then + freemail_trie = rspamd_trie.create(freemail_domains) + rspamd_logger.infox('loaded %1 freemail domains definitions', #freemail_domains) +end + -- Header rules _.each(function(k, r) local f = function(task) @@ -489,7 +629,7 @@ local function process_atom(atom, task) end return res else - --rspamd_logger.err('Cannot find atom ' .. atom) + rspamd_logger.err('Cannot find atom ' .. atom) end return 0 end |