SRS: fix encode operation for empty sender addresses.
[exim.git] / src / src / expand.c
index 85619acfef4c5994ac6cc5655ba664b41690e000..06dc58cb1c8c03f04867c2454f2e18b9c2d51ecd 100644 (file)
@@ -2,8 +2,8 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -3919,7 +3919,7 @@ Returns:       new pointer for expandable string, terminated if non-null
 */
 
 gstring *
-cat_file(FILE *f, gstring *yield, uschar *eol)
+cat_file(FILE * f, gstring * yield, uschar * eol)
 {
 uschar buffer[1024];
 
@@ -3931,8 +3931,6 @@ while (Ufgets(buffer, sizeof(buffer), f))
   if (eol && buffer[len])
     yield = string_cat(yield, eol);
   }
-
-(void) string_from_gstring(yield);
 return yield;
 }
 
@@ -3954,7 +3952,6 @@ while ((rc = tls_read(tls_ctx, buffer, sizeof(buffer))) > 0)
 /* We assume that all errors, and any returns of zero bytes,
 are actually EOF. */
 
-(void) string_from_gstring(yield);
 return yield;
 }
 #endif
@@ -5529,10 +5526,8 @@ while (*s)
     case EITEM_RUN:
       {
       FILE * f;
-      uschar * arg;
-      const uschar ** argv;
-      pid_t pid;
-      int fd_in, fd_out;
+      const uschar * arg, ** argv;
+      BOOL late_expand = TRUE;
 
       if ((expand_forbid & RDO_RUN) != 0)
         {
@@ -5540,17 +5535,45 @@ while (*s)
         goto EXPAND_FAILED;
         }
 
+      /* Handle options to the "run" */
+
+      while (*s == ',')
+       {
+       if (Ustrncmp(++s, "preexpand", 9) == 0)
+         { late_expand = FALSE; s += 9; }
+       else
+         {
+         const uschar * t = s;
+         while (isalpha(*++t)) ;
+         expand_string_message = string_sprintf("bad option '%.*s' for run",
+                                                 (int)(t-s), s);
+         goto EXPAND_FAILED;
+         }
+       }
       Uskip_whitespace(&s);
-      if (*s != '{')
+
+      if (*s != '{')                                   /*}*/
         {
        expand_string_message = US"missing '{' for command arg of run";
-       goto EXPAND_FAILED_CURLY;
+       goto EXPAND_FAILED_CURLY;                       /*"}*/
        }
-      if (!(arg = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
-       goto EXPAND_FAILED;
-      Uskip_whitespace(&s);
+      s++;
+
+      if (late_expand)         /* this is the default case */
+       {
+       int n = Ustrcspn(s, "}");
+       arg = skipping ? NULL : string_copyn(s, n);
+       s += n;
+       }
+      else
+       {
+       if (!(arg = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok)))
+         goto EXPAND_FAILED;
+       Uskip_whitespace(&s);
+       }
+                                                       /*{*/
       if (*s++ != '}')
-        {
+        {                                              /*{*/
        expand_string_message = US"missing '}' closing command arg of run";
        goto EXPAND_FAILED_CURLY;
        }
@@ -5562,13 +5585,17 @@ while (*s)
        }
       else
         {
+       int fd_in, fd_out;
+       pid_t pid;
+
         if (!transport_set_up_command(&argv,    /* anchor for arg list */
             arg,                                /* raw command */
-            FALSE,                              /* don't expand the arguments */
-            0,                                  /* not relevant when... */
-            NULL,                               /* no transporting address */
-            US"${run} expansion",               /* for error messages */
-            &expand_string_message))            /* where to put error message */
+           late_expand,                /* expand args if not already done */
+            0,                          /* not relevant when... */
+            NULL,                       /* no transporting address */
+           late_expand,                /* allow tainted args, when expand-after-split */
+            US"${run} expansion",       /* for error messages */
+            &expand_string_message))    /* where to put error message */
           goto EXPAND_FAILED;
 
         /* Create the child process, making it a group leader. */
@@ -5579,7 +5606,7 @@ while (*s)
           expand_string_message =
             string_sprintf("couldn't create child process: %s", strerror(errno));
           goto EXPAND_FAILED;
-          }
+         }
 
         /* Nothing is written to the standard input. */
 
@@ -6420,7 +6447,6 @@ while (*s)
        goto EXPAND_FAILED;                                             /*{{*/
       if (*s++ != '}')
         {
-       /*{*/
        expand_string_message =
          string_sprintf("missing '}' closing first arg of %s", name);
        goto EXPAND_FAILED_CURLY;
@@ -6917,68 +6943,73 @@ while (*s)
         case 3: goto EXPAND_FAILED;
         }
 
-      g = string_catn(g, US"SRS0=", 5);
-
-      /* ${l_4:${hmac{md5}{SRS_SECRET}{${lc:$return_path}}}}= */
-      hmac_md5(sub[0], string_copylc(sub[1]), cksum, sizeof(cksum));
-      g = string_catn(g, cksum, sizeof(cksum));
-      g = string_catn(g, US"=", 1);
-
-      /* ${base32:${eval:$tod_epoch/86400&0x3ff}}= */
+      if (sub[1] && *(sub[1]))
        {
-       struct timeval now;
-       unsigned long i;
-       gstring * h = NULL;
-
-       gettimeofday(&now, NULL);
-       for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5)
-         h = string_catn(h, &base32_chars[i & 0x1f], 1);
-       if (h) while (h->ptr > 0)
-         g = string_catn(g, &h->s[--h->ptr], 1);
-       }
-      g = string_catn(g, US"=", 1);
+       g = string_catn(g, US"SRS0=", 5);
 
-      /* ${domain:$return_path}=${local_part:$return_path} */
-       {
-        int start, end, domain;
-        uschar * t = parse_extract_address(sub[1], &expand_string_message,
-                                         &start, &end, &domain, FALSE);
-       uschar * s;
+       /* ${l_4:${hmac{md5}{SRS_SECRET}{${lc:$return_path}}}}= */
+       hmac_md5(sub[0], string_copylc(sub[1]), cksum, sizeof(cksum));
+       g = string_catn(g, cksum, sizeof(cksum));
+       g = string_catn(g, US"=", 1);
 
-        if (!t)
-         goto EXPAND_FAILED;
+       /* ${base32:${eval:$tod_epoch/86400&0x3ff}}= */
+         {
+         struct timeval now;
+         unsigned long i;
+         gstring * h = NULL;
 
-       if (domain > 0) g = string_cat(g, t + domain);
+         gettimeofday(&now, NULL);
+         for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5)
+           h = string_catn(h, &base32_chars[i & 0x1f], 1);
+         if (h) while (h->ptr > 0)
+           g = string_catn(g, &h->s[--h->ptr], 1);
+         }
        g = string_catn(g, US"=", 1);
 
-       s = domain > 0 ? string_copyn(t, domain - 1) : t;
-       if ((quoted = Ustrchr(s, '"') != NULL))
+       /* ${domain:$return_path}=${local_part:$return_path} */
          {
-         gstring * h = NULL;
-         DEBUG(D_expand) debug_printf_indent("auto-quoting local part\n");
-         while (*s)            /* de-quote */
+         int start, end, domain;
+         uschar * t = parse_extract_address(sub[1], &expand_string_message,
+                                           &start, &end, &domain, FALSE);
+         uschar * s;
+
+         if (!t)
+           goto EXPAND_FAILED;
+
+         if (domain > 0) g = string_cat(g, t + domain);
+         g = string_catn(g, US"=", 1);
+
+         s = domain > 0 ? string_copyn(t, domain - 1) : t;
+         if ((quoted = Ustrchr(s, '"') != NULL))
            {
-           while (*s && *s != '"') h = string_catn(h, s++, 1);
-           if (*s) s++;
-           while (*s && *s != '"') h = string_catn(h, s++, 1);
-           if (*s) s++;
+           gstring * h = NULL;
+           DEBUG(D_expand) debug_printf_indent("auto-quoting local part\n");
+           while (*s)          /* de-quote */
+             {
+             while (*s && *s != '"') h = string_catn(h, s++, 1);
+             if (*s) s++;
+             while (*s && *s != '"') h = string_catn(h, s++, 1);
+             if (*s) s++;
+             }
+           gstring_release_unused(h);
+           s = string_from_gstring(h);
            }
-         gstring_release_unused(h);
-         s = string_from_gstring(h);
+         g = string_cat(g, s);
          }
-       g = string_cat(g, s);
-        }
 
-      /* Assume that if the original local_part had quotes
-      it was for good reason */
+       /* Assume that if the original local_part had quotes
+       it was for good reason */
 
-      if (quoted) yield = string_catn(yield, US"\"", 1);
-      yield = string_catn(yield, g->s, g->ptr);
-      if (quoted) yield = string_catn(yield, US"\"", 1);
+       if (quoted) yield = string_catn(yield, US"\"", 1);
+       yield = string_catn(yield, g->s, g->ptr);
+       if (quoted) yield = string_catn(yield, US"\"", 1);
 
-      /* @$original_domain */
-      yield = string_catn(yield, US"@", 1);
-      yield = string_cat(yield, sub[2]);
+       /* @$original_domain */
+       yield = string_catn(yield, US"@", 1);
+       yield = string_cat(yield, sub[2]);
+       }
+      else
+       DEBUG(D_expand) debug_printf_indent("null return_path for srs-encode\n");
 
       if (skipping) continue;
       break;
@@ -7247,7 +7278,7 @@ NOT_ITEM: ;
            goto EXPAND_FAILED;
            }
 
-         exim_sha_update(&h, sub, Ustrlen(sub));
+         exim_sha_update_string(&h, sub);
          exim_sha_finish(&h, &b);
          while (b.len-- > 0)
            yield = string_fmt_append(yield, "%02X", *b.data++);
@@ -7275,7 +7306,7 @@ NOT_ITEM: ;
          goto EXPAND_FAILED;
          }
 
-       exim_sha_update(&h, sub, Ustrlen(sub));
+       exim_sha_update_string(&h, sub);
        exim_sha_finish(&h, &b);
        while (b.len-- > 0)
          yield = string_fmt_append(yield, "%02X", *b.data++);