Taint: track in ${utf8clean:} operator
[exim.git] / src / src / expand.c
index 427effedf4606b2b5dc28e1767425672501add6c..3c3184347871c178e174c4a37f7605eade728f75 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 },
@@ -1750,11 +1752,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,40 +1769,61 @@ 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, (const struct sockaddr *)&sun, len) < 0)
+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", NOTIFIER_SOCKET_NAME);
+#else
+len = offsetof(struct sockaddr_un, sun_path)
+  + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s/%s",
+               spool_directory, NOTIFIER_SOCKET_NAME);
+#endif
 
-if (connect(fd, (const struct sockaddr *)&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));
@@ -5265,7 +5293,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];
@@ -5498,7 +5526,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;
           }
@@ -5665,7 +5693,7 @@ while (*s != 0)
 
     case EITEM_TR:
       {
-      int oldptr = yield->ptr;
+      int oldptr = gstring_length(yield);
       int o2m;
       uschar *sub[3];
 
@@ -6404,7 +6432,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;
@@ -6551,7 +6579,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,
@@ -6591,7 +6620,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 */
 
@@ -7523,7 +7552,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++;
@@ -7554,7 +7583,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 (;;)
@@ -7583,7 +7612,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;
         }
@@ -7602,7 +7631,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)
@@ -7722,10 +7751,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;
@@ -7734,7 +7763,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"?"
 
@@ -7746,7 +7775,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++;
@@ -7772,7 +7811,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;
@@ -7817,9 +7856,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;
         }