Use array of actions instead of a linked list to speed up processing.
* Removed required_score, reject_score and action in metric config,
now REJECT is the only default action for a metric.
g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid action definition: %s", cur->key);
return FALSE;
}
- action = memory_pool_alloc (cfg->cfg_pool, sizeof (struct metric_action));
+ action = &metric->actions[action_value];
action->action = action_value;
action->score = action_score;
- metric->actions = g_list_prepend (metric->actions, action);
}
}
else if (new) {
if ((def_metric = g_hash_table_lookup (cfg->metrics, DEFAULT_METRIC)) == NULL) {
def_metric = check_metric_conf (cfg, NULL);
def_metric->name = DEFAULT_METRIC;
- def_metric->required_score = DEFAULT_SCORE;
- def_metric->reject_score = DEFAULT_REJECT_SCORE;
+ def_metric->actions[METRIC_ACTION_REJECT].score = DEFAULT_SCORE;
cfg->metrics_list = g_list_prepend (cfg->metrics_list, def_metric);
g_hash_table_insert (cfg->metrics, DEFAULT_METRIC, def_metric);
}
struct metric *
check_metric_conf (struct config_file *cfg, struct metric *c)
{
+ int i;
if (c == NULL) {
c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct metric));
- c->action = METRIC_ACTION_REJECT;
c->grow_factor = 1.0;
c->symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
c->descriptions = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
+ for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i ++) {
+ c->actions[i].score = -1.0;
+ }
memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->symbols);
memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->descriptions);
}
},
{
"required_score",
- xml_handle_double,
- G_STRUCT_OFFSET (struct metric, required_score),
+ xml_handle_deprecated,
+ 0,
NULL
},
{
"reject_score",
- xml_handle_double,
- G_STRUCT_OFFSET (struct metric, reject_score),
+ xml_handle_deprecated,
+ 0,
NULL
},
{
/* First of all check whether we have data with weight (reject:50 for example) */
if ((p = strchr (data, ':')) == NULL) {
if (check_action_str (data, &res)) {
- metric->action = res;
+ /* XXX: no longer needed */
return TRUE;
}
return FALSE;
return FALSE;
}
else {
- action = memory_pool_alloc (cfg->cfg_pool, sizeof (struct metric_action));
+ action = &metric->actions[res];
action->action = res;
errno = 0;
action->score = strtod (p + 1, &errstr);
msg_err ("invalid double value: %s", data);
return FALSE;
}
- metric->actions = g_list_prepend (metric->actions, action);
}
}
return TRUE;
}
+gboolean
+xml_handle_deprecated (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset)
+{
+
+ msg_err ("parameter is depreciated: %s", ctx->section_name);
+ return TRUE;
+}
+
gboolean
xml_handle_double (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset)
{
/* Flags */
gboolean xml_handle_boolean (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset);
+/* For deprecated attributes */
+gboolean xml_handle_deprecated (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset);
+
/* Specific params */
/* Options specific */
gboolean options_handle_nameserver (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset);
struct dynamic_cfg_metric {
GList *symbols;
- GList *actions;
+ struct dynamic_cfg_action actions[METRIC_ACTION_MAX];
gchar *name;
};
GList *cur, *cur_elt;
struct dynamic_cfg_metric *metric;
struct dynamic_cfg_symbol *sym;
- struct dynamic_cfg_action *act;
if (conf_metrics) {
cur = conf_metrics;
}
g_list_free (metric->symbols);
}
-
- if (metric->actions) {
- cur_elt = metric->actions;
- while (cur_elt) {
- act = cur_elt->data;
- g_slice_free1 (sizeof (struct dynamic_cfg_symbol), act);
- cur_elt = g_list_next (cur_elt);
- }
- g_list_free (metric->actions);
- }
g_slice_free1 (sizeof (struct dynamic_cfg_metric), metric);
cur = g_list_next (cur);
}
static void
apply_dynamic_conf (GList *conf_metrics, struct config_file *cfg)
{
- GList *cur, *cur_elt, *tmp;
+ GList *cur, *cur_elt;
struct dynamic_cfg_metric *metric;
struct dynamic_cfg_symbol *sym;
struct dynamic_cfg_action *act;
struct metric *real_metric;
struct metric_action *real_act;
gdouble *w;
+ gint i, j;
cur = conf_metrics;
while (cur) {
cur_elt = g_list_next (cur_elt);
}
- cur_elt = metric->actions;
- while (cur_elt) {
- act = cur_elt->data;
- tmp = real_metric->actions;
- while (tmp) {
- real_act = tmp->data;
+ for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i ++) {
+ act = &metric->actions[i];
+ if (act->value < 0) {
+ continue;
+ }
+ for (j = METRIC_ACTION_REJECT; j < METRIC_ACTION_MAX; j ++) {
+ real_act = &real_metric->actions[j];
if (real_act->action == act->action) {
real_act->score = act->value;
}
/* Update required score accordingly to metric's action */
- if (act->action == real_metric->action) {
- real_metric->required_score = act->value;
+ if (act->action == METRIC_ACTION_REJECT) {
+ real_metric->actions[METRIC_ACTION_REJECT].score = act->value;
}
- tmp = g_list_next (tmp);
}
- cur_elt = g_list_next (cur_elt);
}
}
cur = g_list_next (cur);
continue;
}
cur_metric = g_slice_alloc0 (sizeof (struct dynamic_cfg_metric));
+ for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i ++) {
+ cur_metric->actions[i].value = -1.0;
+ }
cur_metric->name = g_strdup (json_string_value (cur_nm));
cur_nm = json_object_get (cur_elt, "symbols");
/* Parse symbols */
it_val = json_array_get (cur_nm, j);
if (it_val && json_is_object (it_val)) {
if (json_object_get (it_val, "name") && json_object_get (it_val, "value")) {
-
- cur_action = g_slice_alloc0 (sizeof (struct dynamic_cfg_action));
if (!check_action_str (json_string_value (json_object_get (it_val, "name")), &test_act)) {
msg_err ("unknown action: %s", json_string_value (json_object_get (it_val, "name")));
g_slice_free1 (sizeof (struct dynamic_cfg_action), cur_action);
continue;
}
+ cur_action = &cur_metric->actions[test_act];
cur_action->action = test_act;
cur_action->value = json_number_value (json_object_get (it_val, "value"));
- /* Insert action */
- cur_metric->actions = g_list_prepend (cur_metric->actions, cur_action);
}
else {
msg_info ("json symbol object has no mandatory 'name' and 'value' attributes");
struct dynamic_cfg_symbol *sym;
struct dynamic_cfg_action *act;
FILE *f;
+ gint i;
+ gboolean start = TRUE;
/* Open buffered stream for the descriptor */
if ((f = fdopen (fd, "a+")) == NULL) {
}
if (metric->actions) {
- cur_elt = metric->actions;
fprintf (f, " \"actions\": [\n");
- while (cur_elt) {
- act = cur_elt->data;
- cur_elt = g_list_next (cur_elt);
- if (cur_elt) {
- fprintf (f, " {\"name\": \"%s\",\"value\": %.2f},\n", str_action_metric (act->action), act->value);
+ for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i ++) {
+ act = &metric->actions[i];
+ if (act->value < 0) {
+ continue;
}
- else {
- fprintf (f, " {\"name\": \"%s\",\"value\": %.2f}\n", str_action_metric (act->action), act->value);
+ fprintf (f, " %s{\"name\": \"%s\",\"value\": %.2f}\n",
+ (start ? "" : ","), str_action_metric (act->action), act->value);
+ if (start) {
+ start = FALSE;
}
}
fprintf (f, " ]\n");
{
GList *cur;
struct dynamic_cfg_metric *metric = NULL;
- struct dynamic_cfg_action *act = NULL;
if (cfg->dynamic_conf == NULL) {
msg_info ("dynamic conf is disabled");
if (metric != NULL) {
/* Search for an action */
- cur = metric->actions;
- while (cur) {
- act = cur->data;
- if (act->action == action) {
- act->value = value;
- msg_debug ("change value of action %d to %.2f", action, value);
- break;
- }
- act = NULL;
- cur = g_list_next (cur);
- }
- if (act == NULL) {
- /* Action not found, insert it */
- act = g_slice_alloc (sizeof (struct dynamic_cfg_action));
- act->action = action;
- act->value = value;
- metric->actions = g_list_prepend (metric->actions, act);
- msg_debug ("create action %d in metric %s", action, metric_name);
- }
+ metric->actions[action].value = value;
}
else {
/* Metric not found, create it */
metric = g_slice_alloc0 (sizeof (struct dynamic_cfg_metric));
- act = g_slice_alloc (sizeof (struct dynamic_cfg_action));
- act->action = action;
- act->value = value;
- metric->actions = g_list_prepend (metric->actions, act);
+ metric->actions[action].value = value;
metric->name = g_strdup (metric_name);
cfg->current_dynamic_conf = g_list_prepend (cfg->current_dynamic_conf, metric);
msg_debug ("create metric %s for action %d", metric_name, action);
struct metric_result *res;
double ms, rs;
- /* Avoid concurrenting while checking results */
+ /* Avoid concurrency while checking results */
#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30))
g_static_mutex_lock (&result_mtx);
#else
G_UNLOCK (result_mtx);
#endif
if (!check_metric_settings (res, &ms, &rs)) {
- ms = metric->required_score;
+ ms = metric->actions[METRIC_ACTION_REJECT].score;
}
- return res->score >= ms;
+ return (ms > 0 && res->score >= ms);
}
#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30))
while (cur) {
metric = cur->data;
if (!task->pass_all_filters &&
- metric->action == METRIC_ACTION_REJECT &&
+ metric->actions[METRIC_ACTION_REJECT].score > 0 &&
check_metric_is_spam (task, metric)) {
task->s->wanna_die = TRUE;
check_session_pending (task->s);
rspamd_snprintf (header_name, sizeof (header_name), "X-Spam-%s", metric_res->metric->name);
if (!check_metric_settings (metric_res, &ms, &rs)) {
- ms = metric_res->metric->required_score;
+ ms = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
}
- if (metric_res->score >= ms) {
+ if (ms > 0 && metric_res->score >= ms) {
r += rspamd_snprintf (outbuf + r, sizeof (outbuf) - r, "yes; %.2f/%.2f/%.2f; ", metric_res->score, ms, rs);
}
else {
case METRIC_ACTION_GREYLIST:
return "greylist";
case METRIC_ACTION_NOACTION:
- return "no action";
+ return "no_action";
+ case METRIC_ACTION_MAX:
+ return "invalid max action";
}
return "unknown action";
gint
check_metric_action (double score, double required_score, struct metric *metric)
{
- GList *cur;
struct metric_action *action, *selected_action = NULL;
double max_score = 0;
+ int i;
if (score >= required_score) {
- return metric->action;
+ return METRIC_ACTION_REJECT;
}
else if (metric->actions == NULL) {
return METRIC_ACTION_NOACTION;
}
else {
- cur = metric->actions;
- while (cur) {
- action = cur->data;
+ for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i ++) {
+ action = &metric->actions[i];
+ if (action->score < 0) {
+ continue;
+ }
if (score >= action->score && action->score > max_score) {
selected_action = action;
max_score = action->score;
}
- cur = g_list_next (cur);
}
if (selected_action) {
return selected_action->action;
METRIC_ACTION_REWRITE_SUBJECT,
METRIC_ACTION_ADD_HEADER,
METRIC_ACTION_GREYLIST,
- METRIC_ACTION_NOACTION
+ METRIC_ACTION_NOACTION,
+ METRIC_ACTION_MAX
};
struct metric_action {
gchar *func_name; /**< name of consolidation function */
metric_cons_func func; /**< c consolidation function */
double grow_factor; /**< grow factor for metric */
- double required_score; /**< required score for this metric */
- double reject_score; /**< reject score for this metric */
GHashTable *symbols; /**< weights of symbols in metric */
GHashTable *descriptions; /**< descriptions of symbols in metric */
- enum rspamd_metric_action action; /**< action to do by this metric by default */
- GList *actions; /**< actions that can be performed by this metric */
+ struct metric_action actions[METRIC_ACTION_MAX]; /**< all actions of the metric */
gchar *subject; /**< subject rewrite string */
};
lua_newtable (L);
lua_pushnumber (L, metric_res->score);
lua_rawseti (L, -2, 1);
- lua_pushnumber (L, metric_res->metric->required_score);
+ lua_pushnumber (L, metric_res->metric->actions[METRIC_ACTION_REJECT].score);
lua_rawseti (L, -2, 2);
- lua_pushnumber (L, metric_res->metric->reject_score);
+ lua_pushnumber (L, metric_res->metric->actions[METRIC_ACTION_REJECT].score);
lua_rawseti (L, -2, 3);
}
else {
if (task && metric_name) {
if ((metric_res = g_hash_table_lookup (task->results, metric_name)) != NULL) {
- action = check_metric_action (metric_res->score, metric_res->metric->required_score, metric_res->metric);
+ action = check_metric_action (metric_res->score,
+ metric_res->metric->actions[METRIC_ACTION_REJECT].score, metric_res->metric);
lua_pushstring (L, str_action_metric (action));
}
else {
return "greylist";
case METRIC_ACTION_NOACTION:
return "no action";
+ case METRIC_ACTION_MAX:
+ return "invalid max action";
}
return "unknown action";
}
if (metric_name == NULL || metric_value == NULL) {
m = g_hash_table_lookup (task->cfg->metrics, DEFAULT_METRIC);
- default_required_score = m->required_score;
+ default_required_score = m->actions[METRIC_ACTION_REJECT].score;
default_score = 0;
if (metric_res != NULL && !check_metric_settings (metric_res, &ms, &rs)) {
- ms = m->required_score;
- rs = m->reject_score;
+ ms = m->actions[METRIC_ACTION_REJECT].score;
+ rs = m->actions[METRIC_ACTION_REJECT].score;
}
else if (metric_res == NULL) {
- ms = m->required_score;
- rs = m->reject_score;
+ ms = m->actions[METRIC_ACTION_REJECT].score;
+ rs = m->actions[METRIC_ACTION_REJECT].score;
}
if (!task->is_json) {
if (!task->is_skipped) {
cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset,
cd->log_size - cd->log_offset,
- "(%s: F (no action): [0.00/%.2f/%.2f] [", "default", ms, rs);
+ "(%s: F (no action): [0.00/%.2f] [", "default", ms);
}
else {
cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset,
- cd->log_size - cd->log_offset, "(%s: S: [0.00/%.2f/%.2f] [",
- "default", ms, rs);
+ cd->log_size - cd->log_offset, "(%s: S: [0.00/%.2f] [",
+ "default", ms);
}
}
else {
/* XXX: dirty hack */
if (strcmp (metric_res->metric->name, DEFAULT_METRIC) == 0) {
- default_required_score = metric_res->metric->required_score;
+ default_required_score = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
default_score = metric_res->score;
}
if (!check_metric_settings (metric_res, &ms, &rs)) {
- ms = metric_res->metric->required_score;
- rs = metric_res->metric->reject_score;
+ ms = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
+ rs = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
}
if (!check_metric_action_settings (task, metric_res,
metric_res->score, &action)) {
cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset,
cd->log_size - cd->log_offset,
- "(%s: %c (%s): [%.2f/%.2f/%.2f] [", (gchar *) metric_name,
+ "(%s: %c (%s): [%.2f/%.2f] [", (gchar *) metric_name,
is_spam ? 'T' : 'F', str_action_metric (action),
- metric_res->score, ms, rs);
+ metric_res->score, ms);
}
else {
cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset,
cd->log_size - cd->log_offset,
- "(%s: %c (default): [%.2f/%.2f/%.2f] [",
- (gchar *) metric_name, 'S', metric_res->score, ms, rs);
+ "(%s: %c (default): [%.2f/%.2f] [",
+ (gchar *) metric_name, 'S', metric_res->score, ms);
}
}
}
else {
row->score = metric_res->score;
- row->required_score = metric_res->metric->required_score;
- row->action = check_metric_action (metric_res->score, metric_res->metric->required_score, metric_res->metric);
+ row->required_score = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
+ row->action = check_metric_action (metric_res->score,
+ metric_res->metric->actions[METRIC_ACTION_REJECT].score, metric_res->metric);
cbdata.pos = row->symbols;
cbdata.remain = sizeof (row->symbols);
g_hash_table_foreach (metric_res->symbols, roll_history_symbols_callback, &cbdata);
task = cd->session->task;
if (!check_metric_settings (metric_res, &ms, &rs)) {
- ms = metric_res->metric->required_score;
- rs = metric_res->metric->reject_score;
+ ms = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
+ rs = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
}
if (! check_metric_action_settings (task, metric_res, metric_res->score, &action)) {
action = check_metric_action (metric_res->score, ms, metric_res->metric);
"\"required_score\":%.2f,"
"\"action\":\"%s\","
"\"symbols\":[",
- mres->score >= mres->metric->required_score ? "true" : "false",
- cbdata->task->is_skipped ? "true" : "false", mres->score, mres->metric->required_score,
- str_action_metric (check_metric_action (mres->score, mres->metric->required_score, mres->metric)));
+ mres->score >= mres->metric->actions[METRIC_ACTION_REJECT].score ? "true" : "false",
+ cbdata->task->is_skipped ? "true" : "false", mres->score,
+ mres->metric->actions[METRIC_ACTION_REJECT].score,
+ str_action_metric (
+ check_metric_action (mres->score, mres->metric->actions[METRIC_ACTION_REJECT].score, mres->metric)));
/* Iterate all symbols */
g_hash_table_foreach (mres->symbols, http_scan_metric_symbols_callback, cbdata);
evbuffer_add (evb, "]}" CRLF, 4);
json_t *jv, struct metric *metric, enum rspamd_metric_action act)
{
gdouble actval;
- GList *cur;
- struct metric_action *act_found;
if (!json_is_number (jv)) {
msg_err ("json element data error");
return FALSE;
}
actval = json_number_value (jv);
- if (metric->action == act && metric->required_score != actval) {
+ if (metric->actions[act].score != actval) {
return add_dynamic_action (cfg, DEFAULT_METRIC, act, actval);
}
-
- /* Try to search in all metrics */
- /* XXX: clarify this code, currently it looks like a crap */
- cur = metric->actions;
- while (cur) {
- act_found = cur->data;
- if (act_found->action == act) {
- if (act_found->score != actval) {
- return add_dynamic_action (cfg, DEFAULT_METRIC, act, actval);
- }
- }
- cur = g_list_next (cur);
- }
-
return TRUE;
}
struct rspamd_webui_worker_ctx *ctx = arg;
struct evbuffer *evb;
struct metric *metric;
- GList *cur;
struct metric_action *act;
+ gboolean start = TRUE;
+ gint i;
if (!http_check_password (ctx, req)) {
return;
/* Get actions for default metric */
metric = g_hash_table_lookup (ctx->cfg->metrics, DEFAULT_METRIC);
if (metric != NULL) {
-
- cur = metric->actions;
- while (cur) {
- act = cur->data;
- cur = g_list_next (cur);
- evbuffer_add_printf (evb, "{\"action\":\"%s\",\"value\":%.2f},", str_action_metric (act->action), act->score);
+ for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i ++) {
+ act = &metric->actions[i];
+ if (act->score > 0) {
+ evbuffer_add_printf (evb, "%s{\"action\":\"%s\",\"value\":%.2f}",
+ (start ? "" : ","), str_action_metric (act->action), act->score);
+ if (start) {
+ start = FALSE;
+ }
+ }
}
- /* Print default action */
- evbuffer_add_printf (evb, "{\"action\":\"%s\",\"value\":%.2f}", str_action_metric (metric->action), metric->required_score);
}
evbuffer_add (evb, "]" CRLF, 3);