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.

scan_result.c 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  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. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "mem_pool.h"
  18. #include "scan_result.h"
  19. #include "rspamd.h"
  20. #include "message.h"
  21. #include "lua/lua_common.h"
  22. #include "libserver/cfg_file_private.h"
  23. #include "libmime/scan_result_private.h"
  24. #include "contrib/fastutf8/fastutf8.h"
  25. #include <math.h>
  26. #include "contrib/uthash/utlist.h"
  27. #define msg_debug_metric(...) rspamd_conditional_debug_fast (NULL, NULL, \
  28. rspamd_metric_log_id, "metric", task->task_pool->tag.uid, \
  29. G_STRFUNC, \
  30. __VA_ARGS__)
  31. INIT_LOG_MODULE(metric)
  32. /* Average symbols count to optimize hash allocation */
  33. static struct rspamd_counter_data symbols_count;
  34. static void
  35. rspamd_scan_result_dtor (gpointer d)
  36. {
  37. struct rspamd_scan_result *r = (struct rspamd_scan_result *)d;
  38. struct rspamd_symbol_result sres;
  39. rspamd_set_counter_ema (&symbols_count, kh_size (r->symbols), 0.5);
  40. kh_foreach_value (r->symbols, sres, {
  41. if (sres.options) {
  42. kh_destroy (rspamd_options_hash, sres.options);
  43. }
  44. });
  45. kh_destroy (rspamd_symbols_hash, r->symbols);
  46. kh_destroy (rspamd_symbols_group_hash, r->sym_groups);
  47. }
  48. struct rspamd_scan_result *
  49. rspamd_create_metric_result (struct rspamd_task *task)
  50. {
  51. struct rspamd_scan_result *metric_res;
  52. guint i;
  53. metric_res = task->result;
  54. if (metric_res != NULL) {
  55. return metric_res;
  56. }
  57. metric_res = rspamd_mempool_alloc0 (task->task_pool,
  58. sizeof (struct rspamd_scan_result));
  59. metric_res->symbols = kh_init (rspamd_symbols_hash);
  60. metric_res->sym_groups = kh_init (rspamd_symbols_group_hash);
  61. /* Optimize allocation */
  62. kh_resize (rspamd_symbols_group_hash, metric_res->sym_groups, 4);
  63. if (symbols_count.mean > 4) {
  64. kh_resize (rspamd_symbols_hash, metric_res->symbols, symbols_count.mean);
  65. }
  66. else {
  67. kh_resize (rspamd_symbols_hash, metric_res->symbols, 4);
  68. }
  69. if (task->cfg) {
  70. struct rspamd_action *act, *tmp;
  71. metric_res->actions_limits = rspamd_mempool_alloc0 (task->task_pool,
  72. sizeof (struct rspamd_action_result) * HASH_COUNT (task->cfg->actions));
  73. i = 0;
  74. HASH_ITER (hh, task->cfg->actions, act, tmp) {
  75. if (!(act->flags & RSPAMD_ACTION_NO_THRESHOLD)) {
  76. metric_res->actions_limits[i].cur_limit = act->threshold;
  77. }
  78. metric_res->actions_limits[i].action = act;
  79. i ++;
  80. }
  81. metric_res->nactions = i;
  82. }
  83. rspamd_mempool_add_destructor (task->task_pool,
  84. rspamd_scan_result_dtor,
  85. metric_res);
  86. return metric_res;
  87. }
  88. static inline int
  89. rspamd_pr_sort (const struct rspamd_passthrough_result *pra,
  90. const struct rspamd_passthrough_result *prb)
  91. {
  92. return prb->priority - pra->priority;
  93. }
  94. void
  95. rspamd_add_passthrough_result (struct rspamd_task *task,
  96. struct rspamd_action *action,
  97. guint priority,
  98. double target_score,
  99. const gchar *message,
  100. const gchar *module,
  101. guint flags)
  102. {
  103. struct rspamd_scan_result *metric_res;
  104. struct rspamd_passthrough_result *pr;
  105. metric_res = task->result;
  106. pr = rspamd_mempool_alloc (task->task_pool, sizeof (*pr));
  107. pr->action = action;
  108. pr->priority = priority;
  109. pr->message = message;
  110. pr->module = module;
  111. pr->target_score = target_score;
  112. pr->flags = flags;
  113. DL_APPEND (metric_res->passthrough_result, pr);
  114. DL_SORT (metric_res->passthrough_result, rspamd_pr_sort);
  115. if (!isnan (target_score)) {
  116. msg_info_task ("<%s>: set pre-result to '%s' %s(%.2f): '%s' from %s(%d)",
  117. MESSAGE_FIELD_CHECK (task, message_id), action->name,
  118. flags & RSPAMD_PASSTHROUGH_LEAST ? "*least " : "",
  119. target_score,
  120. message, module, priority);
  121. }
  122. else {
  123. msg_info_task ("<%s>: set pre-result to '%s' %s(no score): '%s' from %s(%d)",
  124. MESSAGE_FIELD_CHECK (task, message_id), action->name,
  125. flags & RSPAMD_PASSTHROUGH_LEAST ? "*least " : "",
  126. message, module, priority);
  127. }
  128. }
  129. static inline gdouble
  130. rspamd_check_group_score (struct rspamd_task *task,
  131. const gchar *symbol,
  132. struct rspamd_symbols_group *gr,
  133. gdouble *group_score,
  134. gdouble w)
  135. {
  136. if (gr != NULL && group_score && gr->max_score > 0.0 && w > 0.0) {
  137. if (*group_score >= gr->max_score && w > 0) {
  138. msg_info_task ("maximum group score %.2f for group %s has been reached,"
  139. " ignoring symbol %s with weight %.2f", gr->max_score,
  140. gr->name, symbol, w);
  141. return NAN;
  142. }
  143. else if (*group_score + w > gr->max_score) {
  144. w = gr->max_score - *group_score;
  145. }
  146. }
  147. return w;
  148. }
  149. #ifndef DBL_EPSILON
  150. #define DBL_EPSILON 2.2204460492503131e-16
  151. #endif
  152. static struct rspamd_symbol_result *
  153. insert_metric_result (struct rspamd_task *task,
  154. const gchar *symbol,
  155. double weight,
  156. const gchar *opt,
  157. enum rspamd_symbol_insert_flags flags)
  158. {
  159. struct rspamd_scan_result *metric_res;
  160. struct rspamd_symbol_result *s = NULL;
  161. gdouble final_score, *gr_score = NULL, next_gf = 1.0, diff;
  162. struct rspamd_symbol *sdef;
  163. struct rspamd_symbols_group *gr = NULL;
  164. const ucl_object_t *mobj, *sobj;
  165. gint max_shots, ret;
  166. guint i;
  167. khiter_t k;
  168. gboolean single = !!(flags & RSPAMD_SYMBOL_INSERT_SINGLE);
  169. gchar *sym_cpy;
  170. metric_res = task->result;
  171. if (!isfinite (weight)) {
  172. msg_warn_task ("detected %s score for symbol %s, replace it with zero",
  173. isnan (weight) ? "NaN" : "infinity", symbol);
  174. weight = 0.0;
  175. }
  176. msg_debug_metric ("want to insert symbol %s, initial weight %.2f",
  177. symbol, weight);
  178. sdef = g_hash_table_lookup (task->cfg->symbols, symbol);
  179. if (sdef == NULL) {
  180. if (flags & RSPAMD_SYMBOL_INSERT_ENFORCE) {
  181. final_score = 1.0 * weight; /* Enforce static weight to 1.0 */
  182. }
  183. else {
  184. final_score = 0.0;
  185. }
  186. msg_debug_metric ("no symbol definition for %s; final multiplier %.2f",
  187. symbol, final_score);
  188. }
  189. else {
  190. if (sdef->cache_item) {
  191. /* Check if we can insert this symbol at all */
  192. if (!rspamd_symcache_is_item_allowed (task, sdef->cache_item, FALSE)) {
  193. msg_debug_metric ("symbol %s is not allowed to be inserted due to settings",
  194. symbol);
  195. return NULL;
  196. }
  197. }
  198. final_score = (*sdef->weight_ptr) * weight;
  199. PTR_ARRAY_FOREACH (sdef->groups, i, gr) {
  200. k = kh_get (rspamd_symbols_group_hash, metric_res->sym_groups, gr);
  201. if (k == kh_end (metric_res->sym_groups)) {
  202. k = kh_put (rspamd_symbols_group_hash, metric_res->sym_groups,
  203. gr, &ret);
  204. kh_value (metric_res->sym_groups, k) = 0;
  205. }
  206. }
  207. msg_debug_metric ("metric multiplier for %s is %.2f",
  208. symbol, *sdef->weight_ptr);
  209. }
  210. if (task->settings) {
  211. gdouble corr;
  212. mobj = ucl_object_lookup (task->settings, "scores");
  213. if (!mobj) {
  214. /* Legacy */
  215. mobj = task->settings;
  216. }
  217. else {
  218. msg_debug_metric ("found scores in the settings");
  219. }
  220. sobj = ucl_object_lookup (mobj, symbol);
  221. if (sobj != NULL && ucl_object_todouble_safe (sobj, &corr)) {
  222. msg_debug_metric ("settings: changed weight of symbol %s from %.2f "
  223. "to %.2f * %.2f",
  224. symbol, final_score, corr, weight);
  225. final_score = corr * weight;
  226. }
  227. }
  228. k = kh_get (rspamd_symbols_hash, metric_res->symbols, symbol);
  229. if (k != kh_end (metric_res->symbols)) {
  230. /* Existing metric score */
  231. s = &kh_value (metric_res->symbols, k);
  232. if (single) {
  233. max_shots = 1;
  234. }
  235. else {
  236. if (sdef) {
  237. max_shots = sdef->nshots;
  238. }
  239. else {
  240. max_shots = task->cfg->default_max_shots;
  241. }
  242. }
  243. msg_debug_metric ("nshots: %d for symbol %s", max_shots, symbol);
  244. if (!single && (max_shots > 0 && (s->nshots >= max_shots))) {
  245. single = TRUE;
  246. }
  247. s->nshots ++;
  248. if (opt) {
  249. rspamd_task_add_result_option (task, s, opt, strlen (opt));
  250. }
  251. /* Adjust diff */
  252. if (!single) {
  253. diff = final_score;
  254. msg_debug_metric ("symbol %s can be inserted multiple times: %.2f weight",
  255. symbol, diff);
  256. }
  257. else {
  258. if (fabs (s->score) < fabs (final_score) &&
  259. signbit (s->score) == signbit (final_score)) {
  260. /* Replace less significant weight with a more significant one */
  261. diff = final_score - s->score;
  262. msg_debug_metric ("symbol %s can be inserted single time;"
  263. " weight adjusted %.2f + %.2f",
  264. symbol, s->score, diff);
  265. }
  266. else {
  267. diff = 0;
  268. }
  269. }
  270. if (diff) {
  271. /* Handle grow factor */
  272. if (metric_res->grow_factor && diff > 0) {
  273. diff *= metric_res->grow_factor;
  274. next_gf *= task->cfg->grow_factor;
  275. }
  276. else if (diff > 0) {
  277. next_gf = task->cfg->grow_factor;
  278. }
  279. msg_debug_metric ("adjust grow factor to %.2f for symbol %s (%.2f final)",
  280. next_gf, symbol, diff);
  281. if (sdef) {
  282. PTR_ARRAY_FOREACH (sdef->groups, i, gr) {
  283. gdouble cur_diff;
  284. k = kh_get (rspamd_symbols_group_hash,
  285. metric_res->sym_groups, gr);
  286. g_assert (k != kh_end (metric_res->sym_groups));
  287. gr_score = &kh_value (metric_res->sym_groups, k);
  288. cur_diff = rspamd_check_group_score (task, symbol, gr,
  289. gr_score, diff);
  290. if (isnan (cur_diff)) {
  291. /* Limit reached, do not add result */
  292. msg_debug_metric (
  293. "group limit %.2f is reached for %s when inserting symbol %s;"
  294. " drop score %.2f",
  295. *gr_score, gr->name, symbol, diff);
  296. diff = NAN;
  297. break;
  298. } else if (gr_score) {
  299. *gr_score += cur_diff;
  300. if (cur_diff < diff) {
  301. /* Reduce */
  302. msg_debug_metric (
  303. "group limit %.2f is reached for %s when inserting symbol %s;"
  304. " reduce score %.2f - %.2f",
  305. *gr_score, gr->name, symbol, diff, cur_diff);
  306. diff = cur_diff;
  307. }
  308. }
  309. }
  310. }
  311. if (!isnan (diff)) {
  312. metric_res->score += diff;
  313. metric_res->grow_factor = next_gf;
  314. if (single) {
  315. msg_debug_metric ("final score for single symbol %s = %.2f; %.2f diff",
  316. symbol, final_score, diff);
  317. s->score = final_score;
  318. } else {
  319. msg_debug_metric ("increase final score for multiple symbol %s += %.2f = %.2f",
  320. symbol, s->score, diff);
  321. s->score += diff;
  322. }
  323. }
  324. }
  325. }
  326. else {
  327. /* New result */
  328. sym_cpy = rspamd_mempool_strdup (task->task_pool, symbol);
  329. k = kh_put (rspamd_symbols_hash, metric_res->symbols,
  330. sym_cpy, &ret);
  331. g_assert (ret > 0);
  332. s = &kh_value (metric_res->symbols, k);
  333. memset (s, 0, sizeof (*s));
  334. /* Handle grow factor */
  335. if (metric_res->grow_factor && final_score > 0) {
  336. final_score *= metric_res->grow_factor;
  337. next_gf *= task->cfg->grow_factor;
  338. }
  339. else if (final_score > 0) {
  340. next_gf = task->cfg->grow_factor;
  341. }
  342. msg_debug_metric ("adjust grow factor to %.2f for symbol %s (%.2f final)",
  343. next_gf, symbol, final_score);
  344. s->name = sym_cpy;
  345. s->sym = sdef;
  346. s->nshots = 1;
  347. if (sdef) {
  348. /* Check group limits */
  349. PTR_ARRAY_FOREACH (sdef->groups, i, gr) {
  350. gdouble cur_score;
  351. k = kh_get (rspamd_symbols_group_hash, metric_res->sym_groups, gr);
  352. g_assert (k != kh_end (metric_res->sym_groups));
  353. gr_score = &kh_value (metric_res->sym_groups, k);
  354. cur_score = rspamd_check_group_score (task, symbol, gr,
  355. gr_score, final_score);
  356. if (isnan (cur_score)) {
  357. /* Limit reached, do not add result */
  358. msg_debug_metric (
  359. "group limit %.2f is reached for %s when inserting symbol %s;"
  360. " drop score %.2f",
  361. *gr_score, gr->name, symbol, final_score);
  362. final_score = NAN;
  363. break;
  364. } else if (gr_score) {
  365. *gr_score += cur_score;
  366. if (cur_score < final_score) {
  367. /* Reduce */
  368. msg_debug_metric (
  369. "group limit %.2f is reached for %s when inserting symbol %s;"
  370. " reduce score %.2f - %.2f",
  371. *gr_score, gr->name, symbol, final_score, cur_score);
  372. final_score = cur_score;
  373. }
  374. }
  375. }
  376. }
  377. if (!isnan (final_score)) {
  378. const double epsilon = DBL_EPSILON;
  379. metric_res->score += final_score;
  380. metric_res->grow_factor = next_gf;
  381. s->score = final_score;
  382. if (final_score > epsilon) {
  383. metric_res->npositive ++;
  384. metric_res->positive_score += final_score;
  385. }
  386. else if (final_score < -epsilon) {
  387. metric_res->nnegative ++;
  388. metric_res->negative_score += fabs (final_score);
  389. }
  390. }
  391. else {
  392. s->score = 0;
  393. }
  394. if (opt) {
  395. rspamd_task_add_result_option (task, s, opt, strlen (opt));
  396. }
  397. }
  398. msg_debug_metric ("final insertion for symbol %s, score %.2f, factor: %f",
  399. symbol,
  400. s->score,
  401. final_score);
  402. return s;
  403. }
  404. struct rspamd_symbol_result *
  405. rspamd_task_insert_result_full (struct rspamd_task *task,
  406. const gchar *symbol,
  407. double weight,
  408. const gchar *opt,
  409. enum rspamd_symbol_insert_flags flags)
  410. {
  411. struct rspamd_symbol_result *s = NULL;
  412. if (task->processed_stages & (RSPAMD_TASK_STAGE_IDEMPOTENT >> 1)) {
  413. msg_err_task ("cannot insert symbol %s on idempotent phase",
  414. symbol);
  415. return NULL;
  416. }
  417. /* Insert symbol to default metric */
  418. s = insert_metric_result (task,
  419. symbol,
  420. weight,
  421. opt,
  422. flags);
  423. /* Process cache item */
  424. if (s && task->cfg->cache && s->sym) {
  425. rspamd_symcache_inc_frequency (task->cfg->cache, s->sym->cache_item);
  426. }
  427. return s;
  428. }
  429. static gchar *
  430. rspamd_task_option_safe_copy (struct rspamd_task *task,
  431. const gchar *val,
  432. gsize vlen,
  433. gsize *outlen)
  434. {
  435. const gchar *p, *end;
  436. p = val;
  437. end = val + vlen;
  438. vlen = 0; /* Reuse */
  439. while (p < end) {
  440. if (*p & 0x80) {
  441. UChar32 uc;
  442. gint off = 0;
  443. U8_NEXT (p, off, end - p, uc);
  444. if (uc > 0) {
  445. if (u_isprint (uc)) {
  446. vlen += off;
  447. }
  448. else {
  449. /* We will replace it with 0xFFFD */
  450. vlen += MAX (off, 3);
  451. }
  452. }
  453. else {
  454. vlen += MAX (off, 3);
  455. }
  456. p += off;
  457. }
  458. else if (!g_ascii_isprint (*p)) {
  459. /* Another 0xFFFD */
  460. vlen += 3;
  461. p ++;
  462. }
  463. else {
  464. p ++;
  465. vlen ++;
  466. }
  467. }
  468. gchar *dest, *d;
  469. dest = rspamd_mempool_alloc (task->task_pool, vlen + 1);
  470. d = dest;
  471. p = val;
  472. while (p < end) {
  473. if (*p & 0x80) {
  474. UChar32 uc;
  475. gint off = 0;
  476. U8_NEXT (p, off, end - p, uc);
  477. if (uc > 0) {
  478. if (u_isprint (uc)) {
  479. memcpy (d, p, off);
  480. d += off;
  481. }
  482. else {
  483. /* We will replace it with 0xFFFD */
  484. *d++ = '\357';
  485. *d++ = '\277';
  486. *d++ = '\275';
  487. }
  488. }
  489. else {
  490. *d++ = '\357';
  491. *d++ = '\277';
  492. *d++ = '\275';
  493. }
  494. p += off;
  495. }
  496. else if (!g_ascii_isprint (*p)) {
  497. /* Another 0xFFFD */
  498. *d++ = '\357';
  499. *d++ = '\277';
  500. *d++ = '\275';
  501. p ++;
  502. }
  503. else {
  504. *d++ = *p++;
  505. }
  506. }
  507. *d = '\0';
  508. *(outlen) = d - dest;
  509. return dest;
  510. }
  511. gboolean
  512. rspamd_task_add_result_option (struct rspamd_task *task,
  513. struct rspamd_symbol_result *s,
  514. const gchar *val,
  515. gsize vlen)
  516. {
  517. struct rspamd_symbol_option *opt, srch;
  518. gboolean ret = FALSE;
  519. gchar *opt_cpy = NULL;
  520. gsize cpy_len;
  521. khiter_t k;
  522. gint r;
  523. if (s && val) {
  524. if (s->opts_len < 0) {
  525. /* Cannot add more options, give up */
  526. msg_debug_task ("cannot add more options to symbol %s when adding option %s",
  527. s->name, val);
  528. return FALSE;
  529. }
  530. if (!s->options) {
  531. s->options = kh_init (rspamd_options_hash);
  532. }
  533. if (vlen + s->opts_len > task->cfg->max_opts_len) {
  534. /* Add truncated option */
  535. msg_info_task ("cannot add more options to symbol %s when adding option %s",
  536. s->name, val);
  537. val = "...";
  538. vlen = 3;
  539. s->opts_len = -1;
  540. }
  541. if (!(s->sym && (s->sym->flags & RSPAMD_SYMBOL_FLAG_ONEPARAM)) &&
  542. kh_size (s->options) < task->cfg->default_max_shots) {
  543. opt_cpy = rspamd_task_option_safe_copy (task, val, vlen, &cpy_len);
  544. /* Append new options */
  545. srch.option = (gchar *)opt_cpy;
  546. srch.optlen = cpy_len;
  547. k = kh_get (rspamd_options_hash, s->options, &srch);
  548. if (k == kh_end (s->options)) {
  549. opt = rspamd_mempool_alloc0 (task->task_pool, sizeof (*opt));
  550. opt->optlen = cpy_len;
  551. opt->option = opt_cpy;
  552. kh_put (rspamd_options_hash, s->options, opt, &r);
  553. DL_APPEND (s->opts_head, opt);
  554. ret = TRUE;
  555. }
  556. }
  557. else {
  558. /* Skip addition */
  559. ret = FALSE;
  560. }
  561. if (ret && s->opts_len >= 0) {
  562. s->opts_len += vlen;
  563. }
  564. }
  565. else if (!val) {
  566. ret = TRUE;
  567. }
  568. return ret;
  569. }
  570. struct rspamd_action*
  571. rspamd_check_action_metric (struct rspamd_task *task)
  572. {
  573. struct rspamd_action_result *action_lim,
  574. *noaction = NULL;
  575. struct rspamd_action *selected_action = NULL, *least_action = NULL;
  576. struct rspamd_passthrough_result *pr;
  577. double max_score = -(G_MAXDOUBLE), sc;
  578. int i;
  579. struct rspamd_scan_result *mres = task->result;
  580. gboolean seen_least = FALSE;
  581. if (mres->passthrough_result != NULL) {
  582. DL_FOREACH (mres->passthrough_result, pr) {
  583. if (!seen_least || !(pr->flags & RSPAMD_PASSTHROUGH_LEAST)) {
  584. sc = pr->target_score;
  585. selected_action = pr->action;
  586. if (!(pr->flags & RSPAMD_PASSTHROUGH_LEAST)) {
  587. if (!isnan (sc)) {
  588. if (pr->action->action_type == METRIC_ACTION_NOACTION) {
  589. mres->score = MIN (sc, mres->score);
  590. }
  591. else {
  592. mres->score = sc;
  593. }
  594. }
  595. return selected_action;
  596. }
  597. else {
  598. seen_least = true;
  599. least_action = selected_action;
  600. if (isnan (sc)) {
  601. if (selected_action->flags & RSPAMD_ACTION_NO_THRESHOLD) {
  602. /*
  603. * In this case, we have a passthrough action that
  604. * is `least` action, however, there is no threshold
  605. * on it.
  606. *
  607. * Hence, we imply the following logic:
  608. *
  609. * - we leave score unchanged
  610. * - we apply passthrough no threshold action unless
  611. * score based action *is not* reject, otherwise
  612. * we apply reject action
  613. */
  614. }
  615. else {
  616. sc = selected_action->threshold;
  617. max_score = sc;
  618. }
  619. }
  620. else {
  621. max_score = sc;
  622. }
  623. }
  624. }
  625. }
  626. }
  627. /* We are not certain about the results during processing */
  628. /*
  629. * Select result by score
  630. */
  631. for (i = mres->nactions - 1; i >= 0; i--) {
  632. action_lim = &mres->actions_limits[i];
  633. sc = action_lim->cur_limit;
  634. if (action_lim->action->action_type == METRIC_ACTION_NOACTION) {
  635. noaction = action_lim;
  636. }
  637. if (isnan (sc) ||
  638. (action_lim->action->flags & (RSPAMD_ACTION_NO_THRESHOLD|RSPAMD_ACTION_HAM))) {
  639. continue;
  640. }
  641. if (mres->score >= sc && sc > max_score) {
  642. selected_action = action_lim->action;
  643. max_score = sc;
  644. }
  645. }
  646. if (selected_action == NULL) {
  647. selected_action = noaction->action;
  648. }
  649. if (selected_action) {
  650. if (seen_least) {
  651. if (least_action->flags & RSPAMD_ACTION_NO_THRESHOLD) {
  652. if (selected_action->action_type != METRIC_ACTION_REJECT &&
  653. selected_action->action_type != METRIC_ACTION_DISCARD) {
  654. /* Override score based action with least action */
  655. selected_action = least_action;
  656. }
  657. }
  658. else {
  659. /* Adjust score if needed */
  660. mres->score = MAX (max_score, mres->score);
  661. }
  662. }
  663. return selected_action;
  664. }
  665. return noaction->action;
  666. }
  667. struct rspamd_symbol_result*
  668. rspamd_task_find_symbol_result (struct rspamd_task *task, const char *sym)
  669. {
  670. struct rspamd_symbol_result *res = NULL;
  671. khiter_t k;
  672. if (task->result) {
  673. k = kh_get (rspamd_symbols_hash, task->result->symbols, sym);
  674. if (k != kh_end (task->result->symbols)) {
  675. res = &kh_value (task->result->symbols, k);
  676. }
  677. }
  678. return res;
  679. }
  680. void
  681. rspamd_task_symbol_result_foreach (struct rspamd_task *task,
  682. GHFunc func,
  683. gpointer ud)
  684. {
  685. const gchar *kk;
  686. struct rspamd_symbol_result res;
  687. if (func && task->result) {
  688. kh_foreach (task->result->symbols, kk, res, {
  689. func ((gpointer)kk, (gpointer)&res, ud);
  690. });
  691. }
  692. }