Merge branch 'master' of ssh://git.exim.org/home/git/exim
authorTodd Lyons <tlyons@exim.org>
Tue, 15 Apr 2014 16:53:43 +0000 (09:53 -0700)
committerTodd Lyons <tlyons@exim.org>
Tue, 15 Apr 2014 16:53:43 +0000 (09:53 -0700)
Fixed Conflicts:
doc/doc-txt/ChangeLog

22 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
src/OS/Makefile-Base
src/src/dns.c
src/src/exim.h
src/src/expand.c
src/src/functions.h
src/src/lookups/dnsdb.c
src/src/readconf.c
src/src/routers/rf_get_munge_headers.c
src/src/string.c
src/src/transport.c
test/confs/0096
test/confs/0097
test/confs/0166
test/mail/0046.userx
test/mail/0166.userx
test/mail/0166.usery
test/mail/0351.userx
test/mail/0412.CALLER
test/scripts/0000-Basic/0481
test/stderr/5400

index 04678b4808c3e40ef28177174f20893c901fbb74..86090290bffd91218f1aec30ea426401e4db30d5 100644 (file)
@@ -6840,7 +6840,7 @@ is used on its own as the result. If the lookup does not succeed, the
 &`fail`& keyword causes a &'forced expansion failure'& &-- see section
 &<<SECTforexpfai>>& for an explanation of what this means.
 
-The supported DNS record types are A, CNAME, MX, NS, PTR, SPF, SRV, and TXT,
+The supported DNS record types are A, CNAME, MX, NS, PTR, SPF, SRV, TLSA and TXT,
 and, when Exim is compiled with IPv6 support, AAAA (and A6 if that is also
 configured). If no type is given, TXT is assumed. When the type is PTR,
 the data can be an IP address, written as normal; inversion and the addition of
@@ -16727,11 +16727,12 @@ and the discussion in chapter &<<CHAPenvironment>>&.
 
 
 
-.option headers_add routers string&!! unset
+.option headers_add routers list&!! unset
 .cindex "header lines" "adding"
 .cindex "router" "adding header lines"
-This option specifies a string of text that is expanded at routing time, and
-associated with any addresses that are accepted by the router. However, this
+This option specifies a list of text headers, newline-separated,
+that is associated with any addresses that are accepted by the router.
+Each item is separately expanded, at routing time.  However, this
 option has no effect when an address is just being verified. The way in which
 the text is used to add header lines at transport time is described in section
 &<<SECTheadersaddrem>>&. New header lines are not actually added until the
@@ -16740,8 +16741,8 @@ header lines in string expansions in the transport's configuration do not
 &"see"& the added header lines.
 
 The &%headers_add%& option is expanded after &%errors_to%&, but before
-&%headers_remove%& and &%transport%&. If the expanded string is empty, or if
-the expansion is forced to fail, the option has no effect. Other expansion
+&%headers_remove%& and &%transport%&. If an item is empty, or if
+an item expansion is forced to fail, the item has no effect. Other expansion
 failures are treated as configuration errors.
 
 Unlike most options, &%headers_add%& can be specified multiple times
@@ -16763,11 +16764,12 @@ avoided. The &%repeat_use%& option of the &%redirect%& router may be of help.
 
 
 
-.option headers_remove routers string&!! unset
+.option headers_remove routers list&!! unset
 .cindex "header lines" "removing"
 .cindex "router" "removing header lines"
-This option specifies a string of text that is expanded at routing time, and
-associated with any addresses that are accepted by the router. However, this
+This option specifies a list of text headers, colon-separated,
+that is associated with any addresses that are accepted by the router.
+Each item is separately expanded, at routing time.  However, this
 option has no effect when an address is just being verified. The way in which
 the text is used to remove header lines at transport time is described in
 section &<<SECTheadersaddrem>>&. Header lines are not actually removed until
@@ -16776,8 +16778,8 @@ to header lines in string expansions in the transport's configuration still
 &"see"& the original header lines.
 
 The &%headers_remove%& option is expanded after &%errors_to%& and
-&%headers_add%&, but before &%transport%&. If the expansion is forced to fail,
-the option has no effect. Other expansion failures are treated as configuration
+&%headers_add%&, but before &%transport%&. If an item expansion is forced to fail,
+the item has no effect. Other expansion failures are treated as configuration
 errors.
 
 Unlike most options, &%headers_remove%& can be specified multiple times
@@ -19777,10 +19779,11 @@ value that the router supplies, and also overriding any value associated with
 &%user%& (see below).
 
 
-.option headers_add transports string&!! unset
+.option headers_add transports list&!! unset
 .cindex "header lines" "adding in transport"
 .cindex "transport" "header lines; adding"
-This option specifies a string of text that is expanded and added to the header
+This option specifies a list of text headers, newline-separated,
+which are (separately) expanded and added to the header
 portion of a message as it is transported, as described in section
 &<<SECTheadersaddrem>>&. Additional header lines can also be specified by
 routers. If the result of the expansion is an empty string, or if the expansion
@@ -19801,18 +19804,20 @@ transports, the settings of &%message_prefix%& and &%message_suffix%& should be
 checked, since this option does not automatically suppress them.
 
 
-.option headers_remove transports string&!! unset
+.option headers_remove transports list&!! unset
 .cindex "header lines" "removing"
 .cindex "transport" "header lines; removing"
-This option specifies a string that is expanded into a list of header names;
+This option specifies a list of header names, colon-separated;
 these headers are omitted from the message as it is transported, as described
 in section &<<SECTheadersaddrem>>&. Header removal can also be specified by
-routers. If the result of the expansion is an empty string, or if the expansion
+routers.
+Each list item is separately expanded.
+If the result of the expansion is an empty string, or if the expansion
 is forced to fail, no action is taken. Other expansion failures are treated as
 errors and cause the delivery to be deferred.
 
 Unlike most options, &%headers_remove%& can be specified multiple times
-for a router; all listed headers are added.
+for a router; all listed headers are removed.
 
 
 
@@ -31963,7 +31968,7 @@ they do not affect the values of the variables that refer to header lines.
 the transport cannot refer to the modified header lines, because such
 expansions all occur before the message is actually transported.
 
-For both routers and transports, the result of expanding a &%headers_add%&
+For both routers and transports, the argument of a &%headers_add%&
 option must be in the form of one or more RFC 2822 header lines, separated by
 newlines (coded as &"\n"&). For example:
 .code
@@ -31973,10 +31978,10 @@ headers_add = X-added-header: added by $primary_hostname\n\
 Exim does not check the syntax of these added header lines.
 
 Multiple &%headers_add%& options for a single router or transport can be
-specified; the values will be concatenated (with a separating newline
-added) before expansion.
+specified; the values will append to a single list of header lines.
+Each header-line is separately expanded.
 
-The result of expanding &%headers_remove%& must consist of a colon-separated
+The argument of a &%headers_remove%& option must consist of a colon-separated
 list of header names. This is confusing, because header names themselves are
 often terminated by colons. In this case, the colons are the list separators,
 not part of the names. For example:
@@ -31985,11 +31990,12 @@ headers_remove = return-receipt-to:acknowledge-to
 .endd
 
 Multiple &%headers_remove%& options for a single router or transport can be
-specified; the values will be concatenated (with a separating colon
-added) before expansion.
+specified; the arguments will append to a single header-names list.
+Each item is separately expanded.
 
-When &%headers_add%& or &%headers_remove%& is specified on a router, its value
-is expanded at routing time, and then associated with all addresses that are
+When &%headers_add%& or &%headers_remove%& is specified on a router,
+items are expanded at routing time,
+and then associated with all addresses that are
 accepted by that router, and also with any new addresses that it generates. If
 an address passes through several routers as a result of aliasing or
 forwarding, the changes are cumulative.
index c459a0f40d2ebc2cacb2fb9613ed4995b1d0060b..17e8091cefcf938f96c86b817b1bd60b8d663a9c 100644 (file)
@@ -66,6 +66,15 @@ JH/10 Bugzilla 1005: ACL "condition =" should accept values which are negative
 
 TL/06 Remove duplicated language in spec file from 4.82 TL/16.
 
+JH/11 Add dnsdb tlsa lookup.  From Todd Lyons.
+
+JH/12 Expand items in router/transport headers_add or headers_remove lists
+      individually rather than the list as a whole.  Bug 1452.
+
+      Required for reasonable handling of multiple headers_ options when
+      they may be empty; requires that headers_remove items with embedded
+      colons must have them doubled (or the list-separator changed).
+
 
 Exim version 4.82
 -----------------
index f0edbf13273360df45120d9e6ced90d01a2ecdd4..8209969f610a4d020047bbe599cc1dcec303ee42 100644 (file)
@@ -316,8 +316,8 @@ OBJ_EXIM = acl.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
         local_scan.o $(EXIM_PERL) $(OBJ_WITH_CONTENT_SCAN) \
         $(OBJ_WITH_OLD_DEMIME) $(OBJ_EXPERIMENTAL)
 
-exim:   lookups/lookups.a auths/auths.a pdkim/pdkim.a \
-        routers/routers.a transports/transports.a \
+exim:   buildlookups buildauths pdkim/pdkim.a \
+        buildrouters buildtransports \
         $(OBJ_EXIM) version.o
        @echo "$(LNCC) -o exim"
        $(FE)$(PURIFY) $(LNCC) -o exim $(LFLAGS) $(OBJ_EXIM) version.o \
@@ -354,7 +354,7 @@ exim_dumpdb: $(OBJ_DUMPDB)
 
 OBJ_FIXDB = exim_fixdb.o util-os.o util-store.o
 
-exim_fixdb:  $(OBJ_FIXDB) auths/auths.a
+exim_fixdb:  $(OBJ_FIXDB) buildauths
        @echo "$(LNCC) -o exim_fixdb"
        $(FE)$(LNCC) $(CFLAGS) $(INCLUDE) -o exim_fixdb $(LFLAGS) $(OBJ_FIXDB) \
          auths/auths.a $(LIBS) $(EXTRALIBS) $(DBMLIB)
@@ -620,7 +620,7 @@ drtables.o:      $(HDRS) drtables.c
 # When using parallel make, we don't have the dependency to force building
 # in the sub-directory unless we force that dependency:
 
-$(OBJ_LOOKUPS):  lookups/lookups.a
+$(OBJ_LOOKUPS):  buildlookups
 
 # The exim monitor's private modules - the sources live in a private
 # subdirectory. The final binary combines the private modules with some
@@ -648,8 +648,7 @@ $(MONBIN): $(HDRS)
 
 # The lookups library.
 
-buildlookups: lookups/lookups.a
-lookups/lookups.a: config.h version.h
+buildlookups:
         @cd lookups && $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" CFLAGS="$(CFLAGS)" \
           CFLAGS_DYNAMIC="$(CFLAGS_DYNAMIC)" HDRS="../version.h $(PHDRS)" \
           FE="$(FE)" RANLIB="$(RANLIB)" RM_COMMAND="$(RM_COMMAND)" \
@@ -658,8 +657,7 @@ lookups/lookups.a: config.h version.h
 
 # The routers library.
 
-buildrouters: routers/routers.a
-routers/routers.a: config.h
+buildrouters:
         @cd routers && $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" CFLAGS="$(CFLAGS)" \
           FE="$(FE)" RANLIB="$(RANLIB)" RM_COMMAND="$(RM_COMMAND)" HDRS="$(PHDRS)" \
           INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"
@@ -667,8 +665,7 @@ routers/routers.a: config.h
 
 # The transports library.
 
-buildtransports: transports/transports.a
-transports/transports.a: config.h
+buildtransports:
         @cd transports && $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" CFLAGS="$(CFLAGS)" \
           FE="$(FE)" RANLIB="$(RANLIB)" RM_COMMAND="$(RM_COMMAND)" HDRS="$(PHDRS)" \
           INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"
@@ -676,8 +673,7 @@ transports/transports.a: config.h
 
 # The library of authorization modules
 
-buildauths: auths/auths.a
-auths/auths.a: config.h
+buildauths:
         @cd auths && $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" CFLAGS="$(CFLAGS)" \
           FE="$(FE)" RANLIB="$(RANLIB)" RM_COMMAND="$(RM_COMMAND)" HDRS="$(PHDRS)" \
           INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"
index 88fa36baa2eaddd56a4f58ccb528a0030cf57e08..2aeb5af62ae9c92bdaf820dce2ec67a93b987e1e 100644 (file)
@@ -479,6 +479,7 @@ switch(t)
   case T_SRV:   return US"SRV";
   case T_NS:    return US"NS";
   case T_CNAME: return US"CNAME";
+  case T_TLSA:  return US"TLSA";
   default:      return US"?";
   }
 }
index b2d47d74e2ff03663e8866f3100470481fceef9e..c72c1f10a1f4644bffa99e4e8745f7fb4d24b5e6 100644 (file)
@@ -321,6 +321,12 @@ header files. I don't suppose they have T_SRV either. */
 #define T_SPF 99
 #endif
 
+/* New TLSA record for DANE */
+#ifndef T_TLSA
+#define T_TLSA 52
+#endif
+#define MAX_TLSA_EXPANDED_SIZE 8192
+
 /* It seems that some versions of arpa/nameser.h don't define *any* of the
 T_xxx macros, which seem to be non-standard nowadays. Just to be on the safe
 side, put in definitions for all the ones that Exim uses. */
index 64a3a86e6156a5b3282dc475f67fa0023de579d8..7a3252eaa17fa4f97aa44f77fcea4f5b8479863b 100644 (file)
@@ -6189,8 +6189,13 @@ while (*s != 0)
              }
            if((c & 0xe0) == 0xc0)              /* 2-byte sequence */
              {
-             bytes_left = 1;
-             codepoint = c & 0x1f;
+             if(c == 0xc0 || c == 0xc1)        /* 0xc0 and 0xc1 are illegal */
+               complete = -1;
+             else
+               {
+                 bytes_left = 1;
+                 codepoint = c & 0x1f;
+               }
              }
            else if((c & 0xf0) == 0xe0)         /* 3-byte sequence */
              {
index 3959615305b62da045033cc34f781fb3b30b6252..35500a1658899d4b3c5ba1927bfeee0a0b97a8d1 100644 (file)
@@ -350,6 +350,7 @@ extern int     stdin_feof(void);
 extern int     stdin_ferror(void);
 extern int     stdin_ungetc(int);
 extern uschar *string_append(uschar *, int *, int *, int, ...);
+extern uschar *string_append_listele(uschar *, uschar, const uschar *);
 extern uschar *string_base62(unsigned long int);
 extern uschar *string_cat(uschar *, int *, int *, const uschar *, int);
 extern uschar *string_copy_dnsdomain(uschar *);
index a8eab2e473599035dda9e71e369963e1fe331fe7..beba0950801af6722305a1c24bd72f9f20060ef0 100644 (file)
@@ -22,6 +22,11 @@ header files. */
 #define T_SPF 99
 #endif
 
+/* New TLSA record for DANE */
+#ifndef T_TLSA
+#define T_TLSA 52
+#endif
+
 /* Table of recognized DNS record types and their integer values. */
 
 static const char *type_names[] = {
@@ -41,6 +46,7 @@ static const char *type_names[] = {
   "ptr",
   "spf",
   "srv",
+  "tlsa",
   "txt",
   "zns"
 };
@@ -62,6 +68,7 @@ static int type_values[] = {
   T_PTR,
   T_SPF,
   T_SRV,
+  T_TLSA,
   T_TXT,
   T_ZNS      /* Private type for "zone nameservers" */
 };
@@ -378,6 +385,29 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer)))
             }
           }
         }
+      else if (type == T_TLSA)
+        {
+        uint8_t usage, selector, matching_type;
+        uint16_t i, payload_length;
+        uschar s[MAX_TLSA_EXPANDED_SIZE];
+       uschar * sp = s;
+        uschar *p = (uschar *)(rr->data);
+
+        usage = *p++;
+        selector = *p++;
+        matching_type = *p++;
+        /* What's left after removing the first 3 bytes above */
+        payload_length = rr->size - 3;
+        sp += sprintf(CS s, "%d %d %d ", usage, selector, matching_type);
+        /* Now append the cert/identifier, one hex char at a time */
+        for (i=0;
+             i < payload_length && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4);
+             i++)
+          {
+          sp += sprintf(CS sp, "%02x", (unsigned char)p[i]);
+          }
+        yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
+        }
       else   /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SRV */
         {
         int priority, weight, port;
index b41d22d2a30c3aa6e21bf3072083b80ee760b0db..81e3bc267c7bfe75e552dae4be441fc7b34ddbee 100644 (file)
@@ -1562,15 +1562,21 @@ switch (type)
       Because we only do this once, near process start-up, I'm prepared to
       let this slide for the time being, even though it rankles.  */
       }
-    else if (*str_target && (ol->type & opt_rep_str))
-     {
+    else if (ol->type & opt_rep_str)
+      {
       uschar sep = Ustrncmp(name, "headers_add", 11)==0 ? '\n' : ':';
-      saved_condition = *str_target;
-      strtemp = saved_condition + Ustrlen(saved_condition)-1;
-      if (*strtemp == sep) *strtemp = 0;       /* eliminate trailing list-sep */
-      strtemp = string_sprintf("%s%c%s", saved_condition, sep, sptr);
-      *str_target = string_copy_malloc(strtemp);
-     }
+      uschar * cp;
+
+      /* Strip trailing whitespace and seperators */
+      for (cp = sptr + Ustrlen(sptr) - 1;
+         cp >= sptr && (*cp == '\n' || *cp == '\t' || *cp == ' ' || *cp == sep);
+         cp--) *cp = '\0';
+
+      if (cp >= sptr)
+       *str_target = string_copy_malloc(
+                     *str_target ? string_sprintf("%s%c%s", *str_target, sep, sptr)
+                                 : sptr);
+      }
     else
       {
       *str_target = sptr;
@@ -4131,4 +4137,6 @@ while(next_section[0] != 0)
 (void)fclose(config_file);
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of readconf.c */
index 76e87e9c72f1f1f2a3e4139b1e7200f6af37708e..c44ba70b8ec400d1fa934c87ac817e3a4003afd3 100644 (file)
@@ -13,7 +13,7 @@
 *      Get additional headers for a router       *
 *************************************************/
 
-/* This function is called by both routers to sort out the additional headers
+/* This function is called by routers to sort out the additional headers
 and header remove list for a particular address.
 
 Arguments:
@@ -32,83 +32,78 @@ rf_get_munge_headers(address_item *addr, router_instance *rblock,
   header_line **extra_headers, uschar **remove_headers)
 {
 /* Default is to retain existing headers */
-
 *extra_headers = addr->p.extra_headers;
 
-if (rblock->extra_headers != NULL)
+if (rblock->extra_headers)
   {
-  header_line *h;
-  uschar *s = expand_string(rblock->extra_headers);
+  uschar * list = rblock->extra_headers;
+  int sep = '\n';
+  uschar * s;
+  int slen;
 
-  if (s == NULL)
-    {
-    if (!expand_string_forcedfail)
+  while ((s = string_nextinlist(&list, &sep, NULL, 0)))
+    if (!(s = expand_string(s)))
       {
-      addr->message = string_sprintf("%s router failed to expand \"%s\": %s",
-        rblock->name, rblock->extra_headers, expand_string_message);
-      return DEFER;
+      if (!expand_string_forcedfail)
+       {
+       addr->message = string_sprintf("%s router failed to expand \"%s\": %s",
+         rblock->name, rblock->extra_headers, expand_string_message);
+       return DEFER;
+       }
       }
-    }
-
-  /* Expand succeeded. Put extra header at the start of the chain because
-  further down it may point to headers from other routers, which may be
-  shared with other addresses. The output function outputs them in reverse
-  order. */
-
-  else
-    {
-    int slen = Ustrlen(s);
-    if (slen > 0)
+    else if ((slen = Ustrlen(s)) > 0)
       {
-      h = store_get(sizeof(header_line));
+      /* Expand succeeded. Put extra headers at the start of the chain because
+      further down it may point to headers from other routers, which may be
+      shared with other addresses. The output function outputs them in reverse
+      order. */
+
+      header_line *  h = store_get(sizeof(header_line));
 
       /* We used to use string_sprintf() to add the newline if needed, but that
       causes problems if the header line is exceedingly long (e.g. adding
       something to a pathologically long line). So avoid it. */
 
       if (s[slen-1] == '\n')
-        {
-        h->text = s;
-        }
+       h->text = s;
       else
-        {
-        h->text = store_get(slen+2);
-        memcpy(h->text, s, slen);
-        h->text[slen++] = '\n';
-        h->text[slen] = 0;
-        }
-
-      h->next = addr->p.extra_headers;
+       {
+       h->text = store_get(slen+2);
+       memcpy(h->text, s, slen);
+       h->text[slen++] = '\n';
+       h->text[slen] = 0;
+       }
+
+      h->next = *extra_headers;
       h->type = htype_other;
       h->slen = slen;
       *extra_headers = h;
       }
-    }
   }
 
 /* Default is to retain existing removes */
-
 *remove_headers = addr->p.remove_headers;
 
-if (rblock->remove_headers != NULL)
+/* Expand items from colon-sep list separately, then build new list */
+if (rblock->remove_headers)
   {
-  uschar *s = expand_string(rblock->remove_headers);
-  if (s == NULL)
-    {
-    if (!expand_string_forcedfail)
+  uschar * list = rblock->remove_headers;
+  int sep = ':';
+  uschar * s;
+  uschar buffer[128];
+
+  while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+    if (!(s = expand_string(s)))
       {
-      addr->message = string_sprintf("%s router failed to expand \"%s\": %s",
-        rblock->name, rblock->remove_headers, expand_string_message);
-      return DEFER;
+      if (!expand_string_forcedfail)
+       {
+       addr->message = string_sprintf("%s router failed to expand \"%s\": %s",
+         rblock->name, rblock->remove_headers, expand_string_message);
+       return DEFER;
+       }
       }
-    }
-  else if (*s != 0)
-    {
-    if (addr->p.remove_headers == NULL)
-      *remove_headers = s;
-    else
-      *remove_headers = string_sprintf("%s : %s", addr->p.remove_headers, s);
-    }
+    else if (*s)
+      *remove_headers = string_append_listele(*remove_headers, ':', s);
   }
 
 return OK;
index 6f54cc6244d03862dc069c321e68faa7d59e86cf..eb73fae3ec41ce315f66fd1f8d8063cf87087ca2 100644 (file)
@@ -966,6 +966,50 @@ return buffer;
 #endif  /* COMPILE_UTILITY */
 
 
+#ifndef COMPILE_UTILITY
+/************************************************
+*      Add element to seperated list           *
+************************************************/
+/* This function is used to build a list, returning
+an allocated null-terminated growable string. The
+given element has any embedded seperator characters
+doubled.
+
+Arguments:
+  list points to the start of the list that is being built, or NULL
+       if this is a new list that has no contents yet
+  sep  list seperator charactoer
+  ele  new lement to be appended to the list
+
+Returns:  pointer to the start of the list, changed if copied for expansion.
+*/
+
+uschar *
+string_append_listele(uschar * list, uschar sep, const uschar * ele)
+{
+uschar * new = NULL;
+int sz = 0, off = 0;
+uschar * sp;
+
+if (list)
+  {
+  new = string_cat(new, &sz, &off, list, Ustrlen(list));
+  new = string_cat(new, &sz, &off, &sep, 1);
+  }
+
+while (sp = Ustrchr(ele, sep))
+  {
+  new = string_cat(new, &sz, &off, ele, sp-ele+1);
+  new = string_cat(new, &sz, &off, &sep, 1);
+  ele = sp+1;
+  }
+new = string_cat(new, &sz, &off, ele, Ustrlen(ele));
+new[off] = '\0';
+return new;
+}
+#endif  /* COMPILE_UTILITY */
+
+
 
 #ifndef COMPILE_UTILITY
 /*************************************************
index 549da469423d3c84fed4642c6677db18cfd4e113..d4495393bccc551a9bf27bb054034f90e2cc0fb3 100644 (file)
@@ -626,19 +626,9 @@ header_line *h;
 /* Then the message's headers. Don't write any that are flagged as "old";
 that means they were rewritten, or are a record of envelope rewriting, or
 were removed (e.g. Bcc). If remove_headers is not null, skip any headers that
-match any entries therein. Then check addr->p.remove_headers too, provided that
-addr is not NULL. */
-
-if (remove_headers)
-  {
-  uschar *s = expand_string(remove_headers);
-  if (!s && !expand_string_forcedfail)
-    {
-    errno = ERRNO_CHHEADER_FAIL;
-    return FALSE;
-    }
-  remove_headers = s;
-  }
+match any entries therein.  It is a colon-sep list; expand the items
+separately and squash any empty ones.
+Then check addr->p.remove_headers too, provided that addr is not NULL. */
 
 for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old)
   {
@@ -656,7 +646,15 @@ for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old)
       uschar buffer[128];
       while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
        {
-       int len = Ustrlen(s);
+       int len;
+
+       if (i == 0)
+         if (!(s = expand_string(s)) && !expand_string_forcedfail)
+           {
+           errno = ERRNO_CHHEADER_FAIL;
+           return FALSE;
+           }
+       len = Ustrlen(s);
        if (strncmpic(h->text, s, len) != 0) continue;
        ss = h->text + len;
        while (*ss == ' ' || *ss == '\t') ss++;
@@ -731,36 +729,40 @@ if (addr)
     }
   }
 
-/* If a string containing additional headers exists, expand it and write
-out the result. This is done last so that if it (deliberately or accidentally)
-isn't in header format, it won't mess up any other headers. An empty string
-or a forced expansion failure are noops. An added header string from a
-transport may not end with a newline; add one if it does not. */
+/* If a string containing additional headers exists it is a newline-sep
+list.  Expand each item and write out the result.  This is done last so that
+if it (deliberately or accidentally) isn't in header format, it won't mess
+up any other headers. An empty string or a forced expansion failure are
+noops. An added header string from a transport may not end with a newline;
+add one if it does not. */
 
 if (add_headers)
   {
-  uschar *s = expand_string(add_headers);
-  if (s == NULL)
-    {
-    if (!expand_string_forcedfail)
-      { errno = ERRNO_CHHEADER_FAIL; return FALSE; }
-    }
-  else
-    {
-    int len = Ustrlen(s);
-    if (len > 0)
+  int sep = '\n';
+  uschar * s;
+
+  while ((s = string_nextinlist(&add_headers, &sep, NULL, 0)))
+    if (!(s = expand_string(s)))
       {
-      if (!sendfn(fd, s, len, use_crlf)) return FALSE;
-      if (s[len-1] != '\n' && !sendfn(fd, US"\n", 1, use_crlf))
-       return FALSE;
-      DEBUG(D_transport)
+      if (!expand_string_forcedfail)
+       { errno = ERRNO_CHHEADER_FAIL; return FALSE; }
+      }
+    else
+      {
+      int len = Ustrlen(s);
+      if (len > 0)
        {
-       debug_printf("added header line(s):\n%s", s);
-       if (s[len-1] != '\n') debug_printf("\n");
-       debug_printf("---\n");
+       if (!sendfn(fd, s, len, use_crlf)) return FALSE;
+       if (s[len-1] != '\n' && !sendfn(fd, US"\n", 1, use_crlf))
+         return FALSE;
+       DEBUG(D_transport)
+         {
+         debug_printf("added header line:\n%s", s);
+         if (s[len-1] != '\n') debug_printf("\n");
+         debug_printf("---\n");
+         }
        }
       }
-    }
   }
 
 /* Separate headers from body with a blank line */
index 1ee9b746f424937002f99f149b6f43f79146e31e..a182a9303ba6b34c0bbfcc8a88e0b94430abbd0f 100644 (file)
@@ -21,7 +21,8 @@ localuser:
   driver = accept
   local_parts = userx
   headers_add = "${if def:h_x-rbl-warning: {Added: xxxx}fail}"
-  headers_remove = "${if def:h_x-rbl-warning: {subject}fail}"
+  # Colon-sep list!
+  headers_remove = "${if def::h_x-rbl-warning:: {subject}fail}"
   transport = local_delivery
 
 
index c8e773fc2a08848259d914f28727620d49b137ad..ad88aee09f51c2014fe1a8c72f2e3c8767e534c1 100644 (file)
@@ -21,7 +21,8 @@ localuser:
   driver = accept
   local_parts = userx
   headers_add = "${if def:h_x-rbl-warning: {Added: by router}}"
-  headers_remove = "${if def:h_x-rbl-warning: {subject}}"
+  # Colon-sep list!
+  headers_remove = "${if def::h_x-rbl-warning:: {subject}}"
   transport = local_delivery
 
 
@@ -35,7 +36,7 @@ local_delivery:
   envelope_to_add
   file = DIR/test-mail/$local_part
   headers_add = "${if def:h_tadd: {Added: by transport}}"
-  headers_remove = "${if def:h_tadd: {tadd}}"
+  headers_remove = "${if def::h_tadd:: {tadd}}"
   return_path_add
   user = CALLER
 
index b94331b44e10e9fc84fe71a5c76c3126a71f2099..7d2f0661175964ca71df8e75adb7bd27c3e793a2 100644 (file)
@@ -30,6 +30,9 @@ user:
   headers_add = X-Delivered-To: $local_part@$domain
   retry_use_local_part
   transport = local_delivery
+  headers_add = X-rtr-hdr: 1
+  headers_add = ${if bool{false} {X-rtr-hdr: 2}}
+  headers_add = X-rtr-hdr: 3
 
 
 # ----- Transports -----
@@ -41,6 +44,9 @@ local_delivery:
   envelope_to_add
   file = DIR/test-mail/$local_part
   user = CALLER
+  headers_add = X-tpt-hdr: 1
+  headers_add = ${if bool{false} {X-tpt-hdr: 2}}
+  headers_add = X-tpt-hdr: 3
 
 
 # End
index cf7f26ea0aaf8117643a45f20cabe30af61b41be..05c078ec0c7820deb23f21ea850840e434de2a15 100644 (file)
Binary files a/test/mail/0046.userx and b/test/mail/0046.userx differ
index 3c58c0da1c29708387c91e91504fef8a5555994a..d076309765ef8e0eecdee1a2709571ad3a51b23e 100644 (file)
@@ -12,5 +12,9 @@ X-Delivered-To: b@test.ex
 X-Delivered-To: c@test.ex
 X-Delivered-To: d@test.ex
 X-Delivered-To: userx@test.ex
+X-rtr-hdr: 1
+X-rtr-hdr: 3
+X-tpt-hdr: 1
+X-tpt-hdr: 3
 
 
index 529767368831f77932f19940ac5cd89df47a14a2..8076ae65d8c0560e7aa7a20d2b33a421a1ed8dd3 100644 (file)
@@ -11,5 +11,9 @@ X-Delivered-To: a@test.ex
 X-Delivered-To: bb@test.ex
 X-Delivered-To: e@test.ex
 X-Delivered-To: usery@test.ex
+X-rtr-hdr: 1
+X-rtr-hdr: 3
+X-tpt-hdr: 1
+X-tpt-hdr: 3
 
 
index 50bfd7d6cae8e9e3d33c109edeb8add3a380fc46..41b34c002bc307a80ede45870c70ee699e1a2861 100644 (file)
@@ -19,7 +19,6 @@ Resent-From: CALLER_NAME <CALLER@test.ex>
 Found: yes
 Found2: yes
 FOUND-found2: !!
-
 TO: userx@test.ex,
 usery@test.ex
 --------------------------------
index 260252bebdf6a1118d90e05a2fb031a26cfd9162..410121dae18b82ddf3ead8b952b3515829556ede 100644 (file)
@@ -7,7 +7,6 @@ From: CALLER_NAME <CALLER@test.ex>
 Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
 Found: no
-
 FROM: CALLER_NAME <CALLER@test.ex>
 --------------------------------
 REPLY_ADDRESS: CALLER_NAME <CALLER@test.ex>
@@ -25,7 +24,6 @@ From: CALLER_NAME <CALLER@test.ex>
 Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
 Found: no
-
 FROM: CALLER_NAME <CALLER@test.ex>
 --------------------------------
 REPLY_ADDRESS: CALLER_NAME <CALLER@test.ex>
@@ -43,7 +41,6 @@ From: CALLER_NAME <CALLER@test.ex>
 Message-Id: <E10HmaZ-0005vi-00@the.local.host.name>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
 Found: no
-
 FROM: CALLER_NAME <CALLER@test.ex>
 --------------------------------
 REPLY_ADDRESS: usery@test.ex
@@ -61,7 +58,6 @@ Message-Id: <E10HmbA-0005vi-00@the.local.host.name>
 Sender: CALLER_NAME <CALLER@test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
 Found: no
-
 FROM: 
 --------------------------------
 REPLY_ADDRESS: 
index d1a9a4a70ad7cfa0b2c388ea42ef663d796821e3..e1b8574dba03be6a6890fc81aea5d7483590a03f 100644 (file)
@@ -1,4 +1,4 @@
-# multiple remove_headers and trailing colons
+# multiple remove_headers in routers, and trailing colons
 exim -odi userx
 Remove-Me: this header is to be removed
 Another: This is another header
index 29ff83b42626157428f84a2d845de67e8d45c6f1..1c9b0d83a183e23deb4342b5b687ef15bca132df 100644 (file)
@@ -207,7 +207,7 @@ X-hdr-rtr: qqq
 added header line(s):
 X-hdr-rtr-new: +++
 ---
-added header line(s):
+added header line:
 X-hdr-tpt-new: new
 ---
 ----------- done cutthrough headers send ------------