You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hfilter.lua 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. --[[
  2. Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
  3. Copyright (c) 2013-2015, Alexey Savelyev <info@homeweb.ru>
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. ]]--
  14. -- Weight for checks_hellohost and checks_hello: 5 - very hard, 4 - hard, 3 - medium, 2 - low, 1 - very low.
  15. -- From HFILTER_HELO_* and HFILTER_HOSTNAME_* symbols the maximum weight is selected in case of their actuating.
  16. if confighelp then
  17. return
  18. end
  19. local rspamd_regexp = require "rspamd_regexp"
  20. local lua_util = require "lua_util"
  21. local rspamc_local_helo = "rspamc.local"
  22. local checks_hellohost = [[
  23. /[-.0-9][0-9][.-]?nat/i 5
  24. /homeuser[.-][0-9]/i 5
  25. /[-.0-9][0-9][.-]?unused-addr/i 3
  26. /[-.0-9][0-9][.-]?pppoe/i 5
  27. /[-.0-9][0-9][.-]?dynamic/i 5
  28. /[.-]catv[.-]/i 5
  29. /unused-addr[.-][0-9]/i 3
  30. /comcast[.-][0-9]/i 5
  31. /[.-]broadband[.-]/i 5
  32. /[0-9][.-]?fbx/i 4
  33. /[.-]peer[.-]/i 1
  34. /[.-]homeuser[.-]/i 5
  35. /[-.0-9][0-9][.-]?catv/i 5
  36. /customers?[.-][0-9]/i 1
  37. /[.-]wifi[.-]/i 5
  38. /[0-9][.-]?kabel/i 3
  39. /dynip[.-][0-9]/i 5
  40. /[.-]broad[.-]/i 5
  41. /[a|x]?dsl-line[.-]?[0-9]/i 4
  42. /[-.0-9][0-9][.-]?ppp/i 5
  43. /pool[.-][0-9]/i 4
  44. /[.-]nat[.-]/i 5
  45. /gprs[.-][0-9]/i 5
  46. /brodband[.-][0-9]/i 5
  47. /[.-]gprs[.-]/i 5
  48. /[.-]user[.-]/i 1
  49. /[-.0-9][0-9][.-]?in-?addr/i 4
  50. /[.-]host[.-]/i 2
  51. /[.-]fbx[.-]/i 4
  52. /dynamic[.-][0-9]/i 5
  53. /[-.0-9][0-9][.-]?peer/i 1
  54. /[-.0-9][0-9][.-]?pool/i 4
  55. /[-.0-9][0-9][.-]?user/i 1
  56. /[.-]cdma[.-]/i 5
  57. /user[.-][0-9]/i 1
  58. /[-.0-9][0-9][.-]?customers?/i 1
  59. /ppp[.-][0-9]/i 5
  60. /kabel[.-][0-9]/i 3
  61. /dhcp[.-][0-9]/i 5
  62. /peer[.-][0-9]/i 1
  63. /[-.0-9][0-9][.-]?host/i 2
  64. /clients?[.-][0-9]{2,}/i 5
  65. /host[.-][0-9]/i 2
  66. /[.-]ppp[.-]/i 5
  67. /[.-]dhcp[.-]/i 5
  68. /[.-]comcast[.-]/i 5
  69. /cable[.-][0-9]/i 3
  70. /[-.0-9][0-9][.-]?dial-?up/i 5
  71. /[-.0-9][0-9][.-]?bredband/i 5
  72. /[-.0-9][0-9][.-]?[a|x]?dsl-line/i 4
  73. /[.-]dial-?up[.-]/i 5
  74. /[.-]cablemodem[.-]/i 5
  75. /pppoe[.-][0-9]/i 5
  76. /[.-]unused-addr[.-]/i 3
  77. /pptp[.-][0-9]/i 5
  78. /broadband[.-][0-9]/i 5
  79. /[.-][a|x]?dsl-line[.-]/i 4
  80. /[.-]customers?[.-]/i 1
  81. /[-.0-9][0-9][.-]?fibertel/i 4
  82. /[-.0-9][0-9][.-]?comcast/i 5
  83. /[.-]dynamic[.-]/i 5
  84. /cdma[.-][0-9]/i 5
  85. /[0-9][.-]?broad/i 5
  86. /fbx[.-][0-9]/i 4
  87. /catv[.-][0-9]/i 5
  88. /[-.0-9][0-9][.-]?homeuser/i 5
  89. /[-.0-9][.-]pppoe[.-]/i 5
  90. /[-.0-9][.-]dynip[.-]/i 5
  91. /[-.0-9][0-9][.-]?[a|x]?dsl/i 4
  92. /[-.0-9][0-9]{3,}[.-]?clients?/i 5
  93. /[-.0-9][0-9][.-]?pptp/i 5
  94. /[.-]clients?[.-]/i 1
  95. /[.-]in-?addr[.-]/i 4
  96. /[.-]pool[.-]/i 4
  97. /[a|x]?dsl[.-]?[0-9]/i 4
  98. /[.-][a|x]?dsl[.-]/i 4
  99. /[-.0-9][0-9][.-]?[a|x]?dsl-dynamic/i 5
  100. /dial-?up[.-][0-9]/i 5
  101. /[-.0-9][0-9][.-]?cablemodem/i 5
  102. /[a|x]?dsl-dynamic[.-]?[0-9]/i 5
  103. /[.-]pptp[.-]/i 5
  104. /[.-][a|x]?dsl-dynamic[.-]/i 5
  105. /[0-9][.-]?wifi/i 5
  106. /fibertel[.-][0-9]/i 4
  107. /dyn[.-][0-9][-.0-9]/i 5
  108. /[-.0-9][0-9][.-]broadband/i 5
  109. /[-.0-9][0-9][.-]cable/i 3
  110. /broad[.-][0-9]/i 5
  111. /[-.0-9][0-9][.-]gprs/i 5
  112. /cablemodem[.-][0-9]/i 5
  113. /[-.0-9][0-9][.-]modem/i 5
  114. /[-.0-9][0-9][.-]dyn/i 5
  115. /[-.0-9][0-9][.-]dynip/i 5
  116. /[-.0-9][0-9][.-]cdma/i 5
  117. /[.-]modem[.-]/i 5
  118. /[.-]kabel[.-]/i 3
  119. /[.-]cable[.-]/i 3
  120. /in-?addr[.-][0-9]/i 4
  121. /nat[.-][0-9]/i 5
  122. /[.-]fibertel[.-]/i 4
  123. /[.-]bredband[.-]/i 5
  124. /modem[.-][0-9]/i 5
  125. /[0-9][.-]?dhcp/i 5
  126. /wifi[.-][0-9]/i 5
  127. ]]
  128. local checks_hellohost_map
  129. local checks_hello = [[
  130. /^[^\.]+$/i 5 # for helo=COMPUTER, ANNA, etc... Without dot in helo
  131. /^(dsl)?(device|speedtouch)\.lan$/i 5
  132. /\.(lan|local|home|localdomain|intra|in-addr.arpa|priv|user|veloxzon)$ 5
  133. ]]
  134. local checks_hello_map
  135. local checks_hello_badip = [[
  136. /^\d\.\d\.\d\.255$/i 1
  137. /^192\.0\.0\./i 1
  138. /^2001:db8::/i 1
  139. /^10\./i 1
  140. /^192\.0\.2\./i 1
  141. /^172\.1[6-9]\./i 1
  142. /^192\.168\./i 1
  143. /^::1$/i 1 # loopback ipv4, ipv6
  144. /^ffxx::/i 1
  145. /^fc00::/i 1
  146. /^203\.0\.113\./i 1
  147. /^fe[cdf][0-9a-f]:/i 1
  148. /^100.12[0-7]\d\./i 1
  149. /^fe[89ab][0-9a-f]::/i 1
  150. /^169\.254\./i 1
  151. /^0\./i 1
  152. /^198\.51\.100\./i 1
  153. /^172\.3[01]\./i 1
  154. /^100.[7-9]\d\./i 1
  155. /^100.1[01]\d\./i 1
  156. /^127\./i 1
  157. /^100.6[4-9]\./i 1
  158. /^192\.88\.99\./i 1
  159. /^172\.2[0-9]\./i 1
  160. ]]
  161. local checks_hello_badip_map
  162. local checks_hello_bareip = [[
  163. /^\d+[x.-]\d+[x.-]\d+[x.-]\d+$/
  164. /^[0-9a-f]+:/
  165. ]]
  166. local checks_hello_bareip_map
  167. local config = {
  168. ['helo_enabled'] = false,
  169. ['hostname_enabled'] = false,
  170. ['from_enabled'] = false,
  171. ['rcpt_enabled'] = false,
  172. ['mid_enabled'] = false,
  173. ['url_enabled'] = false
  174. }
  175. local compiled_regexp = {} -- cache of regexps
  176. local check_local = false
  177. local check_authed = false
  178. local N = "hfilter"
  179. local function check_regexp(str, regexp_text)
  180. local re = compiled_regexp[regexp_text]
  181. if not re then
  182. re = rspamd_regexp.create(regexp_text, 'i')
  183. compiled_regexp[regexp_text] = re
  184. end
  185. return re:match(str)
  186. end
  187. local function add_static_map(data)
  188. return rspamd_config:add_map{
  189. type = 'regexp_multi',
  190. url = {
  191. upstreams = 'static',
  192. data = data,
  193. }
  194. }
  195. end
  196. local function check_fqdn(domain)
  197. if check_regexp(domain,
  198. '(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\\.)+[a-zA-Z0-9-]{2,63}\\.?$)') then
  199. return true
  200. end
  201. return false
  202. end
  203. -- host: host for check
  204. -- symbol_suffix: suffix for symbol
  205. -- eq_ip: ip for comparing or empty string
  206. -- eq_host: host for comparing or empty string
  207. local function check_host(task, host, symbol_suffix, eq_ip, eq_host)
  208. local failed_address = 0
  209. local resolved_address = {}
  210. local function check_host_cb_mx(_, to_resolve, results, err)
  211. if err and (err ~= 'requested record is not found' and err ~= 'no records with this name') then
  212. lua_util.debugm(N, task, 'error looking up %s: %s', to_resolve, err)
  213. end
  214. if not results then
  215. task:insert_result('HFILTER_' .. symbol_suffix .. '_NORES_A_OR_MX', 1.0,
  216. to_resolve)
  217. else
  218. for _,mx in pairs(results) do
  219. if mx['name'] then
  220. local failed_mx_address = 0
  221. -- Capture failed_mx_address
  222. local function check_host_cb_mx_a(_, _, mx_results)
  223. if not mx_results then
  224. failed_mx_address = failed_mx_address + 1
  225. end
  226. if failed_mx_address >= 2 then
  227. task:insert_result('HFILTER_' .. symbol_suffix .. '_NORESOLVE_MX',
  228. 1.0, mx['name'])
  229. end
  230. end
  231. task:get_resolver():resolve('a', {
  232. task=task,
  233. name = mx['name'],
  234. callback = check_host_cb_mx_a
  235. })
  236. task:get_resolver():resolve('aaaa', {
  237. task = task,
  238. name = mx['name'],
  239. callback = check_host_cb_mx_a
  240. })
  241. end
  242. end
  243. end
  244. end
  245. local function check_host_cb_a(_, _, results)
  246. if not results then
  247. failed_address = failed_address + 1
  248. else
  249. for _,result in pairs(results) do
  250. table.insert(resolved_address, result:to_string())
  251. end
  252. end
  253. if failed_address >= 2 then
  254. -- No A or AAAA records
  255. if eq_ip and eq_ip ~= '' then
  256. for _,result in pairs(resolved_address) do
  257. if result == eq_ip then
  258. return true
  259. end
  260. end
  261. task:insert_result('HFILTER_' .. symbol_suffix .. '_IP_A', 1.0, host)
  262. end
  263. task:get_resolver():resolve_mx({
  264. task = task,
  265. name = host,
  266. callback = check_host_cb_mx
  267. })
  268. end
  269. end
  270. if host then
  271. host = string.lower(host)
  272. else
  273. return false
  274. end
  275. if eq_host then
  276. eq_host = string.lower(eq_host)
  277. else
  278. eq_host = ''
  279. end
  280. if check_fqdn(host) then
  281. if eq_host == '' or eq_host ~= host then
  282. task:get_resolver():resolve('a', {
  283. task=task,
  284. name = host,
  285. callback = check_host_cb_a
  286. })
  287. -- Check ipv6 as well
  288. task:get_resolver():resolve('aaaa', {
  289. task = task,
  290. name = host,
  291. callback = check_host_cb_a
  292. })
  293. end
  294. else
  295. task:insert_result('HFILTER_' .. symbol_suffix .. '_NOT_FQDN', 1.0, host)
  296. end
  297. return true
  298. end
  299. --
  300. local function hfilter_callback(task)
  301. -- Links checks
  302. if config['url_enabled'] then
  303. local parts = task:get_text_parts()
  304. if parts then
  305. local plain_text_part = nil
  306. local html_text_part = nil
  307. for _,p in ipairs(parts) do
  308. if p:is_html() then
  309. html_text_part = p
  310. else
  311. plain_text_part = p
  312. end
  313. end
  314. local hc = nil
  315. if html_text_part then
  316. hc = html_text_part:get_html()
  317. if hc then
  318. local url_len = 0
  319. hc:foreach_tag('a', function(_, len)
  320. url_len = url_len + len
  321. return false
  322. end)
  323. local plen = html_text_part:get_length()
  324. if url_len > 0 and plen > 0 then
  325. local rel = url_len / plen
  326. if rel > 0.8 then
  327. local sc = (rel - 0.8) * 5.0
  328. if sc > 1.0 then sc = 1.0 end
  329. task:insert_result('HFILTER_URL_ONLY', sc, tostring(sc))
  330. local lines = html_text_part:get_lines_count()
  331. if lines > 0 and lines < 2 then
  332. task:insert_result('HFILTER_URL_ONELINE', 1.00,
  333. string.format('html:%d:%d', math.floor(sc), lines))
  334. end
  335. end
  336. end
  337. end
  338. end
  339. if not hc and plain_text_part then
  340. local url_len = plain_text_part:get_urls_length()
  341. local plen = plain_text_part:get_length()
  342. if plen > 0 and url_len > 0 then
  343. local rel = url_len / plen
  344. if rel > 0.8 then
  345. local sc = (rel - 0.8) * 5.0
  346. if sc > 1.0 then sc = 1.0 end
  347. task:insert_result('HFILTER_URL_ONLY', sc, tostring(sc))
  348. local lines = plain_text_part:get_lines_count()
  349. if lines > 0 and lines < 2 then
  350. task:insert_result('HFILTER_URL_ONELINE', 1.00,
  351. string.format('plain:%d:%d', math.floor(rel), lines))
  352. end
  353. end
  354. end
  355. end
  356. end
  357. end
  358. --No more checks for auth user or local network
  359. local rip = task:get_from_ip()
  360. if ((not check_authed and task:get_user()) or
  361. (not check_local and rip and rip:is_local())) then
  362. return false
  363. end
  364. --local message = task:get_message()
  365. local ip = false
  366. if rip and rip:is_valid() then
  367. ip = rip:to_string()
  368. end
  369. -- Check's HELO
  370. local weight_helo = 0
  371. local helo
  372. if config['helo_enabled'] then
  373. helo = task:get_helo()
  374. if helo then
  375. if helo ~= rspamc_local_helo then
  376. helo = string.gsub(helo, '[%[%]]', '')
  377. -- Regexp check HELO (checks_hello_badip)
  378. local find_badip = false
  379. local values = checks_hello_badip_map:get_key(helo)
  380. if values then
  381. task:insert_result('HFILTER_HELO_BADIP', 1.0, helo, values)
  382. find_badip = true
  383. end
  384. -- Regexp check HELO (checks_hello_bareip)
  385. local find_bareip = false
  386. if not find_badip then
  387. values = checks_hello_bareip_map:get_key(helo)
  388. if values then
  389. task:insert_result('HFILTER_HELO_BAREIP', 1.0, helo, values)
  390. find_bareip = true
  391. end
  392. end
  393. if not find_badip and not find_bareip then
  394. -- Regexp check HELO (checks_hello)
  395. local weights = checks_hello_map:get_key(helo)
  396. for _,weight in ipairs(weights or {}) do
  397. weight = tonumber(weight) or 0
  398. if weight > weight_helo then
  399. weight_helo = weight
  400. end
  401. end
  402. -- Regexp check HELO (checks_hellohost)
  403. weights = checks_hellohost_map:get_key(helo)
  404. for _,weight in ipairs(weights or {}) do
  405. weight = tonumber(weight) or 0
  406. if weight > weight_helo then
  407. weight_helo = weight
  408. end
  409. end
  410. --FQDN check HELO
  411. if ip and helo and weight_helo == 0 then
  412. check_host(task, helo, 'HELO', ip)
  413. end
  414. end
  415. end
  416. end
  417. end
  418. -- Check's HOSTNAME
  419. local weight_hostname = 0
  420. local hostname = task:get_hostname()
  421. if config['hostname_enabled'] then
  422. if hostname then
  423. -- Check regexp HOSTNAME
  424. local weights = checks_hellohost_map:get_key(hostname)
  425. for _,weight in ipairs(weights or {}) do
  426. weight = tonumber(weight) or 0
  427. if weight > weight_hostname then
  428. weight_hostname = weight
  429. end
  430. end
  431. else
  432. task:insert_result('HFILTER_HOSTNAME_UNKNOWN', 1.00)
  433. end
  434. end
  435. --Insert weight's for HELO or HOSTNAME
  436. if weight_helo > 0 and weight_helo >= weight_hostname then
  437. task:insert_result('HFILTER_HELO_' .. weight_helo, 1.0, helo)
  438. elseif weight_hostname > 0 and weight_hostname > weight_helo then
  439. task:insert_result('HFILTER_HOSTNAME_' .. weight_hostname, 1.0, hostname)
  440. end
  441. -- MAILFROM checks --
  442. local frombounce = false
  443. if config['from_enabled'] then
  444. local from = task:get_from(1)
  445. if from then
  446. --FROM host check
  447. for _,fr in ipairs(from) do
  448. local fr_split = rspamd_str_split(fr['addr'], '@')
  449. if #fr_split == 2 then
  450. check_host(task, fr_split[2], 'FROMHOST', '', '')
  451. if fr_split[1] == 'postmaster' then
  452. frombounce = true
  453. end
  454. end
  455. end
  456. else
  457. if helo and helo ~= rspamc_local_helo then
  458. task:insert_result('HFILTER_FROM_BOUNCE', 1.00, helo)
  459. frombounce = true
  460. end
  461. end
  462. end
  463. -- Recipients checks --
  464. if config['rcpt_enabled'] then
  465. local rcpt = task:get_recipients()
  466. if rcpt then
  467. local count_rcpt = #rcpt
  468. if frombounce then
  469. if count_rcpt > 1 then
  470. task:insert_result('HFILTER_RCPT_BOUNCEMOREONE', 1.00,
  471. tostring(count_rcpt))
  472. end
  473. end
  474. end
  475. end
  476. --Message ID host check
  477. if config['mid_enabled'] then
  478. local message_id = task:get_message_id()
  479. if message_id then
  480. local mid_split = rspamd_str_split(message_id, '@')
  481. if #mid_split == 2 and not string.find(mid_split[2], 'local') then
  482. check_host(task, mid_split[2], 'MID')
  483. end
  484. end
  485. end
  486. return false
  487. end
  488. local symbols_enabled = {}
  489. local symbols_helo = {
  490. "HFILTER_HELO_BAREIP",
  491. "HFILTER_HELO_BADIP",
  492. "HFILTER_HELO_1",
  493. "HFILTER_HELO_2",
  494. "HFILTER_HELO_3",
  495. "HFILTER_HELO_4",
  496. "HFILTER_HELO_5",
  497. "HFILTER_HELO_NORESOLVE_MX",
  498. "HFILTER_HELO_NORES_A_OR_MX",
  499. "HFILTER_HELO_IP_A",
  500. "HFILTER_HELO_NOT_FQDN"
  501. }
  502. local symbols_hostname = {
  503. "HFILTER_HOSTNAME_1",
  504. "HFILTER_HOSTNAME_2",
  505. "HFILTER_HOSTNAME_3",
  506. "HFILTER_HOSTNAME_4",
  507. "HFILTER_HOSTNAME_5",
  508. "HFILTER_HOSTNAME_UNKNOWN"
  509. }
  510. local symbols_rcpt = {
  511. "HFILTER_RCPT_BOUNCEMOREONE"
  512. }
  513. local symbols_mid = {
  514. "HFILTER_MID_NORESOLVE_MX",
  515. "HFILTER_MID_NORES_A_OR_MX",
  516. "HFILTER_MID_NOT_FQDN"
  517. }
  518. local symbols_url = {
  519. "HFILTER_URL_ONLY",
  520. "HFILTER_URL_ONELINE"
  521. }
  522. local symbols_from = {
  523. "HFILTER_FROMHOST_NORESOLVE_MX",
  524. "HFILTER_FROMHOST_NORES_A_OR_MX",
  525. "HFILTER_FROMHOST_NOT_FQDN",
  526. "HFILTER_FROM_BOUNCE"
  527. }
  528. local auth_and_local_conf = lua_util.config_check_local_or_authed(rspamd_config, N,
  529. false, false)
  530. check_local = auth_and_local_conf[1]
  531. check_authed = auth_and_local_conf[2]
  532. local opts = rspamd_config:get_all_opt('hfilter')
  533. if opts then
  534. for k,v in pairs(opts) do
  535. config[k] = v
  536. end
  537. end
  538. local function append_t(t, a)
  539. for _,v in ipairs(a) do table.insert(t, v) end
  540. end
  541. if config['helo_enabled'] then
  542. checks_hello_bareip_map = add_static_map(checks_hello_bareip)
  543. checks_hello_badip_map = add_static_map(checks_hello_badip)
  544. checks_hellohost_map = add_static_map(checks_hellohost)
  545. checks_hello_map = add_static_map(checks_hello)
  546. append_t(symbols_enabled, symbols_helo)
  547. end
  548. if config['hostname_enabled'] then
  549. if not checks_hellohost_map then
  550. checks_hellohost_map = add_static_map(checks_hellohost)
  551. end
  552. append_t(symbols_enabled, symbols_hostname)
  553. end
  554. if config['from_enabled'] then
  555. append_t(symbols_enabled, symbols_from)
  556. end
  557. if config['rcpt_enabled'] then
  558. append_t(symbols_enabled, symbols_rcpt)
  559. end
  560. if config['mid_enabled'] then
  561. append_t(symbols_enabled, symbols_mid)
  562. end
  563. if config['url_enabled'] then
  564. append_t(symbols_enabled, symbols_url)
  565. end
  566. --dumper(symbols_enabled)
  567. if #symbols_enabled > 0 then
  568. local id = rspamd_config:register_symbol{
  569. name = 'HFILTER',
  570. callback = hfilter_callback,
  571. type = 'callback,mime',
  572. score = 0.0,
  573. }
  574. rspamd_config:set_metric_symbol({
  575. name = 'HFILTER',
  576. score = 0.0,
  577. group = 'hfilter'
  578. })
  579. for _,sym in ipairs(symbols_enabled) do
  580. rspamd_config:register_symbol{
  581. type = 'virtual,mime',
  582. score = 1.0,
  583. parent = id,
  584. name = sym,
  585. }
  586. rspamd_config:set_metric_symbol({
  587. name = sym,
  588. score = 0.0,
  589. group = 'hfilter'
  590. })
  591. end
  592. else
  593. lua_util.disable_module(N, "config")
  594. end