]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Project] Support multiply, minus and divide operators in expressions
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 16 Jun 2020 14:35:48 +0000 (15:35 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 16 Jun 2020 14:35:48 +0000 (15:35 +0100)
src/libutil/expression.c
src/libutil/expression.h

index 6099a308f19f67da142c20a68e2888085c2a7188..9b166ee25db2d63931e61d8db1abaf15ae1e72cd 100644 (file)
@@ -89,6 +89,12 @@ rspamd_expr_op_to_str (enum rspamd_expression_op op)
        case OP_PLUS:
                op_str = "+";
                break;
+       case OP_MINUS:
+               op_str = "-";
+               break;
+       case OP_DIVIDE:
+               op_str = "/";
+               break;
        case OP_NOT:
                op_str = "!";
                break;
@@ -180,9 +186,14 @@ rspamd_expr_logic_priority (enum rspamd_expression_op op)
 
        switch (op) {
        case OP_NOT:
+               ret = 7;
+               break;
+       case OP_MULT:
+       case OP_DIVIDE:
                ret = 6;
                break;
        case OP_PLUS:
+       case OP_MINUS:
                ret = 5;
                break;
        case OP_GE:
@@ -191,7 +202,6 @@ rspamd_expr_logic_priority (enum rspamd_expression_op op)
        case OP_LT:
                ret = 4;
                break;
-       case OP_MULT:
        case OP_AND:
                ret = 3;
                break;
@@ -227,6 +237,8 @@ rspamd_expr_is_operation_symbol (gchar a)
        case '<':
        case '+':
        case '*':
+       case '-':
+       case '/':
                return TRUE;
        }
 
@@ -278,6 +290,12 @@ rspamd_expr_str_to_op (const gchar *a, const gchar *end, const gchar **next)
                case '+':
                        op = OP_PLUS;
                        break;
+               case '/':
+                       op = OP_DIVIDE;
+                       break;
+               case '-':
+                       op = OP_MINUS;
+                       break;
                case ')':
                        op = OP_CBRACE;
                        break;
@@ -624,10 +642,50 @@ rspamd_parse_expression (const gchar *line, gsize len,
                                continue;
                        }
                        else if (rspamd_expr_is_operation_symbol (*p)) {
+                               /* Lookahead */
                                if (p + 1 < end) {
                                        gchar t = *(p + 1);
 
-                                       if (t != ':') {
+                                       if (t == ':') {
+                                               /* Special case, treat it as an atom */
+                                       }
+                                       else if (*p == '/') {
+                                               /* Lookahead for division operation to distinguish from regexp */
+                                               const gchar *track = p + 1;
+
+                                               /* Skip spaces */
+                                               while (track < end && g_ascii_isspace (*track)) {
+                                                       track++;
+                                               }
+
+                                               /* Check for a number */
+                                               if (rspamd_regexp_search (num_re,
+                                                               track,
+                                                               end - track,
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               NULL)) {
+                                                       state = PARSE_OP;
+                                                       continue;
+                                               }
+
+                                               /* Fallback to PARSE_ATOM state */
+                                       }
+                                       else if (*p == '-') {
+                                               /* - is used in composites, so we need to distinguish - from
+                                                * 1) unary minus of a limit!
+                                                * 2) -BLAH in composites
+                                                * Decision is simple: require a space after binary `-` op
+                                                */
+                                               if (g_ascii_isspace (t)) {
+                                                       state = PARSE_OP;
+                                                       continue;
+                                               }
+                                               /* Fallback to PARSE_ATOM state */
+                                       }
+                                       else {
+                                               /* Generic operation */
                                                state = PARSE_OP;
                                                continue;
                                        }
@@ -644,6 +702,7 @@ rspamd_parse_expression (const gchar *line, gsize len,
                         * 2) if we have full numeric string, then we check for
                         * the following expression:
                         *  ^\d+\s*[><]$
+                        *  and check the operation on stack
                         */
                        if ((gulong)(end - p) > sizeof ("and ") &&
                                (g_ascii_strncasecmp (p, "and ", sizeof ("and ") - 1) == 0 ||
@@ -656,12 +715,13 @@ rspamd_parse_expression (const gchar *line, gsize len,
                        }
                        else {
                                /*
-                                * If we have any comparison operator in the stack, then try
+                                * If we have any comparison or arithmetic operator in the stack, then try
                                 * to parse limit
                                 */
                                op = GPOINTER_TO_INT (rspamd_expr_stack_peek (e));
 
-                               if (op >= OP_LT && op <= OP_GE) {
+                               if (op == OP_MULT || op == OP_MINUS || op == OP_DIVIDE ||
+                                               op == OP_PLUS || (op >= OP_LT && op <= OP_GE)) {
                                        if (rspamd_regexp_search (num_re,
                                                        p,
                                                        end - p,
@@ -673,6 +733,7 @@ rspamd_parse_expression (const gchar *line, gsize len,
                                                state = PARSE_LIM;
                                                continue;
                                        }
+                                       /* Fallback to atom parsing */
                                }
 
                                /* Try to parse atom */
@@ -973,7 +1034,16 @@ rspamd_ast_do_op (struct rspamd_expression_elt *elt, gdouble val,
                ret = fabs (val) > DOUBLE_EPSILON ? 0.0 : 1.0;
                break;
        case OP_PLUS:
-               ret = acc + val;
+               ret = first_elt ? (val) : (acc + val);
+               break;
+       case OP_MULT:
+               ret = first_elt ? (val) : (acc * val);
+               break;
+       case OP_MINUS:
+               ret = first_elt ? (val) : (acc - val);
+               break;
+       case OP_DIVIDE:
+               ret = first_elt ? (val) : (acc / val);
                break;
        case OP_GE:
                ret = first_elt ? (val >= lim) : (acc >= lim);
@@ -987,7 +1057,6 @@ rspamd_ast_do_op (struct rspamd_expression_elt *elt, gdouble val,
        case OP_LT:
                ret = first_elt ? (val < lim) : (acc < lim);
                break;
-       case OP_MULT:
        case OP_AND:
                ret = first_elt ? (val) : (acc * val);
                break;
index bd18fa75f4a48b161c19c274ca5742802d43bbbe..3a4f1ac3f8de872dcfe91e1969471d49ac0cad4f 100644 (file)
@@ -30,8 +30,10 @@ extern "C" {
 
 enum rspamd_expression_op {
        OP_INVALID = 0,
-       OP_PLUS, /* || or + */
-       OP_MULT, /* && or * */
+       OP_PLUS, /* + */
+       OP_MULT, /* * */
+       OP_MINUS, /* - */
+       OP_DIVIDE, /* / */
        OP_OR, /* || or | */
        OP_AND, /* && or & */
        OP_NOT, /* ! */