File exim-4.86.2+fixes-867e8fe25dbfb1e31493488ad695bde55b890397.patch of Package exim

diff -ru a/daemon.c b/daemon.c
--- a/src/daemon.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/daemon.c	2017-04-24 09:33:17.356999655 +0200
@@ -735,6 +735,7 @@
 /* Release any store used in this process, including the store used for holding
 the incoming host address and an expanded active_hostname. */
 
+log_close_all();
 store_reset(reset_point);
 sender_host_address = NULL;
 }
diff -ru a/deliver.c b/deliver.c
--- a/src/deliver.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/deliver.c	2017-04-24 09:33:17.356999655 +0200
@@ -9,6 +9,7 @@
 
 
 #include "exim.h"
+#include <assert.h>
 
 
 /* Data block for keeping track of subprocesses for parallel remote
@@ -7904,17 +7905,36 @@
 uschar *
 deliver_get_sender_address (uschar * id)
 {
+int rc;
+uschar * new_sender_address,
+       * save_sender_address;
+
 if (!spool_open_datafile(id))
   return NULL;
 
+/* Save and restore the global sender_address.  I'm not sure if we should
+not save/restore all the other global variables too, because
+spool_read_header() may change all of them. But OTOH, when this
+deliver_get_sender_address() gets called, the current message is done
+already and nobody needs the globals anymore. (HS12, 2015-08-21) */
+
 sprintf(CS spoolname, "%s-H", id);
-if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK)
+save_sender_address = sender_address;
+
+rc = spool_read_header(spoolname, TRUE, TRUE);
+
+new_sender_address = sender_address;
+sender_address = save_sender_address;
+
+if (rc != spool_read_OK)
   return NULL;
 
+assert(new_sender_address);
+
 (void)close(deliver_datafile);
 deliver_datafile = -1;
 
-return sender_address;
+return new_sender_address;
 }
 
 /* vi: aw ai sw=2
diff -ru a/dns.c b/dns.c
--- a/src/dns.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/dns.c	2017-04-24 09:33:17.356999655 +0200
@@ -390,7 +390,8 @@
 
 dnss->aptr += namelen;
 GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */
-dnss->aptr += 6;                      /* Don't want class or TTL */
+dnss->aptr += 2;                      /* Don't want class */
+GETLONG(dnss->srr.ttl, dnss->aptr);   /* TTL */
 GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */
 dnss->srr.data = dnss->aptr;          /* The record's data follows */
 dnss->aptr += dnss->srr.size;         /* Advance to next RR */
diff -ru a/exim.c b/exim.c
--- a/src/exim.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/exim.c	2017-04-24 09:33:17.360999706 +0200
@@ -3730,6 +3730,13 @@
     exit(EXIT_FAILURE);
   }
 
+/* Store the initial cwd before we change directories */
+if ((initial_cwd = getcwd(NULL, 0)) == NULL)
+  {
+  perror("exim: can't get the current working directory");
+  exit(EXIT_FAILURE);
+  }
+
 readconf_main();
 
 if (cleanup_environment() == FALSE)
@@ -4017,9 +4024,10 @@
   {
   int i;
   uschar *p = big_buffer;
-  char * dummy;
   Ustrcpy(p, "cwd= (failed)");
-  dummy = /* quieten compiler */ getcwd(CS p+4, big_buffer_size - 4);
+
+  Ustrncpy(p + 4, initial_cwd, big_buffer_size-5);
+
   while (*p) p++;
   (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc);
   while (*p) p++;
diff -ru a/expand.c b/expand.c
--- a/src/expand.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/expand.c	2017-04-24 09:33:17.360999706 +0200
@@ -533,6 +533,7 @@
   { "host_lookup_deferred",vtype_int,         &host_lookup_deferred },
   { "host_lookup_failed",  vtype_int,         &host_lookup_failed },
   { "host_port",           vtype_int,         &deliver_host_port },
+  { "initial_cwd",         vtype_stringptr,   &initial_cwd },
   { "inode",               vtype_ino,         &deliver_inode },
   { "interface_address",   vtype_stringptr,   &interface_address },
   { "interface_port",      vtype_int,         &interface_port },
diff -ru a/functions.h b/functions.h
--- a/src/functions.h	2016-03-02 18:27:51.000000000 +0100
+++ b/src/functions.h	2017-04-24 09:33:17.360999706 +0200
@@ -375,7 +375,7 @@
 extern int     smtp_feof(void);
 extern int     smtp_ferror(void);
 extern uschar *smtp_get_connection_info(void);
-extern BOOL    smtp_get_interface(uschar *, int, address_item *, BOOL *,
+extern BOOL    smtp_get_interface(uschar *, int, address_item *,
                  uschar **, uschar *);
 extern BOOL    smtp_get_port(uschar *, address_item *, int *, uschar *);
 extern int     smtp_getc(void);
diff -ru a/globals.c b/globals.c
--- a/src/globals.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/globals.c	2017-04-24 09:33:17.360999706 +0200
@@ -783,6 +783,7 @@
 uschar *ignore_fromline_hosts  = NULL;
 BOOL    inetd_wait_mode        = FALSE;
 int     inetd_wait_timeout     = -1;
+uschar *initial_cwd            = NULL;
 uschar *interface_address      = NULL;
 int     interface_port         = -1;
 BOOL    is_inetd               = FALSE;
diff -ru a/globals.h b/globals.h
--- a/src/globals.h	2016-03-02 18:27:51.000000000 +0100
+++ b/src/globals.h	2017-04-24 09:33:17.360999706 +0200
@@ -508,6 +508,7 @@
 extern uschar *ignore_fromline_hosts;  /* Hosts permitted to send "From " */
 extern BOOL    inetd_wait_mode;        /* Whether running in inetd wait mode */
 extern int     inetd_wait_timeout;     /* Timeout for inetd wait mode */
+extern uschar *initial_cwd;            /* The directory we where in at startup */
 extern BOOL    is_inetd;               /* True for inetd calls */
 extern uschar *iterate_item;           /* Item from iterate list */
 
diff -ru a/lookupapi.h b/lookupapi.h
--- a/src/lookupapi.h	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookupapi.h	2017-04-24 09:33:17.364999757 +0200
@@ -34,7 +34,7 @@
     int,                          /* length of key or query */
     uschar **,                    /* for returning answer */
     uschar **,                    /* for error message */
-    BOOL *);                      /* to request cache cleanup */
+    uint *);                      /* cache TTL, sconds */
   void (*close)(                  /* close function */
     void *);                      /* handle */
   void (*tidy)(void);             /* tidy function */
@@ -46,9 +46,10 @@
 } lookup_info;
 
 /* This magic number is used by the following lookup_module_info structure
-   for checking API compatibility. It's equivalent to the string"LMM2" */
-#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4d32
+   for checking API compatibility. It used to be equivalent to the string"LMM3" */
+#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4933
 /* Version 2 adds: version_report */
+/* Version 3 change: non/cache becomes TTL in seconds */
 
 typedef struct lookup_module_info {
   uint magic;
diff -ru a/lookups/cdb.c b/lookups/cdb.c
--- a/src/lookups/cdb.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/cdb.c	2017-04-24 09:33:17.364999757 +0200
@@ -279,7 +279,7 @@
         int  key_len,
         uschar **result,
         uschar **errmsg,
-        BOOL *do_cache)
+        uint *do_cache)
 {
   struct cdb_state * cdbp = handle;
   uint32 item_key_len,
diff -ru a/lookups/dbmdb.c b/lookups/dbmdb.c
--- a/src/lookups/dbmdb.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/dbmdb.c	2017-04-24 09:33:17.364999757 +0200
@@ -87,7 +87,7 @@
 
 static int
 dbmdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 EXIM_DB *d = (EXIM_DB *)handle;
 EXIM_DATUM key, data;
@@ -120,7 +120,7 @@
 
 int
 static dbmnz_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 return dbmdb_find(handle, filename, keystring, length-1, result, errmsg,
   do_cache);
@@ -140,7 +140,7 @@
 
 static int
 dbmjz_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 uschar *key_item, *key_buffer, *key_p;
 const uschar *key_elems = keystring;
diff -ru a/lookups/dnsdb.c b/lookups/dnsdb.c
--- a/src/lookups/dnsdb.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/dnsdb.c	2017-04-24 09:33:17.364999757 +0200
@@ -131,7 +131,7 @@
 
 static int
 dnsdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int rc;
 int size = 256;
@@ -388,6 +388,9 @@
       {
       if (rr->type != searchtype) continue;
 
+      if (*do_cache > rr->ttl)
+	*do_cache = rr->ttl;
+
       if (type == T_A || type == T_AAAA || type == T_ADDRESSES)
         {
         dns_address *da;
diff -ru a/lookups/dsearch.c b/lookups/dsearch.c
--- a/src/lookups/dsearch.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/dsearch.c	2017-04-24 09:33:17.364999757 +0200
@@ -67,7 +67,7 @@
 
 int
 static dsearch_find(void *handle, uschar *dirname, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 struct stat statbuf;
 int save_errno;
diff -ru a/lookups/ibase.c b/lookups/ibase.c
--- a/src/lookups/ibase.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/ibase.c	2017-04-24 09:33:17.364999757 +0200
@@ -451,7 +451,7 @@
 
 static int
 ibase_find(void *handle, uschar * filename, uschar * query, int length,
-           uschar ** result, uschar ** errmsg, BOOL *do_cache)
+           uschar ** result, uschar ** errmsg, uint *do_cache)
 {
     int sep = 0;
     uschar *server;
diff -ru a/lookups/ldap.c b/lookups/ldap.c
--- a/src/lookups/ldap.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/ldap.c	2017-04-24 09:33:17.364999757 +0200
@@ -156,7 +156,7 @@
 uschar *error2 = NULL;   /* error message from the server */
 uschar *matched = NULL;  /* partially matched DN */
 
-int    attr_count = 0;
+int    attrs_requested = 0;
 int    error_yield = DEFER;
 int    msgid;
 int    rc, ldap_rc, ldap_parse_rc;
@@ -248,7 +248,7 @@
 /* Count the attributes; we need this later to tell us how to format results */
 
 for (attrp = USS ludp->lud_attrs; attrp != NULL && *attrp != NULL; attrp++)
-  attr_count++;
+  attrs_requested++;
 
 /* See if we can find a cached connection to this host. The port is not
 relevant for ldapi. The host name pointer is set to NULL if no host was given
@@ -713,10 +713,15 @@
         LDAP_RES_SEARCH_ENTRY)
   {
   LDAPMessage  *e;
+  int valuecount;   /* We can see an attr spread across several
+                    entries. If B is derived from A and we request
+                    A and the directory contains both, A and B,
+                    then we get two entries, one for A and one for B.
+                    Here we just count the values per entry */
 
   DEBUG(D_lookup) debug_printf("ldap_result loop\n");
 
-  for(e = ldap_first_entry(lcp->ld, result);
+  for(e = ldap_first_entry(lcp->ld, result), valuecount = 0;
       e != NULL;
       e = ldap_next_entry(lcp->ld, e))
     {
@@ -774,6 +779,11 @@
               attr != NULL;
               attr = US ldap_next_attribute(lcp->ld, e, ber))
       {
+
+      /* In case of attrs_requested == 1 we just count the values, in all other cases
+      (0, >1) we count the values per attribute */
+      if (attrs_requested != 1) valuecount = 0;
+
       if (attr[0] != 0)
         {
         /* Get array of values for this attribute. */
@@ -781,7 +791,8 @@
         if ((firstval = values = USS ldap_get_values(lcp->ld, e, CS attr))
              != NULL)
           {
-          if (attr_count != 1)
+
+          if (attrs_requested != 1)
             {
             if (insert_space)
               data = string_cat(data, &size, &ptr, US" ", 1);
@@ -795,6 +806,7 @@
             {
             uschar *value = *values;
             int len = Ustrlen(value);
+            ++valuecount;
 
             DEBUG(D_lookup) debug_printf("LDAP attr loop %s:%s\n", attr, value);
 
@@ -804,13 +816,13 @@
 	     * attributeTypes B and C from A and then query for A.)
 	     * In all other cases we detect the different attribute
 	     * and append only every non first value. */
-	    if ((attr_count == 1 && data) || (values != firstval))
+            if (data && valuecount > 1)
               data = string_cat(data, &size, &ptr, US",", 1);
 
             /* For multiple attributes, the data is in quotes. We must escape
             internal quotes, backslashes, newlines, and must double commas. */
 
-            if (attr_count != 1)
+            if (attrs_requested != 1)
               {
               int j;
               for (j = 0; j < len; j++)
@@ -851,7 +863,7 @@
 
           /* Closing quote at the end of the data for a named attribute. */
 
-          if (attr_count != 1)
+          if (attrs_requested != 1)
             data = string_cat(data, &size, &ptr, US"\"", 1);
 
           /* Free the values */
@@ -1339,7 +1351,7 @@
 
 static int
 eldap_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1348,7 +1360,7 @@
 
 static int
 eldapm_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1357,7 +1369,7 @@
 
 static int
 eldapdn_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1366,7 +1378,7 @@
 
 int
 eldapauth_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
diff -ru a/lookups/lf_functions.h b/lookups/lf_functions.h
--- a/src/lookups/lf_functions.h	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/lf_functions.h	2017-04-24 09:33:17.364999757 +0200
@@ -12,7 +12,7 @@
 extern uschar *lf_quote(uschar *, uschar *, int, uschar *, int *, int *);
 extern int     lf_sqlperform(const uschar *, const uschar *, const uschar *,
 		 const uschar *, uschar **,
-                 uschar **, BOOL *, int(*)(const uschar *, uschar *, uschar **,
-                 uschar **, BOOL *, BOOL *));
+                 uschar **, uint *, int(*)(const uschar *, uschar *, uschar **,
+                 uschar **, BOOL *, uint *));
 
 /* End of lf_functions.h */
diff -ru a/lookups/lf_sqlperform.c b/lookups/lf_sqlperform.c
--- a/src/lookups/lf_sqlperform.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/lf_sqlperform.c	2017-04-24 09:33:17.364999757 +0200
@@ -27,7 +27,7 @@
   query          the query
   result         where to pass back the result
   errmsg         where to pass back an error message
-  do_cache       to be set FALSE if data is changed
+  do_cache       to be set zero if data is changed
   func           the lookup function to call
 
 Returns:         the return from the lookup function, or DEFER
@@ -36,8 +36,8 @@
 int
 lf_sqlperform(const uschar *name, const uschar *optionname,
   const uschar *optserverlist, const uschar *query,
-  uschar **result, uschar **errmsg, BOOL *do_cache,
-  int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, BOOL *))
+  uschar **result, uschar **errmsg, uint *do_cache,
+  int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *))
 {
 int sep, rc;
 uschar *server;
diff -ru a/lookups/lsearch.c b/lookups/lsearch.c
--- a/src/lookups/lsearch.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/lsearch.c	2017-04-24 09:33:17.364999757 +0200
@@ -323,7 +323,7 @@
 
 static int
 lsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
@@ -340,7 +340,7 @@
 
 static int
 wildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
@@ -357,7 +357,7 @@
 
 static int
 nwildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 return internal_lsearch_find(handle, filename, keystring, length, result,
@@ -375,7 +375,7 @@
 
 static int
 iplsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 do_cache = do_cache;  /* Keep picky compilers happy */
 if ((length == 1 && keystring[0] == '*') ||
diff -ru a/lookups/mysql.c b/lookups/mysql.c
--- a/src/lookups/mysql.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/mysql.c	2017-04-24 09:33:17.364999757 +0200
@@ -74,7 +74,7 @@
   resultptr    where to store the result
   errmsg       where to point an error message
   defer_break  TRUE if no more servers are to be tried after DEFER
-  do_cache     set false if data is changed
+  do_cache     set zero if data is changed
 
 The server string is of the form "host/dbname/user/password". The host can be
 host:port. This string is in a nextinlist temporary buffer, so can be
@@ -85,7 +85,7 @@
 
 static int
 perform_mysql_search(const uschar *query, uschar *server, uschar **resultptr,
-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
+  uschar **errmsg, BOOL *defer_break, uint *do_cache)
 {
 MYSQL *mysql_handle = NULL;        /* Keep compilers happy */
 MYSQL_RES *mysql_result = NULL;
@@ -225,7 +225,7 @@
 was expected (this is all explained clearly in the MySQL manual). In this case,
 we return the number of rows affected by the command. In this event, we do NOT
 want to cache the result; also the whole cache for the handle must be cleaned
-up. Setting do_cache FALSE requests this. */
+up. Setting do_cache zero requests this. */
 
 if ((mysql_result = mysql_use_result(mysql_handle)) == NULL)
   {
@@ -233,7 +233,7 @@
     {
     DEBUG(D_lookup) debug_printf("MYSQL: query was not one that returns data\n");
     result = string_sprintf("%d", mysql_affected_rows(mysql_handle));
-    *do_cache = FALSE;
+    *do_cache = 0;
     goto MYSQL_EXIT;
     }
   *errmsg = string_sprintf("MYSQL: lookup result failed: %s\n",
@@ -341,7 +341,7 @@
 
 static int
 mysql_find(void *handle, uschar *filename, const uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 return lf_sqlperform(US"MySQL", US"mysql_servers", mysql_servers, query,
   result, errmsg, do_cache, perform_mysql_search);
diff -ru a/lookups/nis.c b/lookups/nis.c
--- a/src/lookups/nis.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/nis.c	2017-04-24 09:33:17.364999757 +0200
@@ -42,7 +42,7 @@
 
 static int
 nis_find(void *handle, uschar *filename, uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int rc;
 uschar *nis_data;
@@ -68,7 +68,7 @@
 
 static int
 nis0_find(void *handle, uschar *filename, uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int rc;
 uschar *nis_data;
diff -ru a/lookups/nisplus.c b/lookups/nisplus.c
--- a/src/lookups/nisplus.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/nisplus.c	2017-04-24 09:33:17.364999757 +0200
@@ -43,7 +43,7 @@
 
 static int
 nisplus_find(void *handle, uschar *filename, uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int i;
 int ssize = 0;
diff -ru a/lookups/oracle.c b/lookups/oracle.c
--- a/src/lookups/oracle.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/oracle.c	2017-04-24 09:33:17.364999757 +0200
@@ -517,7 +517,7 @@
 
 static int
 oracle_find(void *handle, uschar *filename, uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int sep = 0;
 uschar *server;
diff -ru a/lookups/passwd.c b/lookups/passwd.c
--- a/src/lookups/passwd.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/passwd.c	2017-04-24 09:33:17.364999757 +0200
@@ -34,7 +34,7 @@
 
 static int
 passwd_find(void *handle, uschar *filename, const uschar *keystring, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 struct passwd *pw;
 
diff -ru a/lookups/pgsql.c b/lookups/pgsql.c
--- a/src/lookups/pgsql.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/pgsql.c	2017-04-24 09:33:17.364999757 +0200
@@ -119,7 +119,7 @@
 
 static int
 perform_pgsql_search(const uschar *query, uschar *server, uschar **resultptr,
-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
+  uschar **errmsg, BOOL *defer_break, uint *do_cache)
 {
 PGconn *pg_conn = NULL;
 PGresult *pg_result = NULL;
@@ -290,10 +290,10 @@
     /* The command was successful but did not return any data since it was
      * not SELECT but either an INSERT, UPDATE or DELETE statement. Tell the
      * high level code to not cache this query, and clean the current cache for
-     * this handle by setting *do_cache FALSE. */
+     * this handle by setting *do_cache zero. */
     result = string_copy(US PQcmdTuples(pg_result));
     offset = Ustrlen(result);
-    *do_cache = FALSE;
+    *do_cache = 0;
     DEBUG(D_lookup) debug_printf("PGSQL: command does not return any data "
       "but was successful. Rows affected: %s\n", result);
 
@@ -399,7 +399,7 @@
 
 static int
 pgsql_find(void *handle, uschar *filename, const uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query,
   result, errmsg, do_cache, perform_pgsql_search);
diff -ru a/lookups/README b/lookups/README
--- a/src/lookups/README	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/README	2017-04-24 09:33:17.364999757 +0200
@@ -122,12 +122,15 @@
   uschar **errmsg     where to put an error message on failure;
                       this is initially set to "", and should be left
                       as that for a standard "entry not found" error
-  BOOL *do_cache      the lookup should set this to FALSE when it changes data.
-                      This is TRUE by default. When set to FALSE the cache tree
+  uint *do_cache      the lookup should set this to 0 when it changes data.
+                      This is MAXINT by default. When set to 0 the cache tree
                       of the current search handle will be cleaned and the
                       current result will NOT be cached. Currently the mysql
                       and pgsql lookups use this when UPDATE/INSERT queries are
                       executed.
+                      If set to a nonzero number of seconds, the cached value
+                      becomes unusable after this time. Currently the dnsdb
+                      lookup uses this to support the TTL value.
 
 Even though the key is zero-terminated, the length is passed because in the
 common case it has been computed already and is often needed.
diff -ru a/lookups/redis.c b/lookups/redis.c
--- a/src/lookups/redis.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/redis.c	2017-04-24 09:33:17.364999757 +0200
@@ -65,7 +65,7 @@
  */
 static int
 perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
+  uschar **errmsg, BOOL *defer_break, uint *do_cache)
 {
        redisContext *redis_handle = NULL;        /* Keep compilers happy */
        redisReply *redis_reply = NULL;
@@ -197,7 +197,7 @@
        case REDIS_REPLY_ERROR:
                *errmsg = string_sprintf("REDIS: lookup result failed: %s\n", redis_reply->str);
                *defer_break = FALSE;
-               *do_cache = FALSE;
+               *do_cache = 0;
                goto REDIS_EXIT;
                /* NOTREACHED */
 
@@ -205,7 +205,7 @@
        case REDIS_REPLY_NIL:
                DEBUG(D_lookup) debug_printf("REDIS: query was not one that returned any data\n");
                result = string_sprintf("");
-               *do_cache = FALSE;
+               *do_cache = 0;
                goto REDIS_EXIT;
                /* NOTREACHED */
 
@@ -304,7 +304,7 @@
 
 static int
 redis_find(void *handle __attribute__((unused)), uschar *filename __attribute__((unused)),
-           uschar *command, int length, uschar **result, uschar **errmsg, BOOL *do_cache)
+           uschar *command, int length, uschar **result, uschar **errmsg, uint *do_cache)
 {
        return lf_sqlperform(US"Redis", US"redis_servers", redis_servers, command,
          result, errmsg, do_cache, perform_redis_search);
diff -ru a/lookups/spf.c b/lookups/spf.c
--- a/src/lookups/spf.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/spf.c	2017-04-24 09:33:17.364999757 +0200
@@ -31,7 +31,9 @@
 #include <spf2/spf_dns_resolv.h>
 #include <spf2/spf_dns_cache.h>
 
-static void *spf_open(uschar *filename, uschar **errmsg) {
+static void *
+spf_open(uschar *filename, uschar **errmsg)
+{
   SPF_server_t *spf_server = NULL;
   spf_server = SPF_server_new(SPF_DNS_CACHE, 0);
   if (spf_server == NULL) {
@@ -41,13 +43,17 @@
   return (void *) spf_server;
 }
 
-static void spf_close(void *handle) {
+static void
+spf_close(void *handle)
+{
   SPF_server_t *spf_server = handle;
   if (spf_server) SPF_server_free(spf_server);
 }
 
-static int spf_find(void *handle, uschar *filename, uschar *keystring, int key_len,
-             uschar **result, uschar **errmsg, BOOL *do_cache) {
+static int
+spf_find(void *handle, uschar *filename, uschar *keystring, int key_len,
+             uschar **result, uschar **errmsg, uint *do_cache)
+{
   SPF_server_t *spf_server = handle;
   SPF_request_t *spf_request = NULL;
   SPF_response_t *spf_response = NULL;
diff -ru a/lookups/sqlite.c b/lookups/sqlite.c
--- a/src/lookups/sqlite.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/sqlite.c	2017-04-24 09:33:17.364999757 +0200
@@ -81,7 +81,7 @@
 
 static int
 sqlite_find(void *handle, uschar *filename, const uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 int ret;
 struct strbuf res = { NULL, 0, 0 };
@@ -93,7 +93,7 @@
   return FAIL;
   }
 
-if (res.string == NULL) *do_cache = FALSE;
+if (res.string == NULL) *do_cache = 0;
 
 *result = res.string;
 return OK;
diff -ru a/lookups/testdb.c b/lookups/testdb.c
--- a/src/lookups/testdb.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/testdb.c	2017-04-24 09:33:17.364999757 +0200
@@ -38,7 +38,7 @@
 
 static int
 testdb_find(void *handle, uschar *filename, const uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 handle = handle;          /* Keep picky compilers happy */
 filename = filename;
@@ -57,7 +57,7 @@
   return DEFER;
   }
 
-if (Ustrcmp(query, "nocache") == 0) *do_cache = FALSE;
+if (Ustrcmp(query, "nocache") == 0) *do_cache = 0;
 
 *result = string_copy(query);
 return OK;
diff -ru a/lookups/whoson.c b/lookups/whoson.c
--- a/src/lookups/whoson.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/lookups/whoson.c	2017-04-24 09:33:17.364999757 +0200
@@ -36,7 +36,7 @@
 
 static int
 whoson_find(void *handle, uschar *filename, uschar *query, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 uschar buffer[80];
 handle = handle;          /* Keep picky compilers happy */
diff -ru a/mime.c b/mime.c
--- a/src/mime.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/mime.c	2017-04-24 09:33:17.368999807 +0200
@@ -550,7 +550,8 @@
 uschar * val = string_cat(NULL, &size, &ptr, US"=?", 2);
 uschar c;
 
-val = string_cat(val, &size, &ptr, charset, Ustrlen(charset));
+if (charset)
+  val = string_cat(val, &size, &ptr, charset, Ustrlen(charset));
 val = string_cat(val, &size, &ptr, US"?Q?", 3);
 
 while ((c = *fname))
@@ -607,7 +608,7 @@
     if (!fgets(CS header, MIME_MAX_HEADER_SIZE, f))
       {
       /* Hit EOF or read error. Ugh. */
-      DEBUG(D_acl) debug_printf("Hit EOF ...\n");
+      DEBUG(D_acl) debug_printf("MIME: Hit EOF ...\n");
       return rc;
       }
 
@@ -619,12 +620,12 @@
       if (Ustrncmp((header+2+Ustrlen(context->boundary)), "--", 2) == 0)
 	{
 	/* END boundary found */
-	DEBUG(D_acl) debug_printf("End boundary found %s\n",
+	DEBUG(D_acl) debug_printf("MIME: End boundary found %s\n",
 	  context->boundary);
 	return rc;
 	}
 
-      DEBUG(D_acl) debug_printf("Next part with boundary %s\n",
+      DEBUG(D_acl) debug_printf("MIME: Next part with boundary %s\n",
 	context->boundary);
       break;
       }
@@ -648,7 +649,7 @@
 
       for (q = p; *q != ';' && *q; q++) ;
       *mh->value = string_copynlc(p, q-p);
-      DEBUG(D_acl) debug_printf("found %s MIME header, value is '%s'\n",
+      DEBUG(D_acl) debug_printf("MIME: found %s header, value is '%s'\n",
 	mh->name, *mh->value);
 
       if (*(p = q)) p++;			/* jump past the ; */
@@ -666,7 +667,7 @@
 	  {
 	  mime_parameter * mp;
 
-	  DEBUG(D_acl) debug_printf("  considering paramlist '%s'\n", p);
+	  DEBUG(D_acl) debug_printf("MIME:   considering paramlist '%s'\n", p);
 
 	  if (  !mime_filename
 	     && strncmpic(CUS"content-disposition:", header, 20) == 0
@@ -700,22 +701,27 @@
 		  uschar * s = q;
 
 		  /* look for a ' in the "filename" */
-		  while(*s != '\'' && *s) s++;	/* s is ' or NUL */
+		  while(*s != '\'' && *s) s++;	/* s is 1st ' or NUL */
 
 		  if ((size = s-q) > 0)
-		    {
 		    mime_filename_charset = string_copyn(q, size);
-		    p = s;
 
-		    while(*p == '\'' && *p) p++; /* p is after ' */
-		    }
+		  if (*(p = s)) p++;
+		  while(*p == '\'') p++;	/* p is after 2nd ' */
 		  }
 		else
 		  p = q;
 
+		DEBUG(D_acl) debug_printf("MIME:    charset %s fname '%s'\n",
+		  mime_filename_charset ? mime_filename_charset : US"<NULL>", p);
+
 		temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen);
-		temp_string = rfc2047_decode(temp_string, FALSE, NULL, 32,
+		DEBUG(D_acl) debug_printf("MIME:    2047-name %s\n", temp_string);
+
+		temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ',
 		  NULL, &err_msg);
+		DEBUG(D_acl) debug_printf("MIME:    plain-name %s\n", temp_string);
+
 		size = Ustrlen(temp_string);
 
 		if (size == slen)
@@ -750,7 +756,7 @@
 		    &dummy_errstr)
 		: NULL;
 	      DEBUG(D_acl) debug_printf(
-		" found %s MIME parameter in %s header, value '%s'\n",
+		"MIME:  found %s parameter in %s header, value '%s'\n",
 		mp->name, mh->name, *mp->value);
 
 	      break;			/* done matching param names */
@@ -768,7 +774,7 @@
 	  if (decoding_failed) mime_filename = mime_fname_rfc2231;
 
 	  DEBUG(D_acl) debug_printf(
-	    " found %s MIME parameter in %s header, value is '%s'\n",
+	    "MIME:  found %s parameter in %s header, value is '%s'\n",
 	    "filename", mh->name, mime_filename);
 	  }
 	}
@@ -809,8 +815,9 @@
        (nested_context.boundary != NULL) &&
        (Ustrncmp(mime_content_type,"multipart",9) == 0) )
     {
-    DEBUG(D_acl) debug_printf("Entering multipart recursion, boundary '%s'\n",
-      nested_context.boundary);
+    DEBUG(D_acl)
+      debug_printf("MIME: Entering multipart recursion, boundary '%s'\n",
+	nested_context.boundary);
 
     nested_context.context =
       context && context->context == MBC_ATTACHMENT
diff -ru a/pdkim/base64.c b/pdkim/base64.c
--- a/src/pdkim/base64.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/pdkim/base64.c	2017-04-24 09:33:17.368999807 +0200
@@ -128,20 +128,22 @@
 
     for( i = j = n = 0; i < slen; i++ )
     {
+	unsigned char c = src[i];
+
         if( ( slen - i ) >= 2 &&
-            src[i] == '\r' && src[i + 1] == '\n' )
+            c == '\r' && src[i + 1] == '\n' )
             continue;
 
-        if( src[i] == '\n' )
+        if( c == '\n' || c == ' ' || c == '\t' )
             continue;
 
-        if( src[i] == '=' && ++j > 2 )
+        if( c == '=' && ++j > 2 )
             return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );
 
-        if( src[i] > 127 || base64_dec_map[src[i]] == 127 )
+        if( c > 127 || base64_dec_map[src[i]] == 127 )
             return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );
 
-        if( base64_dec_map[src[i]] < 64 && j != 0 )
+        if( base64_dec_map[c] < 64 && j != 0 )
             return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );
 
         n++;
@@ -160,11 +162,13 @@
 
    for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ )
    {
-        if( *src == '\r' || *src == '\n' )
+	unsigned char c = *src;
+
+        if( c == '\r' || c == '\n' || c == ' ' || c == '\t' )
             continue;
 
-        j -= ( base64_dec_map[*src] == 64 );
-        x  = (x << 6) | ( base64_dec_map[*src] & 0x3F );
+        j -= ( base64_dec_map[c] == 64 );
+        x  = (x << 6) | ( base64_dec_map[c] & 0x3F );
 
         if( ++n == 4 )
         {
diff -ru a/readconf.c b/readconf.c
--- a/src/readconf.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/readconf.c	2017-04-24 09:33:17.368999807 +0200
@@ -3456,10 +3456,10 @@
       " are obsolete\n");
 #endif	/*SUPPORT_TLS*/
 
-if ((!add_environment || *add_environment == '\0') && !keep_environment)
+if (!keep_environment && environ && *environ)
   log_write(0, LOG_MAIN,
-      "WARNING: purging the environment.\n"
-      " Suggested action: use keep_environment and add_environment.\n");
+      "Warning: purging the environment.\n"
+      " Suggested action: use keep_environment.");
 }
 
 
diff -ru a/receive.c b/receive.c
--- a/src/receive.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/receive.c	2017-04-24 09:33:17.368999807 +0200
@@ -835,7 +835,15 @@
       ch_state = 4;
       continue;
       }
-    ch_state = 1;                       /* The dot itself is removed */
+    /* The dot was removed at state 3. For a doubled dot, here, reinstate
+    it to cutthrough. The current ch, dot or not, is passed both to cutthrough
+    and to file below. */
+    if (ch == '.')
+      {
+      uschar c= ch;
+      (void) cutthrough_puts(&c, 1);
+      }
+    ch_state = 1;
     break;
 
     case 4:                             /* After [CR] LF . CR */
diff -ru a/search.c b/search.c
--- a/src/search.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/search.c	2017-04-24 09:33:17.372999860 +0200
@@ -466,6 +466,7 @@
 {
 tree_node *t = (tree_node *)handle;
 search_cache *c = (search_cache *)(t->data.ptr);
+expiring_data *e;
 uschar *data = NULL;
 int search_type = t->name[0] - '0';
 int old_pool = store_pool;
@@ -491,18 +492,27 @@
 /* Look up the data for the key, unless it is already in the cache for this
 file. No need to check c->item_cache for NULL, tree_search will do so. */
 
-if ((t = tree_search(c->item_cache, keystring)) == NULL)
+if (  (t = tree_search(c->item_cache, keystring))
+   && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL))
+   )
+  { /* Data was in the cache already; set the pointer from the tree node */
+  data = e->ptr;
+  DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n",
+    keystring,
+    filename ? US"\n  in " : US"", filename ? filename : US"");
+  }
+else
   {
-  BOOL do_cache = TRUE;
+  uint do_cache = UINT_MAX;
   int keylength = Ustrlen(keystring);
 
   DEBUG(D_lookup)
     {
-    if (filename != NULL)
-      debug_printf("file lookup required for %s\n  in %s\n",
-        keystring, filename);
-    else
-      debug_printf("database lookup required for %s\n", keystring);
+    if (t) debug_printf("cached data found but past valid time; ");
+    debug_printf("%s lookup required for %s%s%s\n",
+      filename ? US"file" : US"database",
+      keystring,
+      filename ? US"\n  in " : US"", filename ? filename : US"");
     }
 
   /* Call the code for the different kinds of search. DEFER is handled
@@ -511,9 +521,7 @@
 
   if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength,
       &data, &search_error_message, &do_cache) == DEFER)
-    {
     search_find_defer = TRUE;
-    }
 
   /* A record that has been found is now in data, which is either NULL
   or points to a bit of dynamic store. Cache the result of the lookup if
@@ -524,10 +532,22 @@
   else if (do_cache)
     {
     int len = keylength + 1;
-    t = store_get(sizeof(tree_node) + len);
-    memcpy(t->name, keystring, len);
-    t->data.ptr = data;
-    tree_insertnode(&c->item_cache, t);
+
+    if (t)	/* Previous, out-of-date cache entry.  Update with the */
+      { 	/* new result and forget the old one */
+      e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
+      e->ptr = data;
+      }
+    else
+      {
+      e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len);
+      e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
+      e->ptr = data;
+      t = (tree_node *)(e+1);
+      memcpy(t->name, keystring, len);
+      t->data.ptr = e;
+      tree_insertnode(&c->item_cache, t);
+      }
     }
 
   /* If caching was disabled, empty the cache tree. We just set the cache
@@ -540,34 +560,19 @@
     }
   }
 
-/* Data was in the cache already; set the pointer from the tree node */
-
-else
-  {
-  data = US t->data.ptr;
-  DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n",
-    keystring,
-    (filename == NULL)? US"" : US"\n  in ",
-    (filename == NULL)? US"" : filename);
-  }
-
-/* Debug: output the answer */
-
 DEBUG(D_lookup)
   {
-  if (data == NULL)
-    {
-    if (search_find_defer) debug_printf("lookup deferred: %s\n",
-      search_error_message);
-    else debug_printf("lookup failed\n");
-    }
-  else debug_printf("lookup yielded: %s\n", data);
+  if (data)
+    debug_printf("lookup yielded: %s\n", data);
+  else if (search_find_defer)
+    debug_printf("lookup deferred: %s\n", search_error_message);
+  else debug_printf("lookup failed\n");
   }
 
 /* Return it in new dynamic store in the regular pool */
 
 store_pool = old_pool;
-return (data == NULL)? NULL : string_copy(data);
+return data ? string_copy(data) : NULL;
 }
 
 
diff -ru a/smtp_in.c b/smtp_in.c
--- a/src/smtp_in.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/smtp_in.c	2017-04-24 09:33:17.372999860 +0200
@@ -9,6 +9,7 @@
 
 
 #include "exim.h"
+#include <assert.h>
 
 
 /* Initialize for TCP wrappers if so configured. It appears that the macro
@@ -232,6 +233,7 @@
 
 /* Sanity check and validate optional args to MAIL FROM: envelope */
 enum {
+  ENV_MAIL_OPT_NULL,
   ENV_MAIL_OPT_SIZE, ENV_MAIL_OPT_BODY, ENV_MAIL_OPT_AUTH,
 #ifndef DISABLE_PRDR
   ENV_MAIL_OPT_PRDR,
@@ -240,7 +242,6 @@
 #ifdef EXPERIMENTAL_INTERNATIONAL
   ENV_MAIL_OPT_UTF8,
 #endif
-  ENV_MAIL_OPT_NULL
   };
 typedef struct {
   uschar *   name;  /* option requested during MAIL cmd */
@@ -260,7 +261,8 @@
 #ifdef EXPERIMENTAL_INTERNATIONAL
     { US"SMTPUTF8",ENV_MAIL_OPT_UTF8,  FALSE },		/* rfc6531 */
 #endif
-    { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE }
+    /* keep this the last entry */
+    { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE },
   };
 
 /* When reading SMTP from a remote host, we have to use our own versions of the
@@ -3887,7 +3889,7 @@
       if (!extract_option(&name, &value)) break;
 
       for (mail_args = env_mail_type_list;
-           (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list);
+           mail_args->value != ENV_MAIL_OPT_NULL;
            mail_args++
           )
         if (strcmpic(name, mail_args->name) == 0)
@@ -4066,15 +4068,17 @@
 	    }
 	  break;
 #endif
-        /* Unknown option. Stick back the terminator characters and break
+        /* No valid option. Stick back the terminator characters and break
         the loop.  Do the name-terminator second as extract_option sets
-	value==name when it found no equal-sign.
-	An error for a malformed address will occur. */
-        default:
+        value==name when it found no equal-sign.
+        An error for a malformed address will occur. */
+        case ENV_MAIL_OPT_NULL:
           value[-1] = '=';
           name[-1] = ' ';
           arg_error = TRUE;
           break;
+
+        default:  assert(0);
         }
       /* Break out of for loop if switch() had bad argument or
          when start of the email address is reached */
diff -ru a/smtp_out.c b/smtp_out.c
--- a/src/smtp_out.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/smtp_out.c	2017-04-24 09:33:17.372999860 +0200
@@ -26,7 +26,6 @@
                which case the function does nothing
   host_af    AF_INET or AF_INET6 for the outgoing IP address
   addr       the mail address being handled (for setting errors)
-  changed    if not NULL, set TRUE if expansion actually changed istring
   interface  point this to the interface
   msg        to add to any error message
 
@@ -36,7 +35,7 @@
 
 BOOL
 smtp_get_interface(uschar *istring, int host_af, address_item *addr,
-  BOOL *changed, uschar **interface, uschar *msg)
+  uschar **interface, uschar *msg)
 {
 const uschar * expint;
 uschar *iface;
@@ -54,8 +53,6 @@
   return FALSE;
   }
 
-if (changed != NULL) *changed = expint != istring;
-
 while (isspace(*expint)) expint++;
 if (*expint == 0) return TRUE;
 
diff -ru a/structs.h b/structs.h
--- a/src/structs.h	2016-03-02 18:27:51.000000000 +0100
+++ b/src/structs.h	2017-04-24 09:33:17.372999860 +0200
@@ -657,6 +657,16 @@
   uschar  name[1];                /* node name - variable length */
 } tree_node;
 
+/* Structure for holding time-limited data such as DNS returns.
+We use this rather than extending tree_node to avoid wasting
+space for most tree use (variables...) at the cost of complexity
+for the lookups cache */
+
+typedef struct expiring_data {
+  time_t expiry;		  /* if nonzero, data invalid after this time */
+  void   *ptr;			  /* pointer to data */
+} expiring_data;
+
 /* Structure for holding the handle and the cached last lookup for searches.
 This block is pointed to by the tree entry for the file. The file can get
 closed if too many are opened at once. There is a LRU chain for deciding which
@@ -676,6 +686,7 @@
 typedef struct {
   uschar  name[DNS_MAXNAME];      /* domain name */
   int     type;                   /* record type */
+  unsigned short ttl;		  /* time-to-live, seconds */
   int     size;                   /* size of data */
   uschar *data;                   /* pointer to data */
 } dns_record;
diff -ru a/tls-gnu.c b/tls-gnu.c
--- a/src/tls-gnu.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/tls-gnu.c	2017-04-24 09:33:17.376999910 +0200
@@ -1729,6 +1729,7 @@
 
   if (!sigalrm_seen)
     {
+    gnutls_certificate_free_credentials(state->x509_cred);
     (void)fclose(smtp_out);
     (void)fclose(smtp_in);
     }
@@ -2014,6 +2015,8 @@
   }
 
 gnutls_deinit(state->session);
+gnutls_certificate_free_credentials(state->x509_cred);
+
 
 state->tlsp->active = -1;
 memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
@@ -2074,6 +2077,8 @@
     receive_smtp_buffered = smtp_buffered;
 
     gnutls_deinit(state->session);
+    gnutls_certificate_free_credentials(state->x509_cred);
+
     state->session = NULL;
     state->tlsp->active = -1;
     state->tlsp->bits = 0;
diff -ru a/transport.c b/transport.c
--- a/src/transport.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/transport.c	2017-04-24 09:33:17.376999910 +0200
@@ -1752,7 +1752,7 @@
     {
     if (split_spool_directory)
 	sprintf(CS spool_file, "%s%c/%s-D",
-		      spool_dir, new_message_id[5], msgq[i].message_id);
+		      spool_dir, msgq[i].message_id[5], msgq[i].message_id);
     else
 	sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id);
 
diff -ru a/transports/smtp.c b/transports/smtp.c
--- a/src/transports/smtp.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/transports/smtp.c	2017-04-24 09:33:17.376999910 +0200
@@ -1274,14 +1274,19 @@
 static BOOL
 smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare)
 {
-uschar * save_sender_address = sender_address;
-uschar * current_local_identity =
+
+uschar * message_local_identity,
+       * current_local_identity,
+       * new_sender_address;
+
+current_local_identity =
   smtp_local_identity(s_compare->current_sender_address, s_compare->tblock);
-uschar * new_sender_address = deliver_get_sender_address(message_id);
-uschar * message_local_identity =
-  smtp_local_identity(new_sender_address, s_compare->tblock);
 
-sender_address = save_sender_address;
+if (!(new_sender_address = deliver_get_sender_address(message_id)))
+    return 0;
+
+message_local_identity =
+  smtp_local_identity(new_sender_address, s_compare->tblock);
 
 return Ustrcmp(current_local_identity, message_local_identity) == 0;
 }
@@ -3169,7 +3174,6 @@
     BOOL serialized = FALSE;
     BOOL host_is_expired = FALSE;
     BOOL message_defer = FALSE;
-    BOOL ifchanges = FALSE;
     BOOL some_deferred = FALSE;
     address_item *first_addr = NULL;
     uschar *interface = NULL;
@@ -3345,15 +3349,18 @@
     if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
 
     /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface
-    string changes upon expansion, we must add it to the key that is used for
-    retries, because connections to the same host from a different interface
-    should be treated separately. */
+    string is set, even if constant (as different transports can have different
+    constant settings), we must add it to the key that is used for retries,
+    because connections to the same host from a different interface should be
+    treated separately. */
 
     host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET : AF_INET6;
-    if (!smtp_get_interface(ob->interface, host_af, addrlist, &ifchanges,
-         &interface, tid))
-      return FALSE;
-    if (ifchanges) pistring = string_sprintf("%s/%s", pistring, interface);
+    if ((rs = ob->interface) && *rs)
+      {
+      if (!smtp_get_interface(rs, host_af, addrlist, &interface, tid))
+	return FALSE;
+      pistring = string_sprintf("%s/%s", pistring, interface);
+      }
 
     /* The first time round the outer loop, check the status of the host by
     inspecting the retry data. The second time round, we are interested only
diff -ru a/verify.c b/verify.c
--- a/src/verify.c	2016-03-02 18:27:51.000000000 +0100
+++ b/src/verify.c	2017-04-24 09:33:17.380999961 +0200
@@ -21,6 +21,7 @@
 /* Structure for caching DNSBL lookups */
 
 typedef struct dnsbl_cache_block {
+  time_t expiry;
   dns_address *rhs;
   uschar *text;
   int rc;
@@ -443,7 +444,7 @@
 
 	  host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
 
-	  if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
+	  if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
 		  US"callout") ||
 	      !smtp_get_port(tf->port, addr, &port, US"callout"))
 	    log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
@@ -578,7 +579,7 @@
     deliver_domain = addr->domain;
     transport_name = addr->transport->name;
 
-    if (  !smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
+    if (  !smtp_get_interface(tf->interface, host_af, addr, &interface,
             US"callout")
        || !smtp_get_port(tf->port, addr, &port, US"callout")
        )
@@ -3584,21 +3585,37 @@
 
 /* Look for this query in the cache. */
 
-t = tree_search(dnsbl_cache, query);
+if (  (t = tree_search(dnsbl_cache, query))
+   && (cb = t->data.ptr)->expiry > time(NULL)
+   )
+
+/* Previous lookup was cached */
+
+  {
+  HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
+  }
 
 /* If not cached from a previous lookup, we must do a DNS lookup, and
 cache the result in permanent memory. */
 
-if (t == NULL)
+else
   {
+  uint ttl = 3600;
+
   store_pool = POOL_PERM;
 
-  /* Set up a tree entry to cache the lookup */
+  if (t)
+    {
+    HDEBUG(D_dnsbl) debug_printf("cached data found but past valid time; ");
+    }
 
-  t = store_get(sizeof(tree_node) + Ustrlen(query));
-  Ustrcpy(t->name, query);
-  t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
-  (void)tree_insertnode(&dnsbl_cache, t);
+  else
+    {	/* Set up a tree entry to cache the lookup */
+    t = store_get(sizeof(tree_node) + Ustrlen(query));
+    Ustrcpy(t->name, query);
+    t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
+    (void)tree_insertnode(&dnsbl_cache, t);
+    }
 
   /* Do the DNS loopup . */
 
@@ -3616,7 +3633,10 @@
 
   Quite apart from one A6 RR generating multiple addresses, there are DNS
   lists that return more than one A record, so we must handle multiple
-  addresses generated in that way as well. */
+  addresses generated in that way as well.
+
+  Mark the cache entry with the "now" plus the minimum of the address TTLs,
+  or some suitably far-future time if none were found. */
 
   if (cb->rc == DNS_SUCCEED)
     {
@@ -3634,6 +3654,7 @@
           *addrp = da;
           while (da->next != NULL) da = da->next;
           addrp = &(da->next);
+	  if (ttl > rr->ttl) ttl = rr->ttl;
           }
         }
       }
@@ -3645,17 +3666,10 @@
     if (cb->rhs == NULL) cb->rc = DNS_NODATA;
     }
 
+  cb->expiry = time(NULL)+ttl;
   store_pool = old_pool;
   }
 
-/* Previous lookup was cached */
-
-else
-  {
-  HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
-  cb = t->data.ptr;
-  }
-
 /* We now have the result of the DNS lookup, either newly done, or cached
 from a previous call. If the lookup succeeded, check against the address
 list if there is one. This may be a positive equality list (introduced by
Only in a: version.sh
openSUSE Build Service is sponsored by