1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126 |
- /*
- * Copyright 2024 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.
- */
- #include "config.h"
- #include "mem_pool.h"
- #include "scan_result.h"
- #include "rspamd.h"
- #include "message.h"
- #include "lua/lua_common.h"
- #include "libserver/cfg_file_private.h"
- #include "libmime/scan_result_private.h"
- #include "contrib/fastutf8/fastutf8.h"
- #include <math.h>
- #include "contrib/uthash/utlist.h"
-
- #define msg_debug_metric(...) rspamd_conditional_debug_fast(NULL, NULL, \
- rspamd_metric_log_id, "metric", task->task_pool->tag.uid, \
- RSPAMD_LOG_FUNC, \
- __VA_ARGS__)
-
- INIT_LOG_MODULE(metric)
-
- /* Average symbols count to optimize hash allocation */
- static struct rspamd_counter_data symbols_count;
-
- static void
- rspamd_scan_result_dtor(gpointer d)
- {
- struct rspamd_scan_result *r = (struct rspamd_scan_result *) d;
- struct rspamd_symbol_result *sres;
-
- rspamd_set_counter_ema(&symbols_count, kh_size(r->symbols), 0.5);
-
- if (r->symbol_cbref != -1) {
- luaL_unref(r->task->cfg->lua_state, LUA_REGISTRYINDEX, r->symbol_cbref);
- }
-
- kh_foreach_value(r->symbols, sres, {
- if (sres->options) {
- kh_destroy(rspamd_options_hash, sres->options);
- }
- });
-
- kh_destroy(rspamd_symbols_hash, r->symbols);
- kh_destroy(rspamd_symbols_group_hash, r->sym_groups);
- }
-
- static void
- rspamd_metric_actions_foreach_cb(int i, struct rspamd_action *act, void *cbd)
- {
- struct rspamd_scan_result *metric_res = (struct rspamd_scan_result *) cbd;
- metric_res->actions_config[i].flags = RSPAMD_ACTION_RESULT_DEFAULT;
- if (!(act->flags & RSPAMD_ACTION_NO_THRESHOLD)) {
- metric_res->actions_config[i].cur_limit = act->threshold;
- }
- else {
- metric_res->actions_config[i].flags |= RSPAMD_ACTION_RESULT_NO_THRESHOLD;
- }
- metric_res->actions_config[i].action = act;
- }
-
- struct rspamd_scan_result *
- rspamd_create_metric_result(struct rspamd_task *task,
- const char *name, int lua_sym_cbref)
- {
- struct rspamd_scan_result *metric_res;
-
- metric_res = rspamd_mempool_alloc0(task->task_pool,
- sizeof(struct rspamd_scan_result));
- metric_res->symbols = kh_init(rspamd_symbols_hash);
- metric_res->sym_groups = kh_init(rspamd_symbols_group_hash);
-
- if (name) {
- metric_res->name = rspamd_mempool_strdup(task->task_pool, name);
- }
- else {
- metric_res->name = NULL;
- }
-
- metric_res->symbol_cbref = lua_sym_cbref;
- metric_res->task = task;
-
- /* Optimize allocation */
- kh_resize(rspamd_symbols_group_hash, metric_res->sym_groups, 4);
-
- if (symbols_count.mean > 4) {
- kh_resize(rspamd_symbols_hash, metric_res->symbols, symbols_count.mean);
- }
- else {
- kh_resize(rspamd_symbols_hash, metric_res->symbols, 4);
- }
-
- if (task->cfg) {
- size_t nact = rspamd_config_actions_size(task->cfg);
- metric_res->actions_config = rspamd_mempool_alloc0(task->task_pool,
- sizeof(struct rspamd_action_config) * nact);
- rspamd_config_actions_foreach_enumerate(task->cfg, rspamd_metric_actions_foreach_cb, metric_res);
- metric_res->nactions = nact;
- }
-
- rspamd_mempool_add_destructor(task->task_pool,
- rspamd_scan_result_dtor,
- metric_res);
- DL_APPEND(task->result, metric_res);
-
- return metric_res;
- }
-
- static inline int
- rspamd_pr_sort(const struct rspamd_passthrough_result *pra,
- const struct rspamd_passthrough_result *prb)
- {
- return prb->priority - pra->priority;
- }
-
- bool rspamd_add_passthrough_result(struct rspamd_task *task,
- struct rspamd_action *action,
- unsigned int priority,
- double target_score,
- const char *message,
- const char *module,
- uint flags,
- struct rspamd_scan_result *scan_result)
- {
- struct rspamd_passthrough_result *pr;
-
- if (scan_result == NULL) {
- scan_result = task->result;
- }
-
- /* Find the specific action config */
- struct rspamd_action_config *action_config = NULL;
-
- for (unsigned int i = 0; i < scan_result->nactions; i++) {
- struct rspamd_action_config *cur = &scan_result->actions_config[i];
-
- /* We assume that all action pointers are static */
- if (cur->action == action) {
- action_config = cur;
- break;
- }
- }
-
- if (action_config && (action_config->flags & RSPAMD_ACTION_RESULT_DISABLED)) {
- msg_info_task("<%s>: NOT set pre-result to '%s' %s(%.2f): '%s' from %s(%d); action is disabled",
- MESSAGE_FIELD_CHECK(task, message_id), action->name,
- flags & RSPAMD_PASSTHROUGH_LEAST ? "*least " : "",
- target_score,
- message, module, priority);
-
- return false;
- }
-
- pr = rspamd_mempool_alloc(task->task_pool, sizeof(*pr));
- pr->action = action;
- pr->priority = priority;
- pr->message = message;
- pr->module = module;
- pr->target_score = target_score;
- pr->flags = flags;
-
- DL_APPEND(scan_result->passthrough_result, pr);
- DL_SORT(scan_result->passthrough_result, rspamd_pr_sort);
-
- if (!isnan(target_score)) {
-
- msg_info_task("<%s>: set pre-result to '%s' %s(%.2f): '%s' from %s(%d)",
- MESSAGE_FIELD_CHECK(task, message_id), action->name,
- flags & RSPAMD_PASSTHROUGH_LEAST ? "*least " : "",
- target_score,
- message, module, priority);
- }
- else {
- msg_info_task("<%s>: set pre-result to '%s' %s(no score): '%s' from %s(%d)",
- MESSAGE_FIELD_CHECK(task, message_id), action->name,
- flags & RSPAMD_PASSTHROUGH_LEAST ? "*least " : "",
- message, module, priority);
- }
-
- scan_result->nresults++;
-
- return true;
- }
-
- static inline double
- rspamd_check_group_score(struct rspamd_task *task,
- const char *symbol,
- struct rspamd_symbols_group *gr,
- double *group_score,
- double w)
- {
- if (gr != NULL && group_score && gr->max_score > 0.0 && w > 0.0) {
- if (*group_score >= gr->max_score && w > 0) {
- msg_info_task("maximum group score %.2f for group %s has been reached,"
- " ignoring symbol %s with weight %.2f",
- gr->max_score,
- gr->name, symbol, w);
- return NAN;
- }
- else if (*group_score + w > gr->max_score) {
- w = gr->max_score - *group_score;
- }
- }
-
- return w;
- }
-
- #ifndef DBL_EPSILON
- #define DBL_EPSILON 2.2204460492503131e-16
- #endif
-
- static struct rspamd_symbol_result *
- insert_metric_result(struct rspamd_task *task,
- const char *symbol,
- double weight,
- const char *opt,
- struct rspamd_scan_result *metric_res,
- enum rspamd_symbol_insert_flags flags,
- bool *new_sym)
- {
- struct rspamd_symbol_result *symbol_result = NULL;
- double final_score, *gr_score = NULL, diff;
- struct rspamd_symbol *sdef;
- struct rspamd_symbols_group *gr = NULL;
- const ucl_object_t *mobj, *sobj;
- int max_shots = G_MAXINT, ret;
- unsigned int i;
- khiter_t k;
- gboolean single = !!(flags & RSPAMD_SYMBOL_INSERT_SINGLE);
- char *sym_cpy;
-
- if (!isfinite(weight)) {
- msg_warn_task("detected %s score for symbol %s, replace it with zero",
- isnan(weight) ? "NaN" : "infinity", symbol);
- weight = 0.0;
- }
-
- msg_debug_metric("want to insert symbol %s, initial weight %.2f",
- symbol, weight);
-
- sdef = g_hash_table_lookup(task->cfg->symbols, symbol);
- if (sdef == NULL) {
- if (flags & RSPAMD_SYMBOL_INSERT_ENFORCE) {
- final_score = 1.0 * weight; /* Enforce static weight to 1.0 */
- }
- else {
- final_score = 0.0;
- }
-
- msg_debug_metric("no symbol definition for %s; final multiplier %.2f",
- symbol, final_score);
- }
- else {
- if (sdef->cache_item) {
- /* Check if we can insert this symbol at all */
- if (!rspamd_symcache_is_item_allowed(task, sdef->cache_item, FALSE)) {
- msg_debug_metric("symbol %s is not allowed to be inserted due to settings",
- symbol);
- return NULL;
- }
- }
-
- final_score = (*sdef->weight_ptr) * weight;
-
- PTR_ARRAY_FOREACH(sdef->groups, i, gr)
- {
- k = kh_get(rspamd_symbols_group_hash, metric_res->sym_groups, gr);
-
- if (k == kh_end(metric_res->sym_groups)) {
- k = kh_put(rspamd_symbols_group_hash, metric_res->sym_groups,
- gr, &ret);
- kh_value(metric_res->sym_groups, k) = 0;
- }
- }
-
- msg_debug_metric("metric multiplier for %s is %.2f",
- symbol, *sdef->weight_ptr);
- }
-
- if (task->settings) {
- double corr;
- mobj = ucl_object_lookup(task->settings, "scores");
-
- if (!mobj) {
- /* Legacy */
- mobj = task->settings;
- }
- else {
- msg_debug_metric("found scores in the settings");
- }
-
- sobj = ucl_object_lookup(mobj, symbol);
- if (sobj != NULL && ucl_object_todouble_safe(sobj, &corr)) {
- msg_debug_metric("settings: changed weight of symbol %s from %.2f "
- "to %.2f * %.2f",
- symbol, final_score, corr, weight);
- final_score = corr * weight;
- }
- }
-
- k = kh_get(rspamd_symbols_hash, metric_res->symbols, symbol);
- if (k != kh_end(metric_res->symbols)) {
- /* Existing metric score */
- symbol_result = kh_value(metric_res->symbols, k);
- if (single) {
- max_shots = 1;
- }
- else {
- if (sdef) {
- if (sdef->groups) {
- PTR_ARRAY_FOREACH(sdef->groups, i, gr)
- {
- if (gr->flags & RSPAMD_SYMBOL_GROUP_ONE_SHOT) {
- max_shots = 1;
- }
- }
- }
-
- max_shots = MIN(max_shots, sdef->nshots);
- }
- else {
- max_shots = task->cfg->default_max_shots;
- }
- }
-
- msg_debug_metric("nshots: %d for symbol %s", max_shots, symbol);
-
- if (!single && (max_shots > 0 && (symbol_result->nshots >= max_shots))) {
- single = TRUE;
- }
-
- symbol_result->nshots++;
-
- if (opt) {
- rspamd_task_add_result_option(task, symbol_result, opt, strlen(opt));
- }
-
- /* Adjust diff */
- if (!single) {
- diff = final_score;
- msg_debug_metric("symbol %s can be inserted multiple times: %.2f weight",
- symbol, diff);
- }
- else {
- if (fabs(symbol_result->score) < fabs(final_score) &&
- signbit(symbol_result->score) == signbit(final_score)) {
- /* Replace less significant weight with a more significant one */
- diff = final_score - symbol_result->score;
- msg_debug_metric("symbol %s can be inserted single time;"
- " weight adjusted %.2f + %.2f",
- symbol, symbol_result->score, diff);
- }
- else {
- diff = 0;
- }
- }
-
- if (diff) {
-
- if (sdef) {
- PTR_ARRAY_FOREACH(sdef->groups, i, gr)
- {
- double cur_diff;
-
- k = kh_get(rspamd_symbols_group_hash,
- metric_res->sym_groups, gr);
- g_assert(k != kh_end(metric_res->sym_groups));
- gr_score = &kh_value(metric_res->sym_groups, k);
- cur_diff = rspamd_check_group_score(task, symbol, gr,
- gr_score, diff);
-
- if (isnan(cur_diff)) {
- /* Limit reached, do not add result */
- msg_debug_metric(
- "group limit %.2f is reached for %s when inserting symbol %s;"
- " drop score %.2f",
- *gr_score, gr->name, symbol, diff);
-
- diff = NAN;
- break;
- }
- else if (gr_score) {
- *gr_score += cur_diff;
-
- if (cur_diff < diff) {
- /* Reduce */
- msg_debug_metric(
- "group limit %.2f is reached for %s when inserting symbol %s;"
- " reduce score %.2f - %.2f",
- *gr_score, gr->name, symbol, diff, cur_diff);
- diff = cur_diff;
- }
- }
- }
- }
-
- if (!isnan(diff)) {
-
- if (single) {
- msg_debug_metric("final score for single symbol %s = %.2f; %.2f diff",
- symbol, final_score, diff);
- symbol_result->score = final_score;
- }
- else {
- msg_debug_metric("increase final score for multiple symbol %s += %.2f = %.2f",
- symbol, symbol_result->score, diff);
- symbol_result->score += diff;
- }
- }
- }
- }
- else {
- /* New result */
- if (new_sym) {
- *new_sym = true;
- }
-
- sym_cpy = rspamd_mempool_strdup(task->task_pool, symbol);
- k = kh_put(rspamd_symbols_hash, metric_res->symbols,
- sym_cpy, &ret);
- g_assert(ret > 0);
- symbol_result = rspamd_mempool_alloc0(task->task_pool, sizeof(*symbol_result));
- kh_value(metric_res->symbols, k) = symbol_result;
-
- symbol_result->name = sym_cpy;
- symbol_result->sym = sdef;
- symbol_result->nshots = 1;
-
- if (sdef) {
- /* Check group limits */
- PTR_ARRAY_FOREACH(sdef->groups, i, gr)
- {
- double cur_score;
-
- k = kh_get(rspamd_symbols_group_hash, metric_res->sym_groups, gr);
- g_assert(k != kh_end(metric_res->sym_groups));
- gr_score = &kh_value(metric_res->sym_groups, k);
- cur_score = rspamd_check_group_score(task, symbol, gr,
- gr_score, final_score);
-
- if (isnan(cur_score)) {
- /* Limit reached, do not add result */
- msg_debug_metric(
- "group limit %.2f is reached for %s when inserting symbol %s;"
- " drop score %.2f",
- *gr_score, gr->name, symbol, final_score);
- final_score = NAN;
- break;
- }
- else if (gr_score) {
- *gr_score += cur_score;
-
- if (cur_score < final_score) {
- /* Reduce */
- msg_debug_metric(
- "group limit %.2f is reached for %s when inserting symbol %s;"
- " reduce score %.2f - %.2f",
- *gr_score, gr->name, symbol, final_score, cur_score);
- final_score = cur_score;
- }
- }
- }
- }
-
- if (!isnan(final_score)) {
- const double epsilon = DBL_EPSILON;
-
- metric_res->score += final_score;
- symbol_result->score = final_score;
-
- if (final_score > epsilon) {
- metric_res->npositive++;
- metric_res->positive_score += final_score;
- }
- else if (final_score < -epsilon) {
- metric_res->nnegative++;
- metric_res->negative_score += fabs(final_score);
- }
- }
- else {
- symbol_result->score = 0;
- }
-
- if (opt) {
- rspamd_task_add_result_option(task, symbol_result, opt, strlen(opt));
- }
- }
-
- msg_debug_metric("final insertion for symbol %s, score %.2f, factor: %f",
- symbol,
- symbol_result->score,
- final_score);
- metric_res->nresults++;
-
- return symbol_result;
- }
-
- struct rspamd_symbol_result *
- rspamd_task_insert_result_full(struct rspamd_task *task,
- const char *symbol,
- double weight,
- const char *opt,
- enum rspamd_symbol_insert_flags flags,
- struct rspamd_scan_result *result)
- {
- struct rspamd_symbol_result *symbol_result = NULL, *ret = NULL;
- struct rspamd_scan_result *mres;
-
- /*
- * We allow symbols to be inserted for skipped tasks, as it might be a
- * race condition before some symbol is finished and skip flag being set.
- */
- if (!RSPAMD_TASK_IS_SKIPPED(task) && (task->processed_stages & (RSPAMD_TASK_STAGE_IDEMPOTENT >> 1))) {
- msg_err_task("cannot insert symbol %s on idempotent phase",
- symbol);
-
- return NULL;
- }
-
- if (result == NULL) {
- /* Insert everywhere */
- DL_FOREACH(task->result, mres)
- {
- if (mres->symbol_cbref != -1) {
- /* Check if we can insert this symbol to this symbol result */
- GError *err = NULL;
- lua_State *L = (lua_State *) task->cfg->lua_state;
-
- if (!rspamd_lua_universal_pcall(L, mres->symbol_cbref,
- G_STRLOC, 1, "uss", &err,
- rspamd_task_classname, task, symbol, mres->name ? mres->name : "default")) {
- msg_warn_task("cannot call for symbol_cbref for result %s: %e",
- mres->name ? mres->name : "default", err);
- g_error_free(err);
-
- continue;
- }
- else {
- if (!lua_toboolean(L, -1)) {
- /* Skip symbol */
- msg_debug_metric("skip symbol %s for result %s due to Lua return value",
- symbol, mres->name);
- lua_pop(L, 1); /* Remove result */
-
- continue;
- }
-
- lua_pop(L, 1); /* Remove result */
- }
- }
-
- bool new_symbol = false;
-
- symbol_result = insert_metric_result(task,
- symbol,
- weight,
- opt,
- mres,
- flags,
- &new_symbol);
-
- if (mres->name == NULL) {
- /* Default result */
- ret = symbol_result;
-
- /* Process cache item */
- if (symbol_result && task->cfg->cache && symbol_result->sym && symbol_result->nshots == 1) {
- rspamd_symcache_inc_frequency(task->cfg->cache,
- symbol_result->sym->cache_item,
- symbol_result->sym->name);
- }
- }
- else if (new_symbol) {
- /* O(N) but we normally don't have any shadow results */
- LL_APPEND(ret, symbol_result);
- }
- }
- }
- else {
- /* Specific insertion */
- symbol_result = insert_metric_result(task,
- symbol,
- weight,
- opt,
- result,
- flags,
- NULL);
- ret = symbol_result;
-
- if (result->name == NULL) {
- /* Process cache item */
- if (symbol_result && task->cfg->cache && symbol_result->sym && symbol_result->nshots == 1) {
- rspamd_symcache_inc_frequency(task->cfg->cache,
- symbol_result->sym->cache_item,
- symbol_result->sym->name);
- }
- }
- }
-
- return ret;
- }
-
- static char *
- rspamd_task_option_safe_copy(struct rspamd_task *task,
- const char *val,
- gsize vlen,
- gsize *outlen)
- {
- const char *p, *end;
-
- p = val;
- end = val + vlen;
- vlen = 0; /* Reuse */
-
- while (p < end) {
- if (*p & 0x80) {
- UChar32 uc;
- int off = 0;
-
- U8_NEXT(p, off, end - p, uc);
-
- if (uc > 0) {
- if (u_isprint(uc)) {
- vlen += off;
- }
- else {
- /* We will replace it with 0xFFFD */
- vlen += MAX(off, 3);
- }
- }
- else {
- vlen += MAX(off, 3);
- }
-
- p += off;
- }
- else if (!g_ascii_isprint(*p)) {
- /* Another 0xFFFD */
- vlen += 3;
- p++;
- }
- else {
- p++;
- vlen++;
- }
- }
-
- char *dest, *d;
-
- dest = rspamd_mempool_alloc(task->task_pool, vlen + 1);
- d = dest;
- p = val;
-
- while (p < end) {
- if (*p & 0x80) {
- UChar32 uc;
- int off = 0;
-
- U8_NEXT(p, off, end - p, uc);
-
- if (uc > 0) {
- if (u_isprint(uc)) {
- memcpy(d, p, off);
- d += off;
- }
- else {
- /* We will replace it with 0xFFFD */
- *d++ = '\357';
- *d++ = '\277';
- *d++ = '\275';
- }
- }
- else {
- *d++ = '\357';
- *d++ = '\277';
- *d++ = '\275';
- }
-
- p += off;
- }
- else if (!g_ascii_isprint(*p)) {
- /* Another 0xFFFD */
- *d++ = '\357';
- *d++ = '\277';
- *d++ = '\275';
- p++;
- }
- else {
- *d++ = *p++;
- }
- }
-
- *d = '\0';
- *(outlen) = d - dest;
-
- return dest;
- }
-
- gboolean
- rspamd_task_add_result_option(struct rspamd_task *task,
- struct rspamd_symbol_result *s,
- const char *val,
- gsize vlen)
- {
- struct rspamd_symbol_option *opt, srch;
- gboolean ret = FALSE;
- char *opt_cpy = NULL;
- gsize cpy_len;
- khiter_t k;
- int r;
- struct rspamd_symbol_result *cur;
-
- if (s && val) {
- /*
- * Here we assume that this function is all the time called with the
- * symbol from the default result, not some shadow result, or
- * the option insertion will be wrong
- */
- LL_FOREACH(s, cur)
- {
- if (cur->opts_len < 0) {
- /* Cannot add more options, give up */
- msg_debug_task("cannot add more options to symbol %s when adding option %s",
- cur->name, val);
- ret = FALSE;
- continue;
- }
-
- if (!cur->options) {
- cur->options = kh_init(rspamd_options_hash);
- }
-
- if (vlen + cur->opts_len > task->cfg->max_opts_len) {
- /* Add truncated option */
- msg_info_task("cannot add more options to symbol %s when adding option %s",
- cur->name, val);
- val = "...";
- vlen = 3;
- cur->opts_len = -1;
- }
-
- if (!(cur->sym && (cur->sym->flags & RSPAMD_SYMBOL_FLAG_ONEPARAM))) {
-
- srch.option = (char *) val;
- srch.optlen = vlen;
- k = kh_get(rspamd_options_hash, cur->options, &srch);
-
- if (k == kh_end(cur->options)) {
- opt_cpy = rspamd_task_option_safe_copy(task, val, vlen, &cpy_len);
- if (cpy_len != vlen) {
- srch.option = (char *) opt_cpy;
- srch.optlen = cpy_len;
- k = kh_get(rspamd_options_hash, cur->options, &srch);
- }
- /* Append new options */
- if (k == kh_end(cur->options)) {
- opt = rspamd_mempool_alloc0(task->task_pool, sizeof(*opt));
- opt->optlen = cpy_len;
- opt->option = opt_cpy;
-
- kh_put(rspamd_options_hash, cur->options, opt, &r);
- DL_APPEND(cur->opts_head, opt);
-
- if (s == cur) {
- ret = TRUE;
- }
- }
- }
- }
- else {
- /* Skip addition */
- if (s == cur) {
- ret = FALSE;
- }
- }
-
- if (ret && cur->opts_len >= 0) {
- cur->opts_len += vlen;
- }
- }
- }
- else if (!val) {
- ret = TRUE;
- }
-
- task->result->nresults++;
-
- return ret;
- }
-
- struct rspamd_action_config *
- rspamd_find_action_config_for_action(struct rspamd_scan_result *scan_result,
- struct rspamd_action *act)
- {
- for (unsigned int i = 0; i < scan_result->nactions; i++) {
- struct rspamd_action_config *cur = &scan_result->actions_config[i];
-
- if (act == cur->action) {
- return cur;
- }
- }
-
- return NULL;
- }
-
- struct rspamd_action *
- rspamd_check_action_metric(struct rspamd_task *task,
- struct rspamd_passthrough_result **ppr,
- struct rspamd_scan_result *scan_result)
- {
- struct rspamd_action_config *action_lim,
- *noaction = NULL;
- struct rspamd_action *selected_action = NULL, *least_action = NULL;
- struct rspamd_passthrough_result *pr, *sel_pr = NULL;
- double max_score = -(G_MAXDOUBLE), sc;
- gboolean seen_least = FALSE;
-
- if (scan_result == NULL) {
- scan_result = task->result;
- }
-
- if (scan_result->passthrough_result != NULL) {
- DL_FOREACH(scan_result->passthrough_result, pr)
- {
- struct rspamd_action_config *act_config =
- rspamd_find_action_config_for_action(scan_result, pr->action);
-
- /* Skip disabled actions */
- if (act_config && (act_config->flags & RSPAMD_ACTION_RESULT_DISABLED)) {
- continue;
- }
-
- if (!seen_least || !(pr->flags & RSPAMD_PASSTHROUGH_LEAST)) {
- sc = pr->target_score;
- selected_action = pr->action;
-
- if (!(pr->flags & RSPAMD_PASSTHROUGH_LEAST)) {
- if (!isnan(sc)) {
- if (pr->action->action_type == METRIC_ACTION_NOACTION) {
- scan_result->score = MIN(sc, scan_result->score);
- }
- else {
- scan_result->score = sc;
- }
- }
-
- if (ppr) {
- *ppr = pr;
- }
-
- return selected_action;
- }
- else {
- seen_least = true;
- least_action = selected_action;
-
- if (isnan(sc)) {
-
- if (selected_action->flags & RSPAMD_ACTION_NO_THRESHOLD) {
- /*
- * In this case, we have a passthrough action that
- * is `least` action, however, there is no threshold
- * on it.
- *
- * Hence, we imply the following logic:
- *
- * - we leave score unchanged
- * - we apply passthrough no threshold action unless
- * score based action *is not* reject, otherwise
- * we apply reject action
- */
- }
- else {
- sc = selected_action->threshold;
- max_score = sc;
- sel_pr = pr;
- }
- }
- else {
- max_score = sc;
- sel_pr = pr;
- }
- }
- }
- }
- }
-
- /*
- * Select result by score
- */
- for (size_t i = scan_result->nactions - 1; i != (size_t) -1; i--) {
- action_lim = &scan_result->actions_config[i];
- sc = action_lim->cur_limit;
-
- if (action_lim->action->action_type == METRIC_ACTION_NOACTION) {
- noaction = action_lim;
- }
-
- if ((action_lim->flags & (RSPAMD_ACTION_RESULT_DISABLED | RSPAMD_ACTION_RESULT_NO_THRESHOLD))) {
- continue;
- }
-
- if (isnan(sc) ||
- (action_lim->action->flags & (RSPAMD_ACTION_NO_THRESHOLD | RSPAMD_ACTION_HAM))) {
- continue;
- }
-
- if (scan_result->score >= sc && sc > max_score) {
- selected_action = action_lim->action;
- max_score = sc;
- }
- }
-
- if (selected_action == NULL) {
- selected_action = noaction->action;
- }
-
- if (selected_action) {
-
- if (seen_least) {
- /* Adjust least action */
- if (least_action->flags & RSPAMD_ACTION_NO_THRESHOLD) {
- if (selected_action->action_type != METRIC_ACTION_REJECT &&
- selected_action->action_type != METRIC_ACTION_DISCARD) {
- /* Override score based action with least action */
- selected_action = least_action;
-
- if (ppr) {
- *ppr = sel_pr;
- }
- }
- }
- else {
- /* Adjust score if needed */
- if (max_score > scan_result->score) {
- if (ppr) {
- *ppr = sel_pr;
- }
-
- scan_result->score = max_score;
- }
- }
- }
-
- return selected_action;
- }
-
- if (ppr) {
- *ppr = sel_pr;
- }
-
- return noaction->action;
- }
-
- struct rspamd_symbol_result *
- rspamd_task_find_symbol_result(struct rspamd_task *task, const char *sym,
- struct rspamd_scan_result *result)
- {
- struct rspamd_symbol_result *res = NULL;
- khiter_t k;
-
- if (result == NULL) {
- /* Use default result */
- result = task->result;
- }
-
- k = kh_get(rspamd_symbols_hash, result->symbols, sym);
-
- if (k != kh_end(result->symbols)) {
- res = kh_value(result->symbols, k);
- }
-
- return res;
- }
-
- struct rspamd_symbol_result *rspamd_task_remove_symbol_result(
- struct rspamd_task *task,
- const char *symbol,
- struct rspamd_scan_result *result)
- {
- struct rspamd_symbol_result *res = NULL;
- khiter_t k;
-
- if (result == NULL) {
- /* Use default result */
- result = task->result;
- }
-
- k = kh_get(rspamd_symbols_hash, result->symbols, symbol);
-
- if (k != kh_end(result->symbols)) {
- res = kh_value(result->symbols, k);
-
- if (!isnan(res->score)) {
- /* Remove score from the result */
- result->score -= res->score;
-
- /* Also check the group limit */
- if (result->sym_groups && res->sym) {
- struct rspamd_symbol_group *gr;
- int i;
- khiter_t k_groups;
-
- PTR_ARRAY_FOREACH(res->sym->groups, i, gr)
- {
- double *gr_score;
-
- k_groups = kh_get(rspamd_symbols_group_hash,
- result->sym_groups, gr);
-
- if (k_groups != kh_end(result->sym_groups)) {
- gr_score = &kh_value(result->sym_groups, k_groups);
-
- if (gr_score) {
- *gr_score -= res->score;
- }
- }
- }
- }
- }
-
- kh_del(rspamd_symbols_hash, result->symbols, k);
- }
- else {
- return NULL;
- }
-
- return res;
- }
-
- void rspamd_task_symbol_result_foreach(struct rspamd_task *task,
- struct rspamd_scan_result *result, GHFunc func,
- gpointer ud)
- {
- const char *kk;
- struct rspamd_symbol_result *res;
-
- if (result == NULL) {
- /* Use default result */
- result = task->result;
- }
-
- if (func) {
- kh_foreach(result->symbols, kk, res, {
- func((gpointer) kk, (gpointer) res, ud);
- });
- }
- }
-
- struct rspamd_scan_result *
- rspamd_find_metric_result(struct rspamd_task *task,
- const char *name)
- {
- struct rspamd_scan_result *res;
-
- if (name == NULL || strcmp(name, "default") == 0) {
- return task->result;
- }
-
- DL_FOREACH(task->result, res)
- {
- if (res->name && strcmp(res->name, name) == 0) {
- return res;
- }
- }
-
- return NULL;
- }
-
- void rspamd_task_result_adjust_grow_factor(struct rspamd_task *task,
- struct rspamd_scan_result *result,
- double grow_factor)
- {
- const char *kk;
- struct rspamd_symbol_result *res;
- double final_grow_factor = grow_factor;
- double max_limit = G_MINDOUBLE;
-
- if (grow_factor > 1.0) {
-
- for (unsigned int i = 0; i < result->nactions; i++) {
- struct rspamd_action_config *cur = &result->actions_config[i];
-
- if (cur->cur_limit > 0 && max_limit < cur->cur_limit) {
- max_limit = cur->cur_limit;
- }
- }
-
- /* Adjust factor by selecting all symbols and checking those with positive scores */
- kh_foreach(result->symbols, kk, res, {
- if (res->score > 0) {
- double mult = grow_factor - 1.0;
- /* We adjust the factor by the ratio of the score to the max limit */
- if (max_limit > 0 && !isnan(res->score)) {
- mult *= res->score / max_limit;
- final_grow_factor *= 1.0 + mult;
- }
- }
- });
-
- /* At this stage we know that we have some grow factor to apply */
- if (final_grow_factor > 1.0) {
- msg_info_task("calculated final grow factor for task: %.3f (%.2f the original one)",
- final_grow_factor, grow_factor);
- kh_foreach(result->symbols, kk, res, {
- if (res->score > 0) {
- result->score -= res->score;
- res->score *= final_grow_factor;
- result->score += res->score;
- }
- });
- }
- }
- }
|