--[[ Copyright (c) 2011-2015, Vsevolod Stakhov Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ]]-- if confighelp then return end -- Module for checking mail list headers local N = 'maillist' local symbol = 'MAILLIST' local lua_util = require "lua_util" -- EZMLM -- Mailing-List: .*run by ezmlm -- Precedence: bulk -- List-Post: -- List-Help: -- List-Subscribe: -- List-Unsubscribe: -- List-Archive: -- List-Owner: -- List-Post: NO local function check_ml_subscriberu(task) -- List-Id local header = task:get_header('list-id') if not (header and header:find('^<.*%.subscribe%.ru>$')) then return false end -- Other headers header = task:get_header('list-archive') if not (header and header:find('^$')) then return false end header = task:get_header('list-owner') if not (header and header:find('^$')) then return false end header = task:get_header('list-help') if not (header and header:find('^$')) then return false end -- Subscribe and unsubscribe header = task:get_header('list-subscribe') if not (header and header:find('^$')) then return false end header = task:get_header('list-unsubscribe') if not (header and header:find('^$')) then return false end return true end -- Google groups detector -- header exists X-Google-Loop -- RFC 2919 headers exist -- local function check_ml_googlegroup(task) return task:has_header('X-Google-Loop') or task:has_header('X-Google-Group-Id') end -- CGP detector -- X-Listserver = CommuniGate Pro LIST -- RFC 2919 headers exist -- local function check_ml_cgp(task) local header = task:get_header('X-Listserver') if not header or string.sub(header, 0, 20) ~= 'CommuniGate Pro LIST' then return false end return true end -- RFC 2919 headers local function check_generic_list_headers(task) local score = 0 local has_subscribe, has_unsubscribe if task:has_header('List-Id') then score = score + 0.75 lua_util.debugm(N, task, 'has List-Id header, score = %s', score) end local header = task:get_header('Precedence') if header and (header == 'list' or header == 'bulk') then score = score + 0.25 lua_util.debugm(N, task, 'has header "Precedence: %s", score = %s', header, score) end if task:has_header('List-Archive') then score = score + 0.125 lua_util.debugm(N, task, 'has header List-Archive, score = %s', score) end if task:has_header('List-Owner') then score = score + 0.125 lua_util.debugm(N, task, 'has header List-Owner, score = %s', score) end if task:has_header('List-Help') then score = score + 0.125 lua_util.debugm(N, task, 'has header List-Help, score = %s', score) end -- Subscribe and unsubscribe if task:has_header('List-Subscribe') then has_subscribe = true score = score + 0.125 lua_util.debugm(N, task, 'has header List-Subscribe, score = %s', score) end if task:has_header('List-Unsubscribe') then has_unsubscribe = true score = score + 0.125 lua_util.debugm(N, task, 'has header List-Unsubscribe, score = %s', score) end if task:has_header('X-Loop') then score = score + 0.125 lua_util.debugm(N, task, 'has header X-Loop, score = %s', score) end if has_subscribe and has_unsubscribe then score = score + 0.25 elseif (has_unsubscribe) then score = score - 0.25 elseif (has_subscribe) then score = score - 0.75 end lua_util.debugm(N, task, 'final maillist score %s', score) return score end -- RFC 2919 headers exist local function check_maillist(task) local score = check_generic_list_headers(task) if score >= 1 then if check_ml_ezmlm(task) then task:insert_result(symbol, 1, 'ezmlm') elseif check_ml_mailman(task) then task:insert_result(symbol, 1, 'mailman') elseif check_ml_subscriberu(task) then task:insert_result(symbol, 1, 'subscribe.ru') elseif check_ml_googlegroup(task) then task:insert_result(symbol, 1, 'googlegroups') elseif check_ml_cgp(task) then task:insert_result(symbol, 1, 'cgp') else if score > 2 then score = 2 end task:insert_result(symbol, 0.5 * score, 'generic') end end end -- Configuration local opts = rspamd_config:get_all_opt('maillist') if opts then if opts['symbol'] then symbol = opts['symbol'] rspamd_config:register_symbol({ name = symbol, callback = check_maillist }) end end