Avoid the long whats_supported line being mixed with output from other processes
[exim.git] / src / src / expand.c
index cd4522afb378eb1a6a1ab71dbc1dd79412c703d2..789f09907f074e27ff65984337a56ef6f8665fc4 100644 (file)
@@ -589,7 +589,9 @@ static var_entry var_table[] = {
   { "local_part",          vtype_stringptr,   &deliver_localpart },
   { "local_part_data",     vtype_stringptr,   &deliver_localpart_data },
   { "local_part_prefix",   vtype_stringptr,   &deliver_localpart_prefix },
+  { "local_part_prefix_v", vtype_stringptr,   &deliver_localpart_prefix_v },
   { "local_part_suffix",   vtype_stringptr,   &deliver_localpart_suffix },
+  { "local_part_suffix_v", vtype_stringptr,   &deliver_localpart_suffix_v },
   { "local_part_verified", vtype_stringptr,   &deliver_localpart_verified },
 #ifdef HAVE_LOCAL_SCAN
   { "local_scan_data",     vtype_stringptr,   &local_scan_data },
@@ -1172,7 +1174,7 @@ static uschar *
 expand_getkeyed(uschar * key, const uschar * s)
 {
 int length = Ustrlen(key);
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 
 /* Loop to search for the key */
 
@@ -1184,14 +1186,13 @@ while (*s)
 
   while (*s && *s != '=' && !isspace(*s)) s++;
   dkeylength = s - dkey;
-  while (isspace(*s)) s++;
-  if (*s == '=') while (isspace((*(++s))));
+  if (Uskip_whitespace(&s) == '=') while (isspace((*(++s))));
 
   data = string_dequote(&s);
   if (length == dkeylength && strncmpic(key, dkey, length) == 0)
     return data;
 
-  while (isspace(*s)) s++;
+  Uskip_whitespace(&s);
   }
 
 return NULL;
@@ -1750,11 +1751,16 @@ return g ? g->s : NULL;
 static uschar *
 fn_queue_size(void)
 {
-struct sockaddr_un sun = {.sun_family = AF_UNIX};
+struct sockaddr_un sa_un = {.sun_family = AF_UNIX};
 uschar buf[16];
 int fd;
 ssize_t len;
 const uschar * where;
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+uschar * sname;
+#endif
+fd_set fds;
+struct timeval tv;
 
 if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
   {
@@ -1762,38 +1768,62 @@ if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
   return NULL;
   }
 
-#define ABSTRACT_CLIENT
-#ifdef ABSTRACT_CLIENT
-sun.sun_path[0] = 0;   /* Abstract local socket addr - Linux-specific? */
+#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
 len = offsetof(struct sockaddr_un, sun_path) + 1
-  + snprintf(sun.sun_path+1, sizeof(sun.sun_path)-1, "exim_%d", getpid());
+  + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "exim_%d", getpid());
 #else
+sname = string_sprintf("%s/p_%d", spool_directory, getpid());
 len = offsetof(struct sockaddr_un, sun_path)
-  + snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/p_%d",
-      spool_directory, getpid());
+  + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", sname);
 #endif
 
-if (bind(fd, &sun, len) < 0) { where = US"bind"; goto bad; }
+if (bind(fd, (const struct sockaddr *)&sa_un, len) < 0)
+  { where = US"bind"; goto bad; }
 
 #ifdef notdef
-debug_printf("local%s '%s'\n", *sun.sun_path ? "" : " abstract",
-  sun.sun_path+ (*sun.sun_path ? 0 : 1));
+debug_printf("local addr '%s%s'\n",
+  *sa_un.sun_path ? "" : "@",
+  sa_un.sun_path + (*sa_un.sun_path ? 0 : 1));
 #endif
 
-sun.sun_path[0] = 0;   /* Abstract local socket addr - Linux-specific? */
+#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
 len = offsetof(struct sockaddr_un, sun_path) + 1
-  + snprintf(sun.sun_path+1, sizeof(sun.sun_path)-1, "%s", NOTIFIER_SOCKET_NAME);
+  + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "%s",
+             expand_string(notifier_socket));
+#else
+len = offsetof(struct sockaddr_un, sun_path)
+  + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s",
+             expand_string(notifier_socket));
+#endif
 
-if (connect(fd, &sun, len) < 0) { where = US"connect"; goto bad; }
+if (connect(fd, (const struct sockaddr *)&sa_un, len) < 0)
+  { where = US"connect"; goto bad2; }
 
 buf[0] = NOTIFY_QUEUE_SIZE_REQ;
 if (send(fd, buf, 1, 0) < 0) { where = US"send"; goto bad; }
 
-if ((len = recv(fd, buf, sizeof(buf), 0)) < 0) { where = US"recv"; goto bad; }
+FD_ZERO(&fds); FD_SET(fd, &fds);
+tv.tv_sec = 2; tv.tv_usec = 0;
+if (select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tv) != 1)
+  {
+  DEBUG(D_expand) debug_printf("no daemon response; using local evaluation\n");
+  len = snprintf(CS buf, sizeof(buf), "%u", queue_count_cached());
+  }
+else if ((len = recv(fd, buf, sizeof(buf), 0)) < 0)
+  { where = US"recv"; goto bad2; }
 
 close(fd);
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+Uunlink(sname);
+#endif
 return string_copyn(buf, len);
 
+bad2:
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+  Uunlink(sname);
+#endif
 bad:
   close(fd);
   DEBUG(D_expand) debug_printf(" %s: %s\n", where, strerror(errno));
@@ -2013,7 +2043,7 @@ switch (vp->type)
     s = find_header(US"reply-to:", newsize,
                exists_only ? FH_EXISTS_ONLY|FH_WANT_RAW : FH_WANT_RAW,
                headers_charset);
-    if (s) while (isspace(*s)) s++;
+    if (s) Uskip_whitespace(&s);
     if (!s || !*s)
       {
       *newsize = 0;                            /* For the *s==0 case */
@@ -2024,8 +2054,8 @@ switch (vp->type)
     if (s)
       {
       uschar *t;
-      while (isspace(*s)) s++;
-      for (t = s; *t != 0; t++) if (*t == '\n') *t = ' ';
+      Uskip_whitespace(&s);
+      for (t = s; *t; t++) if (*t == '\n') *t = ' ';
       while (t > s && isspace(t[-1])) t--;
       *t = 0;
       }
@@ -2113,7 +2143,7 @@ read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping,
 {
 const uschar *s = *sptr;
 
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 for (int i = 0; i < n; i++)
   {
   if (*s != '{')
@@ -2130,7 +2160,7 @@ for (int i = 0; i < n; i++)
   if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok)))
     return 3;
   if (*s++ != '}') return 1;
-  while (isspace(*s)) s++;
+  Uskip_whitespace(&s);
   }
 if (check_end && *s++ != '}')
   {
@@ -2253,9 +2283,7 @@ uschar * p = s;
 unsigned depth = 0;
 BOOL quotesmode = wrap[0] == wrap[1];
 
-while (isspace(*p)) p++;
-
-if (*p == *wrap)
+if (Uskip_whitespace(&p) == *wrap)
   {
   s = ++p;
   wrap++;
@@ -3995,8 +4023,7 @@ int c;
 int_eximarith_t n;
 uschar *s = *sptr;
 
-while (isspace(*s)) s++;
-if (isdigit((c = *s)))
+if (isdigit((c = Uskip_whitespace(&s))))
   {
   int count;
   (void)sscanf(CS s, (decimal? SC_EXIM_DEC "%n" : SC_EXIM_ARITH "%n"), &n, &count);
@@ -4008,7 +4035,7 @@ if (isdigit((c = *s)))
     case 'm': n *= 1024*1024; s++; break;
     case 'g': n *= 1024*1024*1024; s++; break;
     }
-  while (isspace (*s)) s++;
+  Uskip_whitespace(&s);
   }
 else if (c == '(')
   {
@@ -4030,7 +4057,7 @@ eval_op_unary(uschar **sptr, BOOL decimal, uschar **error)
 {
 uschar *s = *sptr;
 int_eximarith_t x;
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 if (*s == '+' || *s == '-' || *s == '~')
   {
   int op = *s++;
@@ -4748,7 +4775,7 @@ while (*s != 0)
       int expand_setup = 0;
       int nameptr = 0;
       uschar *key, *filename;
-      const uschar *affix;
+      const uschar * affix, * opts;
       uschar *save_lookup_value = lookup_value;
       int save_expand_nmax =
         save_expand_strings(save_expand_nstring, save_expand_nlength);
@@ -4788,20 +4815,19 @@ while (*s != 0)
       kinds. Allow everything except space or { to appear; the actual content
       is checked by search_findtype_partial. */                /*}*/
 
-      while (*s != 0 && *s != '{' && !isspace(*s))     /*}*/
+      while (*s && *s != '{' && !isspace(*s))          /*}*/
         {
         if (nameptr < sizeof(name) - 1) name[nameptr++] = *s;
         s++;
         }
-      name[nameptr] = 0;
+      name[nameptr] = '\0';
       while (isspace(*s)) s++;
 
       /* Now check for the individual search type and any partial or default
       options. Only those types that are actually in the binary are valid. */
 
-      stype = search_findtype_partial(name, &partial, &affix, &affixlen,
-        &starflags);
-      if (stype < 0)
+      if ((stype = search_findtype_partial(name, &partial, &affix, &affixlen,
+         &starflags, &opts)) < 0)
         {
         expand_string_message = search_error_message;
         goto EXPAND_FAILED;
@@ -4861,16 +4887,13 @@ while (*s != 0)
         if (mac_islookup(stype, lookup_querystyle))
           filename = NULL;
         else
-          {
-          if (*filename != '/')
-            {
-            expand_string_message = string_sprintf(
-              "absolute file name expected for \"%s\" lookup", name);
-            goto EXPAND_FAILED;
-            }
-          while (*key != 0 && !isspace(*key)) key++;
-          if (*key != 0) *key++ = 0;
-          }
+          if (*filename == '/')
+           {
+           while (*key && !isspace(*key)) key++;
+           if (*key) *key++ = '\0';
+           }
+         else
+           filename = NULL;
         }
 
       /* If skipping, don't do the next bit - just lookup_value == NULL, as if
@@ -4897,7 +4920,7 @@ while (*s != 0)
           goto EXPAND_FAILED;
           }
         lookup_value = search_find(handle, filename, key, partial, affix,
-          affixlen, starflags, &expand_setup);
+          affixlen, starflags, &expand_setup, opts);
         if (f.search_find_defer)
           {
           expand_string_message =
@@ -5263,7 +5286,7 @@ while (*s != 0)
       {
       client_conn_ctx cctx;
       int timeout = 5;
-      int save_ptr = yield->ptr;
+      int save_ptr = gstring_length(yield);
       FILE * fp = NULL;
       uschar * arg;
       uschar * sub_arg[4];
@@ -5304,8 +5327,9 @@ while (*s != 0)
        uschar * item;
        int sep = 0;
 
-       item = string_nextinlist(&list, &sep, NULL, 0);
-        if ((timeout = readconf_readtime(item, 0, FALSE)) < 0)
+       if (  !(item = string_nextinlist(&list, &sep, NULL, 0))
+          || !*item
+          || (timeout = readconf_readtime(item, 0, FALSE)) < 0)
           {
           expand_string_message = string_sprintf("bad time value %s", item);
           goto EXPAND_FAILED;
@@ -5496,7 +5520,7 @@ while (*s != 0)
 
         if (sigalrm_seen)
           {
-          yield->ptr = save_ptr;
+          if (yield) yield->ptr = save_ptr;
           expand_string_message = US "socket read timed out";
           goto SOCK_FAIL;
           }
@@ -5593,7 +5617,8 @@ while (*s != 0)
 
         /* Create the child process, making it a group leader. */
 
-        if ((pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE)) < 0)
+        if ((pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE,
+                             US"expand-run")) < 0)
           {
           expand_string_message =
             string_sprintf("couldn't create child process: %s", strerror(errno));
@@ -5663,7 +5688,7 @@ while (*s != 0)
 
     case EITEM_TR:
       {
-      int oldptr = yield->ptr;
+      int oldptr = gstring_length(yield);
       int o2m;
       uschar *sub[3];
 
@@ -6402,7 +6427,7 @@ while (*s != 0)
     case EITEM_REDUCE:
       {
       int sep = 0;
-      int save_ptr = yield->ptr;
+      int save_ptr = gstring_length(yield);
       uschar outsep[2] = { '\0', '\0' };
       const uschar *list, *expr, *temp;
       uschar *save_iterate_item = iterate_item;
@@ -6549,7 +6574,8 @@ while (*s != 0)
         item of the output list, add in a space if the new item begins with the
         separator character, or is an empty string. */
 
-        if (yield->ptr != save_ptr && (temp[0] == *outsep || temp[0] == 0))
+        if (  yield && yield->ptr != save_ptr
+          && (temp[0] == *outsep || temp[0] == 0))
           yield = string_catn(yield, US" ", 1);
 
         /* Add the string in "temp" to the output list that we are building,
@@ -6589,7 +6615,7 @@ while (*s != 0)
       the redundant final separator. Even though an empty item at the end of a
       list does not count, this is tidier. */
 
-      else if (yield->ptr != save_ptr) yield->ptr--;
+      else if (yield && yield->ptr != save_ptr) yield->ptr--;
 
       /* Restore preserved $item */
 
@@ -7521,7 +7547,7 @@ while (*s != 0)
         {
         uschar outsep[2] = { ':', '\0' };
         uschar *address, *error;
-        int save_ptr = yield->ptr;
+        int save_ptr = gstring_length(yield);
         int start, end, domain;  /* Not really used */
 
         while (isspace(*sub)) sub++;
@@ -7552,7 +7578,7 @@ while (*s != 0)
 
           if (address)
             {
-            if (yield->ptr != save_ptr && address[0] == *outsep)
+            if (yield && yield->ptr != save_ptr && address[0] == *outsep)
               yield = string_catn(yield, US" ", 1);
 
             for (;;)
@@ -7581,7 +7607,7 @@ while (*s != 0)
         /* If we have generated anything, remove the redundant final
         separator. */
 
-        if (yield->ptr != save_ptr) yield->ptr--;
+        if (yield && yield->ptr != save_ptr) yield->ptr--;
         f.parse_allow_group = FALSE;
         continue;
         }
@@ -7600,7 +7626,7 @@ while (*s != 0)
       case EOP_QUOTE_LOCAL_PART:
       if (!arg)
         {
-        BOOL needs_quote = (*sub == 0);      /* TRUE for empty string */
+        BOOL needs_quote = (!*sub);      /* TRUE for empty string */
         uschar *t = sub - 1;
 
         if (c == EOP_QUOTE)
@@ -7720,10 +7746,10 @@ while (*s != 0)
 
       case EOP_FROM_UTF8:
         {
-        while (*sub != 0)
+       uschar * buff = store_get(4, is_tainted(sub));
+        while (*sub)
           {
           int c;
-          uschar buff[4];
           GETUTF8INC(c, sub);
           if (c > 255) c = '_';
           buff[0] = c;
@@ -7732,7 +7758,7 @@ while (*s != 0)
         continue;
         }
 
-         /* replace illegal UTF-8 sequences by replacement character  */
+      /* replace illegal UTF-8 sequences by replacement character  */
 
       #define UTF8_REPLACEMENT_CHAR US"?"
 
@@ -7744,7 +7770,17 @@ while (*s != 0)
         int complete;
         uschar seq_buff[4];                    /* accumulate utf-8 here */
 
-        while (*sub != 0)
+       /* Manually track tainting, as we deal in individual chars below */
+
+       if (is_tainted(sub))
+         if (yield->s && yield->ptr)
+           gstring_rebuffer(yield);
+         else
+           yield->s = store_get(yield->size = Ustrlen(sub), TRUE);
+
+       /* Check the UTF-8, byte-by-byte */
+
+        while (*sub)
          {
          complete = 0;
          uschar c = *sub++;
@@ -7770,7 +7806,7 @@ while (*s != 0)
            }
          else  /* no bytes left: new sequence */
            {
-           if((c & 0x80) == 0) /* 1-byte sequence, US-ASCII, keep it */
+           if(!(c & 0x80))     /* 1-byte sequence, US-ASCII, keep it */
              {
              yield = string_catn(yield, &c, 1);
              continue;
@@ -7815,9 +7851,8 @@ while (*s != 0)
         * Eg, ${length_1:フィル} is one byte, not one character, so we expect
         * ${utf8clean:${length_1:フィル}} to yield '?' */
         if (bytes_left != 0)
-          {
           yield = string_catn(yield, UTF8_REPLACEMENT_CHAR, 1);
-          }
+
         continue;
         }