]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-var-expand: Use lib-regex instead of libc regex
authorAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 1 Aug 2025 09:17:49 +0000 (12:17 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 29 Aug 2025 08:42:35 +0000 (11:42 +0300)
src/lib-var-expand/expansion-filter-if.c
src/lib-var-expand/expansion-filter.c
src/lib-var-expand/test-var-expand.c

index cb708402e983be680ecc85e9b595cd0c52bed1d9..5866efe805d939f80f06a010fb0e3b7d256c5a70 100644 (file)
@@ -3,10 +3,9 @@
 #include "lib.h"
 #include "var-expand-private.h"
 #include "expansion.h"
+#include "dregex.h"
 #include "wildcard-match.h"
 
-#include <regex.h>
-
 enum var_expand_if_op {
        OP_UNKNOWN,
        OP_NUM_EQ,
@@ -65,7 +64,6 @@ fn_if_cmp(struct var_expand_state *state, const struct var_expand_parameter *p_l
          enum var_expand_if_op op, const struct var_expand_parameter *p_rhs,
          bool *result_r, const char **error_r)
 {
-       bool neg = FALSE;
        if (op < OP_STR_EQ) {
                intmax_t a;
                intmax_t b;
@@ -109,6 +107,8 @@ fn_if_cmp(struct var_expand_state *state, const struct var_expand_parameter *p_l
                return -1;
        }
 
+       bool neg ATTR_UNUSED = FALSE;
+
        switch (op) {
        case OP_STR_EQ:
                *result_r = strcmp(lhs,rhs) == 0;
@@ -140,29 +140,15 @@ fn_if_cmp(struct var_expand_state *state, const struct var_expand_parameter *p_l
        case OP_STR_REGEXP: {
                int ec;
                bool res;
-               regex_t reg;
-               if ((ec = regcomp(&reg, rhs, REG_EXTENDED)) != 0) {
-                       size_t size;
-                       char *errbuf;
-                       size = regerror(ec, &reg, NULL, 0);
-                       errbuf = t_malloc_no0(size);
-                       (void)regerror(ec, &reg, errbuf, size);
-                       *error_r = t_strdup_printf("regexp() failed: %s",
-                                                  errbuf);
+
+               ec = dregex_match(rhs, lhs, 0, error_r);
+
+               if (ec < 0)
                        return -1;
-               }
-               if ((ec = regexec(&reg, lhs, 0, 0, 0)) != 0) {
-                       i_assert(ec == REG_NOMATCH);
-                       res = FALSE;
-               } else {
-                       res = TRUE;
-               }
-               regfree(&reg);
-               /* this should be same as neg.
-                  if NOT_REGEXP, neg == TRUE and res should be FALSE
-                  if REGEXP, ned == FALSE, and res should be TRUE
-                */
+
+               res = ec == 1;
                *result_r = res != neg;
+
                return 0;
        }
        default:
index 790005786df35a0dab621780b9d802590eceb3e0..4bff618d97a2d4e3908010f49f9b53dd16c0fd08 100644 (file)
@@ -8,11 +8,11 @@
 #include "str.h"
 #include "strescape.h"
 #include "str-sanitize.h"
+#include "dregex.h"
 #include "var-expand-private.h"
 #include "expansion.h"
 
 #include <ctype.h>
-#include <regex.h>
 
 ARRAY_DEFINE_TYPE(var_expand_filter, struct var_expand_filter);
 static ARRAY_TYPE(var_expand_filter) dyn_filters = ARRAY_INIT;
@@ -795,66 +795,15 @@ static int fn_regexp(const struct var_expand_statement *stmt,
 
        ERROR_IF_NO_TRANSFER_TO("regexp");
 
-       int ret;
-       regex_t reg;
-       regmatch_t matches[10];
-       const char *input = str_c(state->transfer);
-       i_zero(&reg);
-       i_zero(&matches);
-       if ((ret = regcomp(&reg, pat, REG_EXTENDED)) != 0) {
-               char errbuf[1024] = {0};
-               (void)regerror(ret, &reg, errbuf, sizeof(errbuf));
-               regfree(&reg);
-               *error_r = t_strdup(errbuf);
-               return -1;
-       }
-
-       ret = regexec(&reg, input, N_ELEMENTS(matches), matches, 0);
-       if (ret == REG_NOMATCH) {
-               /* no match, do not modify */
-               regfree(&reg);
-               return 0;
-       }
-
-       /* perform replacement */
+       const char *input ATTR_UNUSED = str_c(state->transfer);
        string_t *dest = t_str_new(strlen(rep));
-       const char *p0 = rep;
-       const char *p1;
-       ret = 0;
-
-       /* Supports up to 9 capture groups,
-        * if we need more, then this code should
-        * be refactored to see how many we really need
-        * and create a proper template from this. */
-       while ((p1 = strchr(p0, '\\')) != NULL) {
-               if (i_isdigit(p1[1])) {
-                       /* looks like a placeholder */
-                       str_append_data(dest, p0, p1 - p0);
-                       unsigned int g = p1[1] - '0';
-                       if (g >= N_ELEMENTS(matches) ||
-                           matches[g].rm_so == -1) {
-                               *error_r = "Invalid capture group";
-                               ret = -1;
-                               break;
-                       }
-                       i_assert(matches[g].rm_eo >= matches[g].rm_so);
-                       str_append_data(dest, input + matches[g].rm_so,
-                                       matches[g].rm_eo - matches[g].rm_so);
-                       p0 = p1 + 2;
-               } else {
-                       str_append_c(dest, *p1);
-                       p1++;
-               }
-       }
 
-       regfree(&reg);
+       int ret = dregex_replace(pat, input, rep, dest, 0, error_r);
 
-       if (ret == 0) {
-               str_append(dest, p0);
+       if (ret > 0)
                var_expand_state_set_transfer_data(state, dest->data, dest->used);
-       }
 
-       return ret == 0 ? 0 : -1;
+       return ret < 0 ? -1 : 0;
 }
 
 static int fn_number(const struct var_expand_statement *stmt, bool be,
index 3f3327f1b347537b2a6ca964b38317e2db06f86e..6ee360c637fca1b970b7664ee36faca86ae71c1a 100644 (file)
@@ -196,8 +196,10 @@ static void test_var_expand_builtin_filters(void) {
                { .in = "%{truncate(3)}", .out = "truncate: No value to truncate", .ret = -1 },
                /* ldap dn */
                { .in = "cn=%{first},ou=%{domain | ldap_dn}", .out = "cn=hello,ou=test,dc=dovecot,dc=org", .ret = 0 },
+#ifdef HAVE_LIBPCRE
                /* regexp */
-               { .in = "%{literal('hello world') | regexp('(.*) (.*)', '\\\\2 \\\\1')}", .out = "world hello" },
+               { .in = "%{literal('hello world') | regexp('(.*) (.*)', '$2 $1')}", .out = "world hello" },
+#endif
                /* index */
                { .in = "%{user | index('@',0)}", .out = "user", .ret = 0 },
                { .in = "%{user | username}", .out = "user", .ret = 0 },
@@ -342,6 +344,7 @@ static void test_var_expand_if(void)
                { .in = "%{literal('a') | if('!*', '*a*', 'yes', 'no')}", .out = "no", .ret = 0 },
                { .in = "%{literal('a') | if('!*', '*b*', 'yes', 'no')}", .out = "yes", .ret = 0 },
                { .in = "%{literal('a') | if('!*', '*', 'yes', 'no')}", .out = "no", .ret = 0 },
+#ifdef HAVE_LIBPCRE
                { .in = "%{literal('a') | if('~', 'a', 'yes', 'no')}", .out = "yes", .ret = 0 },
                { .in = "%{literal('a') | if('~', 'b', 'yes', 'no')}", .out = "no", .ret = 0 },
                { .in = "%{literal('a') | if('~', '.*a.*', 'yes', 'no')}", .out = "yes", .ret = 0 },
@@ -354,6 +357,7 @@ static void test_var_expand_if(void)
                { .in = "%{literal('a') | if('!~', '.*', 'yes', 'no')}", .out = "no", .ret = 0 },
                { .in = "%{literal('this is test') | if('~', '^test', 'yes', 'no')}", .out = "no", .ret = 0 },
                { .in = "%{literal('this is test') | if('~', '.*test', 'yes', 'no')}", .out = "yes", .ret = 0 },
+#endif
                /* variable expansion */
                { .in = "%{alpha | if('eq', alpha, 'yes', 'no')}", .out = "yes", .ret = 0 },
                { .in = "%{alpha | if('eq', beta, 'yes', 'no')}", .out = "no", .ret = 0 },