tidying
[exim.git] / src / src / rewrite.c
index 6a6da47dfa0c70d4e3265fb85a033efe5a4f0a23..ce35c43655dd43fe8ed6f24e4a306d8fade3c12b 100644 (file)
@@ -2,8 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2021 - 2023 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* Functions concerned with rewriting headers */
 
@@ -102,7 +104,7 @@ rewrite_one(const uschar *s, int flag, BOOL *whole, BOOL add_header, uschar *nam
 {
 const uschar *yield = s;
 const uschar *subject = s;
-uschar *domain = NULL;
+const uschar *domain = NULL;
 BOOL done = FALSE;
 int rule_number = 1;
 int yield_start = 0, yield_end = 0;
@@ -117,9 +119,9 @@ for (rewrite_rule * rule = rewrite_rules;
   {
   int start, end, pdomain;
   int count = 0;
-  uschar *save_localpart;
-  const uschar *save_domain;
-  uschar *error, *new;
+  const uschar * save_localpart;
+  const uschar * save_domain;
+  uschar * error, * new;
   const uschar * newparsed;
 
   /* Come back here for a repeat after a successful rewrite. We do this
@@ -135,7 +137,8 @@ for (rewrite_rule * rule = rewrite_rules;
 
   if (flag & rewrite_smtp)
     {
-    uschar *key = expand_string(rule->key);
+    BOOL textonly_re;
+    const uschar * key = expand_string_2(rule->key, &textonly_re);
     if (!key)
       {
       if (!f.expand_string_forcedfail)
@@ -143,7 +146,8 @@ for (rewrite_rule * rule = rewrite_rules;
           "checking for SMTP rewriting: %s", rule->key, expand_string_message);
       continue;
       }
-    if (match_check_string(subject, key, 0, TRUE, FALSE, FALSE, NULL) != OK)
+    if (match_check_string(subject, key, 0,
+      textonly_re ? MCS_CACHEABLE | MCS_PARTIAL : MCS_PARTIAL, NULL) != OK)
       continue;
     new = expand_string(rule->replacement);
     }
@@ -154,7 +158,7 @@ for (rewrite_rule * rule = rewrite_rules;
 
   else
     {
-    if (!domain) domain = Ustrrchr(subject, '@') + 1;
+    if (!domain) domain = CUstrrchr(subject, '@') + 1;
 
     /* Use the general function for matching an address against a list (here
     just one item, so use the "impossible value" separator UCHAR_MAX+1). */
@@ -178,16 +182,14 @@ for (rewrite_rule * rule = rewrite_rules;
     save_domain = deliver_domain;
 
     /* We have subject pointing to "localpart@domain" and domain pointing to
-    the domain. Temporarily terminate the local part so that it can be
-    set up as an expansion variable */
+    the domain. Split into local part and domain so that it can be set up as 
+    an expansion variable */
 
-    domain[-1] = 0;
-    deliver_localpart = US subject;
+    deliver_localpart = US string_copyn(subject, domain-subject-1);
     deliver_domain = domain;
 
     new = expand_string(rule->replacement);
 
-    domain[-1] = '@';
     deliver_localpart = save_localpart;
     deliver_domain = save_domain;
     }
@@ -446,14 +448,14 @@ rewrite_one_header(header_line *h, int flag,
   rewrite_rule *rewrite_rules, int existflags, BOOL replace)
 {
 int lastnewline = 0;
-header_line *newh = NULL;
+header_line * newh = NULL;
 rmark function_reset_point = store_mark();
-uschar *s = Ustrchr(h->text, ':') + 1;
+uschar * s = Ustrchr(h->text, ':') + 1;
 
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 
-DEBUG(D_rewrite)
-  debug_printf("rewrite_one_header: type=%c:\n  %s", h->type, h->text);
+DEBUG(D_rewrite)       /* The header text includes the trailing newline */
+  debug_printf_indent("rewrite_one_header: type=%c:\n  %s", h->type, h->text);
 
 f.parse_allow_group = TRUE;     /* Allow group syntax */
 
@@ -466,25 +468,32 @@ We want to avoid keeping store for any intermediate versions. */
 
 while (*s)
   {
-  uschar *sprev;
-  uschar *ss = parse_find_address_end(s, FALSE);
-  uschar *recipient, *new;
+  uschar * sprev = s;
+  uschar * ss = parse_find_address_end(s, FALSE), * ss1 = ss;
+  uschar * recipient, * new;
   rmark loop_reset_point = store_mark();
-  uschar *errmess = NULL;
+  uschar * errmess = NULL;
   BOOL changed = FALSE;
-  int terminator = *ss;
+  uschar terminator = *ss;
   int start, end, domain;
 
+  /* If we hit the end of the header, trim trailing newline and whitespace */
+
+  if (!terminator)
+    {
+    while (ss1 > s && isspace(ss1[-1])) ss1--;
+    terminator = *ss1;
+    }
+
   /* Temporarily terminate the string at this point, and extract the
   operative address within. Then put back the terminator and prepare for
   the next address, saving the start of the old one. */
 
-  *ss = 0;
+  *ss1 = '\0';
   recipient = parse_extract_address(s, &errmess, &start, &end, &domain, FALSE);
-  *ss = terminator;
-  sprev = s;
-  s = ss + (terminator ? 1 :0);
-  while (isspace(*s)) s++;
+  *ss1 = terminator;
+  s = ss + (*ss ? 1 : 0);
+  Uskip_whitespace(&s);
 
   /* There isn't much we can do for syntactic disasters at this stage.
   Pro tem (possibly for ever) ignore them.
@@ -492,30 +501,16 @@ while (*s)
   empty address, overlong addres. Sometimes the result matters, sometimes not.
   It seems this function is called for *any* header we see. */
 
-
   if (!recipient)
     {
-#if 0
-    /* FIXME:
-    This was(!) an attempt tho handle empty rewrits, but seemingly it
-    needs more effort to decide if the returned empty address matters.
-    Now this will now break test 471 again.
-
-    471 fails now because it uses an overlong address, for wich parse_extract_address()
-    returns an empty address (which was not expected).
-
-    Checking the output and exit if rewrite_rules or routed_old are present
-    isn't a good idea either: It's enough to have *any* rewrite rule
-    in the configuration plus "To: undisclosed recpients:;" to exit(), which
-    is not what we want.
-    */
-
-    if (rewrite_rules || routed_old)
-      {
-      log_write(0, LOG_MAIN, "rewrite: %s", errmess);
-      exim_exit(EXIT_FAILURE);
-      }
-#endif
+    /* Log unparesable addresses in the header. Slightly ugly because a
+    null output from the extract can also result from a header without an
+    address, "To: undisclosed recpients:;" being the classic case. Ignore
+    this one and carry on. */
+
+    if (Ustrcmp(errmess, "empty address") != 0)
+      log_write(0, LOG_MAIN, "qualify/rewrite: %s", errmess);
+
     loop_reset_point = store_reset(loop_reset_point);
     continue;
     }
@@ -597,7 +592,8 @@ while (*s)
   point, because we may have a rewritten line from a previous time round the
   loop. */
 
-  if (!changed) loop_reset_point = store_reset(loop_reset_point);
+  if (!changed)
+    loop_reset_point = store_reset(loop_reset_point);
 
   /* If the address has changed, create a new header containing the
   rewritten address. We do not need to set the chain pointers at this
@@ -615,7 +611,7 @@ while (*s)
     int oldlen = end - start;
 
     header_line * prev = newh ? newh : h;
-    uschar * newt = store_get_perm(prev->slen - oldlen + newlen + 4, TRUE);
+    uschar * newt = store_get_perm(prev->slen - oldlen + newlen + 4, GET_TAINTED);
     uschar * newtstart = newt;
 
     int type = prev->type;
@@ -679,7 +675,7 @@ while (*s)
 
     store_reset(function_reset_point);
     function_reset_point = store_mark();
-    newh = store_get(sizeof(header_line), FALSE);
+    newh = store_get(sizeof(header_line), GET_UNTAINTED);
     newh->type = type;
     newh->slen = slen;
     newh->text = string_copyn(newtstart, slen);