summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2015-03-17 18:35:42 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2015-03-17 18:35:42 +0000
commit1a4f62ba87320fcda783f6225574dd6e3bf2368a (patch)
tree6ac99ee440f5cf30e495b585931f56e7a545261d
parent7989b1fe1541d150231e61995ac96b063d6a0c05 (diff)
downloadrspamd-1a4f62ba87320fcda783f6225574dd6e3bf2368a.tar.gz
rspamd-1a4f62ba87320fcda783f6225574dd6e3bf2368a.zip
Add processing for AND, NOT and OR operations.
-rw-r--r--src/libutil/expression.c102
1 files changed, 102 insertions, 0 deletions
diff --git a/src/libutil/expression.c b/src/libutil/expression.c
index 23398510e..c028d75f9 100644
--- a/src/libutil/expression.c
+++ b/src/libutil/expression.c
@@ -27,6 +27,8 @@
#include "printf.h"
#include "regexp.h"
+#define RSPAMD_EXPR_FLAG_NEGATE (1 << 0)
+
enum rspamd_expression_op {
OP_INVALID = 0,
OP_PLUS, /* || or + */
@@ -57,6 +59,8 @@ struct rspamd_expression_elt {
gint op_idx;
} lim;
} p;
+ gint flags;
+ gint value;
};
struct rspamd_expression {
@@ -519,10 +523,108 @@ err:
return FALSE;
}
+#define CHOSE_OPERAND(e1, e2) (((e1)->p.atom->priority >= (e2)->p.atom->priority) ? \
+ (e1) : (e2))
+#define CHOOSE_REMAIN(e1, e2, es) ((es) == (e1) ? (e2) : (e1))
+
gint
rspamd_process_expression (struct rspamd_expression *expr, gpointer data)
{
+ struct rspamd_expression_elt *elt, *st_elt[2], *ev;
+ guint i;
+
g_assert (expr != NULL);
+ /* Ensure that stack is empty at this point */
+ g_assert (expr->expression_stack->len == 0);
+
+ /* Go through the whole expression */
+ for (i = 0; i < expr->expressions->len; i ++) {
+ elt = &g_array_index (expr->expressions, struct rspamd_expression_elt, i);
+
+ if (elt->type == ELT_ATOM || elt->type == ELT_LIMIT) {
+ /* Push this value to the stack without processing */
+ rspamd_expr_stack_push (expr, elt);
+ }
+ else {
+ /*
+ * Here we can process atoms on stack and apply
+ * some optimizations for them
+ */
+ g_assert (expr->expression_stack->len > 0);
+
+ switch (elt->p.op) {
+ case OP_NOT:
+ /* Just setup flag for the atom on top of the stack */
+ st_elt[0] = rspamd_expr_stack_pop (expr);
+ g_assert (st_elt[0]->type == ELT_ATOM);
+
+ if (st_elt[0]->flags & RSPAMD_EXPR_FLAG_NEGATE) {
+ st_elt[0]->flags &= ~RSPAMD_EXPR_FLAG_NEGATE;
+ }
+ else {
+ st_elt[0]->flags |= RSPAMD_EXPR_FLAG_NEGATE;
+ }
+
+ rspamd_expr_stack_push (expr, st_elt[0]);
+ break;
+ case OP_OR:
+ /* Evaluate first, if it evaluates to true, then push true */
+ g_assert (expr->expression_stack->len > 1);
+ st_elt[0] = rspamd_expr_stack_pop (expr);
+ st_elt[1] = rspamd_expr_stack_pop (expr);
+ ev = CHOSE_OPERAND (st_elt[0], st_elt[1]);
+ ev->value = expr->subr->process (data, ev->p.atom);
+
+ if (ev->flags & RSPAMD_EXPR_FLAG_NEGATE) {
+ ev->value = !ev->value;
+ }
+
+ if (ev->value) {
+ rspamd_expr_stack_push (expr, ev);
+ }
+ else {
+ ev = CHOOSE_REMAIN (st_elt[0], st_elt[1], ev);
+ ev->value = expr->subr->process (data, ev->p.atom);
+
+ if (ev->flags & RSPAMD_EXPR_FLAG_NEGATE) {
+ ev->value = !ev->value;
+ }
+ /* Push the remaining op */
+ rspamd_expr_stack_push (expr, ev);
+ }
+ break;
+ case OP_AND:
+ /* Evaluate first, if it evaluates to false, then push false */
+ g_assert (expr->expression_stack->len > 1);
+ st_elt[0] = rspamd_expr_stack_pop (expr);
+ st_elt[1] = rspamd_expr_stack_pop (expr);
+ ev = CHOSE_OPERAND (st_elt[0], st_elt[1]);
+ ev->value = expr->subr->process (data, ev->p.atom);
+
+ if (ev->flags & RSPAMD_EXPR_FLAG_NEGATE) {
+ ev->value = !ev->value;
+ }
+
+ if (!ev->value) {
+ rspamd_expr_stack_push (expr, ev);
+ }
+ else {
+ ev = CHOOSE_REMAIN (st_elt[0], st_elt[1], ev);
+ ev->value = expr->subr->process (data, ev->p.atom);
+
+ if (ev->flags & RSPAMD_EXPR_FLAG_NEGATE) {
+ ev->value = !ev->value;
+ }
+ /* Push the remaining op */
+ rspamd_expr_stack_push (expr, ev);
+ }
+ break;
+ default:
+ g_assert (0);
+ break;
+ }
+ }
+ }
return 0;
}