Merge branch 'multiqueue_336': Named queues
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 3 Jun 2016 14:50:00 +0000 (15:50 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 3 Jun 2016 14:50:47 +0000 (15:50 +0100)
16 files changed:
1  2 
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/OS/Makefile-Base
src/src/acl.c
src/src/deliver.c
src/src/exim.c
src/src/expand.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/readconf.c
src/src/receive.c
src/src/smtp_in.c
test/runtest
test/stderr/0575
test/stdout/0574

index f165c368aa73873d15774a8e965d971f4f28bbbc,174873d07a0528abc5b3498164bb4c453f280a26..9a10d8d21ee3568d2d077b938f33bebba252679e
@@@ -3825,6 -3825,14 +3825,14 @@@ This option is not intended for use by 
  by Exim in conjunction with the &%-MC%& option. It signifies that the
  remote host supports the ESMTP &_DSN_& extension.
  
+ .new
+ .vitem &%-MCG%&
+ .oindex "&%-MCG%&"
+ This option is not intended for use by external callers. It is used internally
+ by Exim in conjunction with the &%-MC%& option. It signifies that an
+ alternate queue is used, named by the following option.
+ .wen
  .vitem &%-MCP%&
  .oindex "&%-MCP%&"
  This option is not intended for use by external callers. It is used internally
@@@ -4371,7 -4379,10 +4379,10 @@@ relax this restriction (and also the sa
  and &%-S%& options).
  
  .cindex "queue runner" "description of operation"
- The &%-q%& option starts one queue runner process. This scans the queue of
+ .new
+ If other commandline options do not specify an action,
+ .wen
+ the &%-q%& option starts one queue runner process. This scans the queue of
  waiting messages, and runs a delivery process for each one in turn. It waits
  for each delivery process to finish before starting the next one. A delivery
  process may not actually do any deliveries if the retry times for the addresses
@@@ -4456,8 -4467,29 +4467,29 @@@ The &'l'& (the letter &"ell"&) flag spe
  be done. If a message requires any remote deliveries, it remains on the queue
  for later delivery.
  
- .vitem &%-q%&<&'qflags'&>&~<&'start&~id'&>&~<&'end&~id'&>
+ .new
+ .vitem &%-q[q][i][f[f]][l][G<name>[/<time>]]]%&
+ .oindex "&%-qG%&"
+ .cindex queue named
+ .cindex "named queues"
  .cindex "queue" "delivering specific messages"
+ If the &'G'& flag and a name is present, the queue runner operates on the
+ queue with the given name rather than the default queue.
+ The name should not contain a &'/'& character.
+ For a periodic queue run (see below)
+ append to the name a slash and a time value.
+ If other commandline options speicify an action, a &'-qG<name>'& option
+ will specify a queue to operate on.
+ For example:
+ .code
+ exim -bp -qGquarantine
+ mailq -qGquarantime
+ exim -qGoffpeak -Rf @special.domain.example
+ .endd
+ .wen
+ .vitem &%-q%&<&'qflags'&>&~<&'start&~id'&>&~<&'end&~id'&>
  When scanning the queue, Exim can be made to skip over messages whose ids are
  lexically less than a given value by following the &%-q%& option with a
  starting message id. For example:
@@@ -9181,8 -9213,8 +9213,8 @@@ The environment is adjusted by the &%ke
  .cindex "&%extract%&" "substrings by key"
  The key and <&'string1'&> are first expanded separately. Leading and trailing
  white space is removed from the key (but not from any of the strings). The key
 -must not consist entirely of digits. The expanded <&'string1'&> must be of the
 -form:
 +must not be empty and must not consist entirely of digits.
 +The expanded <&'string1'&> must be of the form:
  .display
  <&'key1'&> = <&'value1'&>  <&'key2'&> = <&'value2'&> ...
  .endd
@@@ -10482,7 -10514,7 +10514,7 @@@ variables or headers inside regular exp
  .cindex "SHA-1 hash"
  .cindex "expansion" "SHA-1 hashing"
  .cindex certificate fingerprint
 -.cindex "&%sha2%& expansion item"
 +.cindex "&%sha1%& expansion item"
  The &%sha1%& operator computes the SHA-1 hash value of the string, and returns
  it as a 40-digit hexadecimal number, in which any letters are in upper case.
  
@@@ -10490,38 -10522,16 +10522,38 @@@ If the string is a single variable of t
  returns the SHA-1 hash fingerprint of the certificate.
  
  
 -.vitem &*${sha256:*&<&'certificate'&>&*}*&
 +.vitem &*${sha256:*&<&'string'&>&*}*&
  .cindex "SHA-256 hash"
  .cindex certificate fingerprint
  .cindex "expansion" "SHA-256 hashing"
  .cindex "&%sha256%& expansion item"
 -The &%sha256%& operator computes the SHA-256 hash fingerprint of the
 -certificate,
 +.new
 +The &%sha256%& operator computes the SHA-256 hash value of the string
  and returns
  it as a 64-digit hexadecimal number, in which any letters are in upper case.
 -Only arguments which are a single variable of certificate type are supported.
 +.wen
 +
 +If the string is a single variable of type certificate,
 +returns the SHA-256 hash fingerprint of the certificate.
 +
 +
 +.new
 +.vitem &*${sha3:*&<&'string'&>&*}*& &&&
 +       &*${sha3_<n>:*&<&'string'&>&*}*&
 +.cindex "SHA3 hash"
 +.cindex "expansion" "SHA3 hashing"
 +.cindex "&%sha3%& expansion item"
 +The &%sha3%& operator computes the SHA3-256 hash value of the string
 +and returns
 +it as a 64-digit hexadecimal number, in which any letters are in upper case.
 +
 +If a number is appended, separated by an underbar, it specifies
 +the output length.  Values of 224, 256, 384 and 512 are accepted;
 +with 256 being the default.
 +
 +The &%sha3%& expansion item is only supported if Exim has been
 +compiled with GnuTLS 3.5.0 or later.
 +.wen
  
  
  .vitem &*${stat:*&<&'string'&>&*}*&
@@@ -12210,6 -12220,14 +12242,14 @@@ The value set for the &%qualify_domain%
  The value set for the &%qualify_recipient%& option in the configuration file,
  or if not set, the value of &$qualify_domain$&.
  
+ .new
+ .vitem &$queue_name$&
+ .vindex &$queue_name$&
+ .cindex "named queues"
+ .cindex queues named
+ The name of the spool queue in use; empty for the default queue.
+ .wen
  .vitem &$rcpt_count$&
  .vindex "&$rcpt_count$&"
  When a message is being received by SMTP, this variable contains the number of
@@@ -12798,7 -12816,7 +12838,7 @@@ When a message is received from a remot
  connection, this variable is set to the cipher suite that was negotiated, for
  example DES-CBC3-SHA. In other circumstances, in particular, for message
  received over unencrypted connections, the variable is empty. Testing
 -&$tls_cipher$& for emptiness is one way of distinguishing between encrypted and
 +&$tls_in_cipher$& for emptiness is one way of distinguishing between encrypted and
  non-encrypted connections during ACL processing.
  
  The deprecated &$tls_cipher$& variable is the same as &$tls_in_cipher$& during message reception,
@@@ -15920,7 -15938,7 +15960,7 @@@ large list. In most situations, &%queue
  
  
  
- .option queue_run_max main integer 5
+ .option queue_run_max main integer&!! 5
  .cindex "queue runner" "maximum number of"
  This controls the maximum number of queue runner processes that an Exim daemon
  can run simultaneously. This does not mean that it starts them all at once,
@@@ -15935,6 -15953,13 +15975,13 @@@ the limit, allowing any number of simul
  run. If you do not want queue runs to occur, omit the &%-q%&&'xx'& setting on
  the daemon's command line.
  
+ .new
+ .cindex queues named
+ .condex "named queues"
+ To set limits for different named queues use
+ an expansion depending on the &$queue_name$& variable.
+ .wen
  .option queue_smtp_domains main "domain list&!!" unset
  .cindex "queueing incoming messages"
  .cindex "message" "queueing remote deliveries"
@@@ -28590,6 -28615,19 +28637,19 @@@ all the conditions are true, wherever i
  effect.
  
  
+ .new
+ .vitem &*queue*&&~=&~<&'text'&>
+ This modifier specifies the use of a named queue for spool files
+ for the message.
+ It can only be used before the message is received (i.e. not in
+ the DATA ACL).
+ This could be used, for example, for known high-volume burst sources
+ of traffic, or for quarantine of messages.
+ Separate queue-runner processes will be needed for named queues.
+ If the text after expansion is empty, the default queue is used.
+ .wen
  .vitem &*remove_header*&&~=&~<&'text'&>
  This modifier specifies one or more header names in a colon-separated list
   that are to be removed from an incoming message, assuming, of course, that
@@@ -28734,18 -28772,13 +28794,18 @@@ with &`-d`&, with the output going to 
  &'debuglog'&.  The filename can be adjusted with the &'tag'& option, which
  may access any variables already defined.  The logging may be adjusted with
  the &'opts'& option, which takes the same values as the &`-d`& command-line
 -option.  Some examples (which depend on variables that don't exist in all
 +option.
 +.new
 +Logging may be stopped, and the file removed, with the &'kill'& option.
 +.wen
 +Some examples (which depend on variables that don't exist in all
  contexts):
  .code
        control = debug
        control = debug/tag=.$sender_host_address
        control = debug/opts=+expand+acl
        control = debug/tag=.$message_exim_id/opts=+expand
 +      control = debug/kill
  .endd
  
  
@@@ -35555,7 -35588,6 +35615,7 @@@ the following table
  &`CV  `&        certificate verification status
  &`D   `&        duration of &"no mail in SMTP session"&
  &`DN  `&        distinguished name from peer certificate
 +&`DS  `&        DNSSEC secured lookups
  &`DT  `&        on &`=>`& lines: time taken for a delivery
  &`F   `&        sender address (on delivery lines)
  &`H   `&        host name and IP address
  &`P   `&        on &`<=`& lines: protocol used
  &`    `&        on &`=>`& and &`**`& lines: return path
  &`PRX `&        on &'<='& and&`=>`& lines: proxy address
+ &`Q   `&        alternate queue name
  &`QT  `&        on &`=>`& lines: time spent on queue so far
  &`    `&        on &"Completed"& lines: time spent on queue
  &`R   `&        on &`<=`& lines: reference for local bounce
@@@ -35646,7 -35679,6 +35707,7 @@@ selection marked by asterisks
  &` deliver_time               `&  time taken to perform delivery
  &` delivery_size              `&  add &`S=`&&'nnn'& to => lines
  &`*dnslist_defer              `&  defers of DNS list (aka RBL) lookups
 +&` dnssec                     `&  DNSSEC secured lookups
  &`*etrn                       `&  ETRN commands
  &`*host_lookup_failed         `&  as it says
  &` ident_timeout              `&  timeout for ident connection
@@@ -35754,14 -35786,6 +35815,14 @@@ the &"=>"& line, tagged with S=
  &%dnslist_defer%&: A log entry is written if an attempt to look up a host in a
  DNS black list suffers a temporary error.
  .next
 +.cindex log dnssec
 +.cindex dnssec logging
 +&%dnssec%&: For message acceptance and (attempted) delivery log lines, when
 +dns lookups gave secure results a tag of DS is added.
 +For acceptance this covers the reverse and forward lookups for host name verification.
 +It does not cover helo-name verification.
 +For delivery this covers the SRV, MX, A and/or AAAA lookups.
 +.next
  .cindex "log" "ETRN commands"
  .cindex "ETRN" "logging"
  &%etrn%&: Every valid ETRN command that is received is logged, before the ACL
@@@ -38416,7 -38440,7 +38477,7 @@@ form of the name
  Log lines and Received-by: header lines will acquire a "utf8"
  prefix on the protocol element, eg. utf8esmtp.
  
 -The following expansion operator can be used:
 +The following expansion operators can be used:
  .code
  ${utf8_domain_to_alabel:str}
  ${utf8_domain_from_alabel:str}
diff --combined doc/doc-txt/NewStuff
index 90aefc4fe8dbdbd514fb65a455850e2ffe4ba84d,7cc6ace3973270c994d3132b9cc04d09c5f75673..2a776b7305bccde69e4d1c67a11d894695de1cd6
@@@ -9,18 -9,14 +9,23 @@@ the documentation is updated, this fil
  Version 4.88
  ------------
  
 - 1. The new perl_tainmode option allows to run the embedded perl
 + 1. The new perl_taintmode option allows to run the embedded perl
      interpreter in taint mode.
  
 - 2. Facility for named queues:  A commandline argument can specify
 + 2. New log_selector: dnssec, adds a "DS" tag to acceptance and delivery lines.
 +
 + 3. Speculative debugging, via a "kill" option to the "control=debug" ACL
 +    modifier.
 +
 + 4. New expansion item ${sha3:<string>} / ${sha3_<N>:<string>}.
 +    N can be 224, 256 (default), 384, 512.
 +    With GnuTLS 3.5.0 or later, only.
 +
++ 5. Facility for named queues:  A commandline argument can specify
+     the queue name for a queue operation, and an ACL modifier can set
+     the queue to be used for a message.  A $queue_name variable gives
+     visibility.
  
  Version 4.87
  ------------
diff --combined src/OS/Makefile-Base
index 3995444cfd453d587098e9acb916bc9727f26901,2e77adbd53da715c3b3f92e79cb82d316c71b8ee..b9eaabaa6f79b8a09bc76b66f10359be7aee55a1
@@@ -279,7 -279,6 +279,7 @@@ exipick: Makefile ../src/exipick.sr
        @rm -f exipick
        @sed -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \
          -e "s?SPOOL_DIRECTORY?$(SPOOL_DIRECTORY)?" \
 +        -e "s?BIN_DIRECTORY?$(BIN_DIRECTORY)?" \
          ../src/exipick.src > exipick-t
        @mv exipick-t exipick
        @chmod a+x exipick
@@@ -331,7 -330,7 +331,7 @@@ OBJ_LOOKUPS = lookups/lf_quote.o lookup
  
  OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
          directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \
 -        filtertest.o globals.o dkim.o \
 +        filtertest.o globals.o dkim.o hash.o \
          header.o host.o ip.o log.o lss.o match.o moan.o \
          os.o parse.o queue.o \
          rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
@@@ -410,7 -409,7 +410,7 @@@ exim_tidydb: $(OBJ_TIDYDB
  
  exim_dbmbuild: exim_dbmbuild.o
        @echo "$(LNCC) -o exim_dbmbuild"
 -      $(FE)$(LNCC) -o exim_dbmbuild $(LFLAGS) exim_dbmbuild.o \
 +      $(FE)$(LNCC) $(CFLAGS) $(INCLUDE) -o exim_dbmbuild $(LFLAGS) exim_dbmbuild.o \
          $(LIBS) $(EXTRALIBS) $(DBMLIB)
        @if [ x"$(STRIP_COMMAND)" != x"" ]; then \
          echo $(STRIP_COMMAND) exim_dbmbuild; \
@@@ -448,7 -447,13 +448,13 @@@ MONBIN = em_StripChart.o $(EXIMON_TEXTP
  # The complete modules list also includes some specially compiled versions of
  # code from the main Exim source tree.
  
- OBJ_MONBIN = util-spool_in.o util-store.o util-string.o tod.o tree.o $(MONBIN)
+ OBJ_MONBIN = util-spool_in.o \
+            util-store.o \
+            util-string.o \
+            util-queue.o \
+            tod.o \
+            tree.o \
+            $(MONBIN)
  
  eximon.bin: $(EXIMON_EDITME) eximon $(OBJ_MONBIN) \
              ../exim_monitor/em_version.c
  # in one. This list is overkill, but it doesn't really take much time to
  # rebuild Exim on a modern computer.
  
 -HDRS  =       config.h \
 +HDRS  =       blob.h \
 +      config.h \
        dbfunctions.h \
        dbstuff.h \
        exim.h \
        functions.h \
        globals.h \
 +      hash.h \
        local_scan.h \
        macros.h \
        mytypes.h \
 +      sha_ver.h \
        structs.h \
        os.h
  PHDRS = ../config.h \
@@@ -568,6 -570,10 +574,10 @@@ util-string.o:   $(HDRS) string.
        @echo "$(CC) -DCOMPILE_UTILITY string.c"
        $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-string.o string.c
  
+ util-queue.o:   $(HDRS) queue.c
+       @echo "$(CC) -DCOMPILE_UTILITY queue.c"
+       $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-queue.o queue.c
  util-os.o:       $(HDRS) os.c
        @echo "$(CC) -DCOMPILE_UTILITY os.c"
        $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) \
@@@ -602,7 -608,6 +612,7 @@@ environment.o:   $(HDRS) environment.
  filter.o:        $(HDRS) filter.c
  filtertest.o:    $(HDRS) filtertest.c
  globals.o:       $(HDRS) globals.c
 +hash.o:          $(HDRS) hash.c
  header.o:        $(HDRS) header.c
  host.o:          $(HDRS) host.c
  ip.o:            $(HDRS) ip.c
diff --combined src/src/acl.c
index dd59b49520d5f065a047e0d5f7142ccb4d33e2a2,0972a23fcd2a9cca9f2a992105580c7a981760da..381fdccf4db6643740e632b7d4ed069ba85dcde7
@@@ -88,6 -88,7 +88,7 @@@ enum { ACLC_ACL
  #ifdef WITH_CONTENT_SCAN
         ACLC_MIME_REGEX,
  #endif
+        ACLC_QUEUE,
         ACLC_RATELIMIT,
         ACLC_RECIPIENTS,
  #ifdef WITH_CONTENT_SCAN
         ACLC_VERIFY };
  
  /* ACL conditions/modifiers: "delay", "control", "continue", "endpass",
- "message", "log_message", "log_reject_target", "logwrite", and "set" are
+ "message", "log_message", "log_reject_target", "logwrite", "queue" and "set" are
  modifiers that look like conditions but always return TRUE. They are used for
  their side effects. */
  
@@@ -152,13 -153,16 +153,16 @@@ static uschar *conditions[] = 
  #ifdef WITH_CONTENT_SCAN
    US"mime_regex",
  #endif
+   US"queue",
    US"ratelimit",
    US"recipients",
  #ifdef WITH_CONTENT_SCAN
    US"regex",
  #endif
    US"remove_header",
-   US"sender_domains", US"senders", US"set",
+   US"sender_domains",
+   US"senders",
+   US"set",
  #ifdef WITH_CONTENT_SCAN
    US"spam",
  #endif
@@@ -298,6 -302,7 +302,7 @@@ static uschar cond_expand_at_top[] = 
  #ifdef WITH_CONTENT_SCAN
    TRUE,    /* mime_regex */
  #endif
+   TRUE,    /* queue */
    TRUE,    /* ratelimit */
    FALSE,   /* recipients */
  #ifdef WITH_CONTENT_SCAN
@@@ -360,6 -365,7 +365,7 @@@ static uschar cond_modifiers[] = 
  #ifdef WITH_CONTENT_SCAN
    FALSE,   /* mime_regex */
  #endif
+   TRUE,    /* queue */
    FALSE,   /* ratelimit */
    FALSE,   /* recipients */
  #ifdef WITH_CONTENT_SCAN
@@@ -507,6 -513,12 +513,12 @@@ static unsigned int cond_forbids[] = 
    ~(1<<ACL_WHERE_MIME),                            /* mime_regex */
    #endif
  
+   (1<<ACL_WHERE_NOTSMTP)|                          /* queue */
+   #ifndef DISABLE_PRDR
+     (1<<ACL_WHERE_PRDR)|
+   #endif
+     (1<<ACL_WHERE_DATA),
    0,                                               /* ratelimit */
  
    (unsigned int)
@@@ -2976,6 -2988,8 +2988,6 @@@ acl_check_condition(int verb, acl_condi
  {
  uschar *user_message = NULL;
  uschar *log_message = NULL;
 -uschar *debug_tag = NULL;
 -uschar *debug_opts = NULL;
  int rc = OK;
  #ifdef WITH_CONTENT_SCAN
  int sep = -'/';
@@@ -3327,39 -3341,24 +3339,39 @@@ for (; cb != NULL; cb = cb->next
        break;
  
        case CONTROL_DEBUG:
 -      while (*p == '/')
          {
 -        if (Ustrncmp(p, "/tag=", 5) == 0)
 -          {
 -          const uschar *pp = p + 5;
 -          while (*pp != '\0' && *pp != '/') pp++;
 -          debug_tag = string_copyn(p+5, pp-p-5);
 -          p = pp;
 -          }
 -        else if (Ustrncmp(p, "/opts=", 6) == 0)
 +        uschar * debug_tag = NULL;
 +        uschar * debug_opts = NULL;
 +        BOOL kill = FALSE;
 +
 +        while (*p == '/')
            {
 -          const uschar *pp = p + 6;
 -          while (*pp != '\0' && *pp != '/') pp++;
 -          debug_opts = string_copyn(p+6, pp-p-6);
 +          const uschar * pp = p+1;
 +          if (Ustrncmp(pp, "tag=", 4) == 0)
 +            {
 +            for (pp += 4; *pp && *pp != '/';) pp++;
 +            debug_tag = string_copyn(p+5, pp-p-5);
 +            }
 +          else if (Ustrncmp(pp, "opts=", 5) == 0)
 +            {
 +            for (pp += 5; *pp && *pp != '/';) pp++;
 +            debug_opts = string_copyn(p+6, pp-p-6);
 +            }
 +          else if (Ustrncmp(pp, "kill", 4) == 0)
 +            {
 +            for (pp += 4; *pp && *pp != '/';) pp++;
 +            kill = TRUE;
 +            }
 +          else
 +            while (*pp && *pp != '/') pp++;
            p = pp;
            }
 +
 +          if (kill)
 +            debug_logging_stop();
 +          else
 +            debug_logging_activate(debug_tag, debug_opts);
          }
 -        debug_logging_activate(debug_tag, debug_opts);
        break;
  
        case CONTROL_SUPPRESS_LOCAL_FIXUPS:
        rc = match_isinlist(dkim_cur_signer,
                            &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
      else
-        rc = FAIL;
+       rc = FAIL;
      break;
  
      case ACLC_DKIM_STATUS:
      break;
      #endif
  
+     case ACLC_QUEUE:
+     queue_name = string_copy_malloc(arg);
+     break;
      case ACLC_RATELIMIT:
      rc = acl_ratelimit(arg, where, log_msgptr);
      break;
diff --combined src/src/deliver.c
index 1421852aa1e8f8aba2216e2567b5720dec9a22a2,0a8d7000251d6a3ddb88ca991bb3f58dfdd1325d..b389c373121c8922e6daf4e866727ab339d9d9fc
@@@ -76,8 -76,6 +76,6 @@@ static int  return_count
  static uschar *frozen_info = US"";
  static uschar *used_return_path = NULL;
  
- static uschar spoolname[PATH_MAX];
  
  
  /*************************************************
@@@ -285,10 -283,9 +283,9 @@@ int fd = Uopen(filename, O_WRONLY|O_APP
  
  if (fd < 0 && errno == ENOENT)
    {
-   uschar temp[16];
-   sprintf(CS temp, "msglog/%s", message_subdir);
-   if (message_subdir[0] == 0) temp[6] = 0;
-   (void)directory_make(spool_directory, temp, MSGLOG_DIRECTORY_MODE, TRUE);
+   (void)directory_make(spool_directory,
+                       spool_sname(US"msglog", message_subdir),
+                       MSGLOG_DIRECTORY_MODE, TRUE);
    fd = Uopen(filename, O_WRONLY|O_APPEND|O_CREAT, mode);
    }
  
@@@ -711,31 -708,25 +708,31 @@@ return s
  
  
  static uschar *
 -d_hostlog(uschar *s, int *sizep, int *ptrp, address_item *addr)
 +d_hostlog(uschar * s, int * sp, int * pp, address_item * addr)
  {
 -s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
 -  US" [", addr->host_used->address, US"]");
 +host_item * h = addr->host_used;
 +
 +s = string_append(s, sp, pp, 2, US" H=", h->name);
 +
 +if (LOGGING(dnssec) && h->dnssec == DS_YES)
 +  s = string_cat(s, sp, pp, US" DS");
 +
 +s = string_append(s, sp, pp, 3, US" [", h->address, US"]");
 +
  if (LOGGING(outgoing_port))
 -  s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
 -    addr->host_used->port));
 +  s = string_append(s, sp, pp, 2, US":", string_sprintf("%d", h->port));
  
  #ifdef SUPPORT_SOCKS
  if (LOGGING(proxy) && proxy_local_address)
    {
 -  s = string_append(s, sizep, ptrp, 3, US" PRX=[", proxy_local_address, US"]");
 +  s = string_append(s, sp, pp, 3, US" PRX=[", proxy_local_address, US"]");
    if (LOGGING(outgoing_port))
 -    s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
 +    s = string_append(s, sp, pp, 2, US":", string_sprintf("%d",
        proxy_local_port));
    }
  #endif
  
 -return d_log_interface(s, sizep, ptrp);
 +return d_log_interface(s, sp, pp);
  }
  
  
@@@ -886,6 -877,9 +883,9 @@@ if (LOGGING(sender_on_delivery) || msg
        sender_address,
    US">");
  
+ if (*queue_name)
+   s = string_append(s, &size, &ptr, 2, US" Q=", queue_name);
  #ifdef EXPERIMENTAL_SRS
  if(addr->prop.srs_sender)
    s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->prop.srs_sender, US">");
@@@ -1273,6 -1267,9 +1273,9 @@@ else if (result == DEFER || result == P
  
      s = string_cat(s, &size, &ptr, log_address);
  
+     if (*queue_name)
+       s = string_append(s, &size, &ptr, 2, US" Q=", queue_name);
      /* Either driver_name contains something and driver_kind contains
      " router" or " transport" (note the leading space), or driver_name is
      a null string and driver_kind contains "routing" without the leading
@@@ -1394,6 -1391,9 +1397,9 @@@ els
    if (LOGGING(sender_on_delivery))
      s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">");
  
+   if (*queue_name)
+     s = string_append(s, &size, &ptr, 2, US" Q=", queue_name);
    /* Return path may not be set if no delivery actually happened */
  
    if (used_return_path && LOGGING(return_path_on_delivery))
@@@ -1950,12 -1950,13 +1956,13 @@@ if (  !shadowin
        || tp->log_output || tp->log_fail_output || tp->log_defer_output
     )  )
    {
-   uschar *error;
+   uschar * error;
    addr->return_filename =
-     string_sprintf("%s/msglog/%s/%s-%d-%d", spool_directory, message_subdir,
-       message_id, getpid(), return_count++);
-   addr->return_file = open_msglog_file(addr->return_filename, 0400, &error);
-   if (addr->return_file < 0)
+     spool_fname(US"msglog", message_subdir, message_id,
+       string_sprintf("-%d-%d", getpid(), return_count++));
+   
+   if ((addr->return_file = open_msglog_file(addr->return_filename, 0400, &error)) < 0)
      {
      common_error(TRUE, addr, errno, US"Unable to %s file for %s transport "
        "to return message: %s", error, tp->name, strerror(errno));
@@@ -4378,13 -4379,13 +4385,13 @@@ for (delivery_count = 0; addr_remote; d
      a dup-with-new-file-pointer. */
  
      (void)close(deliver_datafile);
-     sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir,
-       message_id);
-     deliver_datafile = Uopen(spoolname, O_RDWR | O_APPEND, 0);
+     {
+     uschar * fname = spool_fname(US"input", message_subdir, message_id, US"-D");
  
-     if (deliver_datafile < 0)
+     if ((deliver_datafile = Uopen(fname, O_RDWR | O_APPEND, 0)) < 0)
        log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed to reopen %s for remote "
-         "parallel delivery: %s", spoolname, strerror(errno));
+         "parallel delivery: %s", fname, strerror(errno));
+     }
  
      /* Set the close-on-exec flag */
  
@@@ -5231,53 -5232,51 +5238,51 @@@ store, and also the list of recipients 
  assorted flags. It updates message_size. If there is a reading or format error,
  give up; if the message has been around for sufficiently long, remove it. */
  
- sprintf(CS spoolname, "%s-H", id);
- if ((rc = spool_read_header(spoolname, TRUE, TRUE)) != spool_read_OK)
    {
-   if (errno == ERRNO_SPOOLFORMAT)
+   uschar * spoolname = string_sprintf("%s-H", id);
+   if ((rc = spool_read_header(spoolname, TRUE, TRUE)) != spool_read_OK)
      {
-     struct stat statbuf;
-     sprintf(CS big_buffer, "%s/input/%s/%s", spool_directory, message_subdir,
-       spoolname);
-     if (Ustat(big_buffer, &statbuf) == 0)
-       log_write(0, LOG_MAIN, "Format error in spool file %s: "
-         "size=" OFF_T_FMT, spoolname, statbuf.st_size);
-     else log_write(0, LOG_MAIN, "Format error in spool file %s", spoolname);
-     }
-   else
-     log_write(0, LOG_MAIN, "Error reading spool file %s: %s", spoolname,
-       strerror(errno));
+     if (errno == ERRNO_SPOOLFORMAT)
+       {
+       struct stat statbuf;
+       if (Ustat(spool_fname(US"input", message_subdir, spoolname, US""),
+               &statbuf) == 0)
+       log_write(0, LOG_MAIN, "Format error in spool file %s: "
+         "size=" OFF_T_FMT, spoolname, statbuf.st_size);
+       else
+       log_write(0, LOG_MAIN, "Format error in spool file %s", spoolname);
+       }
+     else
+       log_write(0, LOG_MAIN, "Error reading spool file %s: %s", spoolname,
+       strerror(errno));
  
-   /* If we managed to read the envelope data, received_time contains the
-   time the message was received. Otherwise, we can calculate it from the
-   message id. */
+     /* If we managed to read the envelope data, received_time contains the
+     time the message was received. Otherwise, we can calculate it from the
+     message id. */
  
-   if (rc != spool_read_hdrerror)
-     {
-     received_time = 0;
-     for (i = 0; i < 6; i++)
-       received_time = received_time * BASE_62 + tab62[id[i] - '0'];
-     }
+     if (rc != spool_read_hdrerror)
+       {
+       received_time = 0;
+       for (i = 0; i < 6; i++)
+       received_time = received_time * BASE_62 + tab62[id[i] - '0'];
+       }
  
-   /* If we've had this malformed message too long, sling it. */
+     /* If we've had this malformed message too long, sling it. */
  
-   if (now - received_time > keep_malformed)
-     {
-     sprintf(CS spoolname, "%s/msglog/%s/%s", spool_directory, message_subdir, id);
-     Uunlink(spoolname);
-     sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir, id);
-     Uunlink(spoolname);
-     sprintf(CS spoolname, "%s/input/%s/%s-H", spool_directory, message_subdir, id);
-     Uunlink(spoolname);
-     sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id);
-     Uunlink(spoolname);
-     log_write(0, LOG_MAIN, "Message removed because older than %s",
-       readconf_printtime(keep_malformed));
-     }
+     if (now - received_time > keep_malformed)
+       {
+       Uunlink(spool_fname(US"msglog", message_subdir, id, US""));
+       Uunlink(spool_fname(US"input", message_subdir, id, US"-D"));
+       Uunlink(spool_fname(US"input", message_subdir, id, US"-H"));
+       Uunlink(spool_fname(US"input", message_subdir, id, US"-J"));
+       log_write(0, LOG_MAIN, "Message removed because older than %s",
+       readconf_printtime(keep_malformed));
+       }
  
-   (void)close(deliver_datafile);
-   deliver_datafile = -1;
-   return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
+     (void)close(deliver_datafile);
+     deliver_datafile = -1;
+     return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
+     }
    }
  
  /* The spool header file has been read. Look to see if there is an existing
@@@ -5289,37 -5288,39 +5294,39 @@@ existence, as it will get further succe
  run, and it will be deleted if this function gets to its end successfully.
  Otherwise it might be needed again. */
  
- sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id);
- jread = Ufopen(spoolname, "rb");
- if (jread)
    {
-   while (Ufgets(big_buffer, big_buffer_size, jread))
+   uschar * fname = spool_fname(US"input", message_subdir, id, US"-J");
+   if ((jread = Ufopen(fname, "rb")))
      {
-     int n = Ustrlen(big_buffer);
-     big_buffer[n-1] = 0;
-     tree_add_nonrecipient(big_buffer);
-     DEBUG(D_deliver) debug_printf("Previously delivered address %s taken from "
-       "journal file\n", big_buffer);
+     while (Ufgets(big_buffer, big_buffer_size, jread))
+       {
+       int n = Ustrlen(big_buffer);
+       big_buffer[n-1] = 0;
+       tree_add_nonrecipient(big_buffer);
+       DEBUG(D_deliver) debug_printf("Previously delivered address %s taken from "
+       "journal file\n", big_buffer);
+       }
+     (void)fclose(jread);
+     /* Panic-dies on error */
+     (void)spool_write_header(message_id, SW_DELIVERING, NULL);
+     }
+   else if (errno != ENOENT)
+     {
+     log_write(0, LOG_MAIN|LOG_PANIC, "attempt to open journal for reading gave: "
+       "%s", strerror(errno));
+     return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
      }
-   (void)fclose(jread);
-   /* Panic-dies on error */
-   (void)spool_write_header(message_id, SW_DELIVERING, NULL);
-   }
- else if (errno != ENOENT)
-   {
-   log_write(0, LOG_MAIN|LOG_PANIC, "attempt to open journal for reading gave: "
-     "%s", strerror(errno));
-   return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
-   }
  
- /* A null recipients list indicates some kind of disaster. */
  /* A null recipients list indicates some kind of disaster. */
  
- if (!recipients_list)
-   {
-   (void)close(deliver_datafile);
-   deliver_datafile = -1;
-   log_write(0, LOG_MAIN, "Spool error: no recipients for %s", spoolname);
-   return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
+   if (!recipients_list)
+     {
+     (void)close(deliver_datafile);
+     deliver_datafile = -1;
+     log_write(0, LOG_MAIN, "Spool error: no recipients for %s", fname);
+     return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
+     }
    }
  
  
@@@ -5407,16 -5408,14 +5414,14 @@@ done by rewriting the header spool file
  
  if (message_logs)
    {
-   uschar *error;
+   uschar * fname = spool_fname(US"msglog", message_subdir, id, US"");
+   uschar * error;
    int fd;
  
-   sprintf(CS spoolname, "%s/msglog/%s/%s", spool_directory, message_subdir, id);
-   fd = open_msglog_file(spoolname, SPOOL_MODE, &error);
-   if (fd < 0)
+   if ((fd = open_msglog_file(fname, SPOOL_MODE, &error)) < 0)
      {
      log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't %s message log %s: %s", error,
-       spoolname, strerror(errno));
+       fname, strerror(errno));
      return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
      }
  
    if (!(message_log = fdopen(fd, "a")))
      {
      log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't fdopen message log %s: %s",
-       spoolname, strerror(errno));
+       fname, strerror(errno));
      return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
      }
    }
@@@ -6644,13 -6643,12 +6649,12 @@@ therein are added to the non-recipients
  
  if (addr_local || addr_remote)
    {
-   sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id);
-   journal_fd = Uopen(spoolname, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE);
-   if (journal_fd < 0)
+   uschar * fname = spool_fname(US"input", message_subdir, id, US"-J");
+   
+   if ((journal_fd = Uopen(fname, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE)) <0)
      {
      log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't open journal file %s: %s",
-       spoolname, strerror(errno));
+       fname, strerror(errno));
      return DELIVER_NOT_ATTEMPTED;
      }
  
      || fchmod(journal_fd, SPOOL_MODE)
      )
      {
-     int ret = Uunlink(spoolname);
+     int ret = Uunlink(fname);
      log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't set perms on journal file %s: %s",
-       spoolname, strerror(errno));
+       fname, strerror(errno));
      if(ret  &&  errno != ENOENT)
        log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
-         spoolname, strerror(errno));
+         fname, strerror(errno));
      return DELIVER_NOT_ATTEMPTED;
      }
    }
@@@ -7509,40 -7507,43 +7513,43 @@@ Then delete the message itself. *
  
  if (!addr_defer)
    {
+   uschar * fname;
    if (message_logs)
      {
-     sprintf(CS spoolname, "%s/msglog/%s/%s", spool_directory, message_subdir,
-       id);
+     fname = spool_fname(US"msglog", message_subdir, id, US"");
      if (preserve_message_logs)
        {
        int rc;
-       sprintf(CS big_buffer, "%s/msglog.OLD/%s", spool_directory, id);
-       if ((rc = Urename(spoolname, big_buffer)) < 0)
+       uschar * moname = spool_fname(US"msglog.OLD", US"", id, US"");
+       if ((rc = Urename(fname, moname)) < 0)
          {
-         (void)directory_make(spool_directory, US"msglog.OLD",
-           MSGLOG_DIRECTORY_MODE, TRUE);
-         rc = Urename(spoolname, big_buffer);
+         (void)directory_make(spool_directory,
+                             spool_sname(US"msglog.OLD", US""),
+                             MSGLOG_DIRECTORY_MODE, TRUE);
+         rc = Urename(fname, moname);
          }
        if (rc < 0)
          log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to move %s to the "
-           "msglog.OLD directory", spoolname);
+           "msglog.OLD directory", fname);
        }
      else
-       if (Uunlink(spoolname) < 0)
+       if (Uunlink(fname) < 0)
          log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
-                 spoolname, strerror(errno));
+                 fname, strerror(errno));
      }
  
    /* Remove the two message files. */
  
-   sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir, id);
-   if (Uunlink(spoolname) < 0)
+   fname = spool_fname(US"input", message_subdir, id, US"-D");
+   if (Uunlink(fname) < 0)
      log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
-       spoolname, strerror(errno));
-   sprintf(CS spoolname, "%s/input/%s/%s-H", spool_directory, message_subdir, id);
-   if (Uunlink(spoolname) < 0)
+       fname, strerror(errno));
+   fname = spool_fname(US"input", message_subdir, id, US"-H");
+   if (Uunlink(fname) < 0)
      log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
-       spoolname, strerror(errno));
+       fname, strerror(errno));
  
    /* Log the end of this message, with queue time if requested. */
  
@@@ -8009,9 -8010,10 +8016,10 @@@ if (journal_fd >= 0) (void)close(journa
  
  if (remove_journal)
    {
-   sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id);
-   if (Uunlink(spoolname) < 0 && errno != ENOENT)
-     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", spoolname,
+   uschar * fname = spool_fname(US"input", message_subdir, id, US"-J");
+   if (Uunlink(fname) < 0 && errno != ENOENT)
+     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", fname,
        strerror(errno));
  
    /* Move the message off the spool if reqested */
@@@ -8086,6 -8088,7 +8094,7 @@@ int rc
  uschar * new_sender_address,
         * save_sender_address;
  BOOL save_qr = queue_running;
+ uschar * spoolname;
  
  /* make spool_open_datafile non-noisy on fail */
  
@@@ -8104,7 -8107,7 +8113,7 @@@ spool_read_header() may change all of t
  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);
+ spoolname = string_sprintf("%s-H", id);
  save_sender_address = sender_address;
  
  rc = spool_read_header(spoolname, TRUE, TRUE);
diff --combined src/src/exim.c
index 474851c21ec9a7b577aa21256d62617ed8d35625,1fb543f1b8e4393b0a58d085efc5022f0969afed..08ceab7e130925cae3e8aa8b370e596333312666
@@@ -12,10 -12,6 +12,10 @@@ Also a few functions that don't natural
  
  #include "exim.h"
  
 +#ifdef __GLIBC__
 +# include <gnu/libc-version.h>
 +#endif
 +
  #ifdef USE_GNUTLS
  # include <gnutls/gnutls.h>
  # if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP)
@@@ -1029,14 -1025,6 +1029,14 @@@ DEBUG(D_any) do 
    fprintf(f, "Compiler: <unknown>\n");
  #endif
  
 +#ifdef __GLIBC__
 +  fprintf(f, "Library version: Glibc: Compile: %d.%d\n",
 +              __GLIBC__, __GLIBC_MINOR__);
 +  if (__GLIBC_PREREQ(2, 1))
 +    fprintf(f, "                        Runtime: %s\n",
 +              gnu_get_libc_version());
 +#endif
 +
  #ifdef SUPPORT_TLS
    tls_version_report(f);
  #endif
    characters; unless it's an ancient version of PCRE in which case it
    is not defined. */
  #ifndef PCRE_PRERELEASE
 -#define PCRE_PRERELEASE
 +# define PCRE_PRERELEASE
  #endif
  #define QUOTE(X) #X
  #define EXPAND_AND_QUOTE(X) QUOTE(X)
@@@ -2059,7 -2047,6 +2059,7 @@@ for (i = 1; i < argc; i++
        sender_host_address = argv[i];
        host_checking = checking = log_testing_mode = TRUE;
        host_checking_callout = argrest[1] == 'c';
 +      message_logs = FALSE;
        }
  
      /* -bi: This option is used by sendmail to initialize *the* alias file,
  
      /* -MCD: set the smtp_use_dsn flag; this indicates that the host
         that exim is connected to supports the esmtp extension DSN */
      else if (Ustrcmp(argrest, "CD") == 0)
        {
        smtp_use_dsn = TRUE;
        break;
        }
  
+     /* -MCG: set the queue name, to a non-default value */
+     else if (Ustrcmp(argrest, "CG") == 0)
+       {
+       if (++i < argc) queue_name = string_copy(argv[i]);
+       else badarg = TRUE;
+       break;
+       }
      /* -MCP: set the smtp_use_pipelining flag; this is useful only when
      it preceded -MC (see above) */
  
  
      else if (Ustrcmp(argrest, "CQ") == 0)
        {
-       if(++i < argc) passed_qr_pid = (pid_t)(Uatol(argv[i]));
+       if (++i < argc) passed_qr_pid = (pid_t)(Uatol(argv[i]));
          else badarg = TRUE;
-       if(++i < argc) passed_qr_pipe = (int)(Uatol(argv[i]));
+       if (++i < argc) passed_qr_pipe = (int)(Uatol(argv[i]));
          else badarg = TRUE;
        break;
        }
      if (*argrest == 'f')
        {
        queue_run_force = TRUE;
-       if (*(++argrest) == 'f')
+       if (*++argrest == 'f')
          {
          deliver_force_thaw = TRUE;
          argrest++;
        argrest++;
        }
  
-     /* -q[f][f][l]: Run the queue, optionally forced, optionally local only,
-     optionally starting from a given message id. */
+     /* -q[f][f][l][G<name>]... Work on the named queue */
+     if (*argrest == 'G')
+       {
+       int i;
+       for (argrest++, i = 0; argrest[i] && argrest[i] != '/'; ) i++;
+       queue_name = string_copyn(argrest, i);
+       argrest += i;
+       if (*argrest == '/') argrest++;
+       }
+     /* -q[f][f][l][G<name>]: Run the queue, optionally forced, optionally local
+     only, optionally named, optionally starting from a given message id. */
  
      if (*argrest == 0 &&
          (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1])))
          stop_queue_run_id = argv[++i];
        }
  
-     /* -q[f][f][l]<n>: Run the queue at regular intervals, optionally forced,
-     optionally local only. */
+     /* -q[f][f][l][G<name>/]<n>: Run the queue at regular intervals, optionally
+     forced, optionally local only, optionally named. */
  
-     else
+     else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i],
+                                               0, FALSE)) <= 0)
        {
-       if (*argrest != 0)
-         queue_interval = readconf_readtime(argrest, 0, FALSE);
-       else
-         queue_interval = readconf_readtime(argv[++i], 0, FALSE);
-       if (queue_interval <= 0)
-         {
-         fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]);
-         exit(EXIT_FAILURE);
-         }
+       fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]);
+       exit(EXIT_FAILURE);
        }
      break;
  
      if (*argrest != 0)
        {
        int i;
-       for (i = 0; i < sizeof(rsopts)/sizeof(uschar *); i++)
-         {
+       for (i = 0; i < nelem(rsopts); i++)
          if (Ustrcmp(argrest, rsopts[i]) == 0)
            {
            if (i != 2) queue_run_force = TRUE;
            if (i == 1 || i == 4) deliver_force_thaw = TRUE;
            argrest += Ustrlen(rsopts[i]);
            }
-         }
        }
  
      /* -R: Set string to match in addresses for forced queue run to
      pick out particular messages. */
  
-     if (*argrest == 0)
+     if (*argrest)
+       deliver_selectstring = argrest;
+     else if (i+1 < argc)
+       deliver_selectstring = argv[++i];
+     else
        {
-       if (i+1 < argc) deliver_selectstring = argv[++i]; else
-         {
-         fprintf(stderr, "exim: string expected after -R\n");
-         exit(EXIT_FAILURE);
-         }
+       fprintf(stderr, "exim: string expected after -R\n");
+       exit(EXIT_FAILURE);
        }
-     else deliver_selectstring = argrest;
      break;
  
  
      in all cases provided there are no further characters in this
      argument. */
  
-     if (*argrest != 0)
+     if (*argrest)
        {
        int i;
-       for (i = 0; i < sizeof(rsopts)/sizeof(uschar *); i++)
-         {
+       for (i = 0; i < nelem(rsopts); i++)
          if (Ustrcmp(argrest, rsopts[i]) == 0)
            {
            if (i != 2) queue_run_force = TRUE;
            if (i == 1 || i == 4) deliver_force_thaw = TRUE;
            argrest += Ustrlen(rsopts[i]);
            }
-         }
        }
  
      /* -S: Set string to match in addresses for forced queue run to
      pick out particular messages. */
  
-     if (*argrest == 0)
+     if (*argrest)
+       deliver_selectstring_sender = argrest;
+     else if (i+1 < argc)
+       deliver_selectstring_sender = argv[++i];
+     else
        {
-       if (i+1 < argc) deliver_selectstring_sender = argv[++i]; else
-         {
-         fprintf(stderr, "exim: string expected after -S\n");
-         exit(EXIT_FAILURE);
-         }
+       fprintf(stderr, "exim: string expected after -S\n");
+       exit(EXIT_FAILURE);
        }
-     else deliver_selectstring_sender = argrest;
      break;
  
      /* -Tqt is an option that is exclusively for use by the testing suite.
  
  /* If -R or -S have been specified without -q, assume a single queue run. */
  
- if ((deliver_selectstring != NULL || deliver_selectstring_sender != NULL) &&
-   queue_interval < 0) queue_interval = 0;
+ if (  (deliver_selectstring || deliver_selectstring_sender)
+    && queue_interval < 0)
+     queue_interval = 0;
  
  
  END_ARG:
@@@ -3489,12 -3488,12 +3501,12 @@@ if (
      ) ||
      (
      msg_action_arg > 0 &&
-     (daemon_listen || queue_interval >= 0 || list_options ||
+     (daemon_listen || queue_interval > 0 || list_options ||
        (checking && msg_action != MSG_LOAD) ||
        bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0)
      ) ||
      (
-     (daemon_listen || queue_interval >= 0) &&
+     (daemon_listen || queue_interval > 0) &&
      (sender_address != NULL || list_options || list_queue || checking ||
        bi_option)
      ) ||
@@@ -4670,7 -4669,11 +4682,11 @@@ if (queue_interval == 0 && !daemon_list
      (start_queue_run_id == NULL)? US"" : start_queue_run_id,
      (stop_queue_run_id == NULL)?  US"" : US" stopping at ",
      (stop_queue_run_id == NULL)?  US"" : stop_queue_run_id);
-   set_process_info("running the queue (single queue run)");
+   if (*queue_name)
+     set_process_info(CS string_sprintf(
+       "running the '%s' queue (single queue run)", queue_name));
+   else
+     set_process_info("running the queue (single queue run)");
    queue_run(start_queue_run_id, stop_queue_run_id, FALSE);
    exim_exit(EXIT_SUCCESS);
    }
@@@ -5714,8 -5717,8 +5730,8 @@@ while (more
  
        if (geteuid() != root_uid && !deliver_drop_privilege && !unprivileged)
          {
-         (void)child_exec_exim(CEE_EXEC_EXIT, FALSE, NULL, FALSE, 2, US"-Mc",
-           message_id);
+         (void)child_exec_exim(CEE_EXEC_EXIT, FALSE, NULL, FALSE,
+               2, US"-Mc", message_id);
          /* Control does not return here. */
          }
  
diff --combined src/src/expand.c
index 1484a30272275b11ec14a718a0566c8f7cd18aef,249254923ba4f2d9c484e9802c05fa8294e81377..a0b36f7e2268fd0d5741fa9e2dc87dc355227f67
@@@ -231,7 -231,6 +231,7 @@@ static uschar *op_table_main[] = 
    US"s",
    US"sha1",
    US"sha256",
 +  US"sha3",
    US"stat",
    US"str2b64",
    US"strlen",
@@@ -274,7 -273,6 +274,7 @@@ enum 
    EOP_S,
    EOP_SHA1,
    EOP_SHA256,
 +  EOP_SHA3,
    EOP_STAT,
    EOP_STR2B64,
    EOP_STRLEN,
@@@ -627,6 -625,7 +627,7 @@@ static var_entry var_table[] = 
    { "prvscheck_result",    vtype_stringptr,   &prvscheck_result },
    { "qualify_domain",      vtype_stringptr,   &qualify_domain_sender },
    { "qualify_recipient",   vtype_stringptr,   &qualify_domain_recipient },
+   { "queue_name",          vtype_stringptr,   &queue_name },
    { "rcpt_count",          vtype_int,         &rcpt_count },
    { "rcpt_defer_count",    vtype_int,         &rcpt_defer_count },
    { "rcpt_fail_count",     vtype_int,         &rcpt_fail_count },
@@@ -2508,6 -2507,7 +2509,6 @@@ switch(cond_type
      checking for them individually. */
  
      if (!isalpha(name[0]) && yield != NULL)
 -      {
        if (sub[i][0] == 0)
          {
          num[i] = 0;
          num[i] = expanded_string_integer(sub[i], FALSE);
          if (expand_string_message != NULL) return NULL;
          }
 -      }
      }
  
    /* Result not required */
        uschar digest[16];
  
        md5_start(&base);
 -      md5_end(&base, (uschar *)sub[0], Ustrlen(sub[0]), digest);
 +      md5_end(&base, sub[0], Ustrlen(sub[0]), digest);
  
        /* If the length that we are comparing against is 24, the MD5 digest
        is expressed as a base64 string. This is the way LDAP does it. However,
  
        if (sublen == 24)
          {
 -        uschar *coded = b64encode((uschar *)digest, 16);
 +        uschar *coded = b64encode(digest, 16);
          DEBUG(D_auth) debug_printf("crypteq: using MD5+B64 hashing\n"
            "  subject=%s\n  crypted=%s\n", coded, sub[1]+5);
          tempcond = (Ustrcmp(coded, sub[1]+5) == 0);
      else if (strncmpic(sub[1], US"{sha1}", 6) == 0)
        {
        int sublen = Ustrlen(sub[1]+6);
 -      sha1 base;
 +      hctx h;
        uschar digest[20];
  
 -      sha1_start(&base);
 -      sha1_end(&base, (uschar *)sub[0], Ustrlen(sub[0]), digest);
 +      sha1_start(&h);
 +      sha1_end(&h, sub[0], Ustrlen(sub[0]), digest);
  
        /* If the length that we are comparing against is 28, assume the SHA1
        digest is expressed as a base64 string. If the length is 40, assume a
  
        if (sublen == 28)
          {
 -        uschar *coded = b64encode((uschar *)digest, 20);
 +        uschar *coded = b64encode(digest, 20);
          DEBUG(D_auth) debug_printf("crypteq: using SHA1+B64 hashing\n"
            "  subject=%s\n  crypted=%s\n", coded, sub[1]+6);
          tempcond = (Ustrcmp(coded, sub[1]+6) == 0);
@@@ -3338,7 -3339,7 +3339,7 @@@ chash_start(int type, void *base
  if (type == HMAC_MD5)
    md5_start((md5 *)base);
  else
 -  sha1_start((sha1 *)base);
 +  sha1_start((hctx *)base);
  }
  
  static void
@@@ -3347,7 -3348,7 +3348,7 @@@ chash_mid(int type, void *base, uschar 
  if (type == HMAC_MD5)
    md5_mid((md5 *)base, string);
  else
 -  sha1_mid((sha1 *)base, string);
 +  sha1_mid((hctx *)base, string);
  }
  
  static void
@@@ -3356,7 -3357,7 +3357,7 @@@ chash_end(int type, void *base, uschar 
  if (type == HMAC_MD5)
    md5_end((md5 *)base, string, length, digest);
  else
 -  sha1_end((sha1 *)base, string, length, digest);
 +  sha1_end((hctx *)base, string, length, digest);
  }
  
  
@@@ -3415,7 -3416,8 +3416,7 @@@ prvs_hmac_sha1(uschar *address, uschar 
  {
  uschar *hash_source, *p;
  int size = 0,offset = 0,i;
 -sha1 sha1_base;
 -void *use_base = &sha1_base;
 +hctx h;
  uschar innerhash[20];
  uschar finalhash[20];
  uschar innerkey[64];
@@@ -3444,13 -3446,13 +3445,13 @@@ for (i = 0; i < Ustrlen(key); i++
    outerkey[i] ^= key[i];
    }
  
 -chash_start(HMAC_SHA1, use_base);
 -chash_mid(HMAC_SHA1, use_base, innerkey);
 -chash_end(HMAC_SHA1, use_base, hash_source, offset, innerhash);
 +chash_start(HMAC_SHA1, &h);
 +chash_mid(HMAC_SHA1, &h, innerkey);
 +chash_end(HMAC_SHA1, &h, hash_source, offset, innerhash);
  
 -chash_start(HMAC_SHA1, use_base);
 -chash_mid(HMAC_SHA1, use_base, outerkey);
 -chash_end(HMAC_SHA1, use_base, innerhash, 20, finalhash);
 +chash_start(HMAC_SHA1, &h);
 +chash_mid(HMAC_SHA1, &h, outerkey);
 +chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash);
  
  p = finalhash_hex;
  for (i = 0; i < 3; i++)
@@@ -5143,7 -5145,7 +5144,7 @@@ while (*s != 0
        {
        uschar *sub[3];
        md5 md5_base;
 -      sha1 sha1_base;
 +      hctx sha1_ctx;
        void *use_base;
        int type, i;
        int hashlen;      /* Number of octets for the hash algorithm's output */
        else if (Ustrcmp(sub[0], "sha1") == 0)
          {
          type = HMAC_SHA1;
 -        use_base = &sha1_base;
 +        use_base = &sha1_ctx;
          hashlen = 20;
          hashblocklen = 64;
          }
        else
  #endif
          {
 -        sha1 base;
 +        hctx h;
          uschar digest[20];
          int j;
          char st[41];
 -        sha1_start(&base);
 -        sha1_end(&base, sub, Ustrlen(sub), digest);
 +        sha1_start(&h);
 +        sha1_end(&h, sub, Ustrlen(sub), digest);
          for(j = 0; j < 20; j++) sprintf(st+2*j, "%02X", digest[j]);
 -        yield = string_cat(yield, &size, &ptr, US st);
 +        yield = string_catn(yield, &size, &ptr, US st, 40);
          }
          continue;
  
        case EOP_SHA256:
 -#ifdef SUPPORT_TLS
 +#ifdef EXIM_HAVE_SHA2
        if (vp && *(void **)vp->value)
          {
          uschar * cp = tls_cert_fprt_sha256(*(void **)vp->value);
          yield = string_cat(yield, &size, &ptr, cp);
          }
        else
 +        {
 +        hctx h;
 +        blob b;
 +        char st[3];
 +
 +        exim_sha_init(&h, HASH_SHA256);
 +        exim_sha_update(&h, sub, Ustrlen(sub));
 +        exim_sha_finish(&h, &b);
 +        while (b.len-- > 0)
 +          {
 +          sprintf(st, "%02X", *b.data++);
 +          yield = string_catn(yield, &size, &ptr, US st, 2);
 +          }
 +        }
 +#else
 +        expand_string_message = US"sha256 only supported with TLS";
  #endif
 -        expand_string_message = US"sha256 only supported for certificates";
          continue;
  
 +      case EOP_SHA3:
 +#ifdef EXIM_HAVE_SHA3
 +      {
 +      hctx h;
 +      blob b;
 +      char st[3];
 +      hashmethod m = !arg ? HASH_SHA3_256
 +        : Ustrcmp(arg, "224") == 0 ? HASH_SHA3_224
 +        : Ustrcmp(arg, "256") == 0 ? HASH_SHA3_256
 +        : Ustrcmp(arg, "384") == 0 ? HASH_SHA3_384
 +        : Ustrcmp(arg, "512") == 0 ? HASH_SHA3_512
 +        : HASH_BADTYPE;
 +
 +      if (m == HASH_BADTYPE)
 +        {
 +        expand_string_message = US"unrecognised sha3 variant";
 +        goto EXPAND_FAILED;
 +        }
 +
 +      exim_sha_init(&h, m);
 +      exim_sha_update(&h, sub, Ustrlen(sub));
 +      exim_sha_finish(&h, &b);
 +      while (b.len-- > 0)
 +        {
 +        sprintf(st, "%02X", *b.data++);
 +        yield = string_catn(yield, &size, &ptr, US st, 2);
 +        }
 +      }
 +        continue;
 +#else
 +      expand_string_message = US"sha3 only supported with GnuTLS 3.5.0 +";
 +      goto EXPAND_FAILED;
 +#endif
 +
        /* Convert hex encoding to base64 encoding */
  
        case EOP_HEX2B64:
diff --combined src/src/functions.h
index cc4cb7ea6eb88255c81865e36f22241b8f1945d1,0956c4069fc158a9543c082c46481702122f24b2..ebbdc55e291bb7144fcd293935f3c49e00ddcb3a
@@@ -123,7 -123,6 +123,7 @@@ extern int     dcc_process(uschar **)
  #endif
  
  extern void    debug_logging_activate(uschar *, uschar *);
 +extern void    debug_logging_stop(void);
  extern void    debug_print_argv(const uschar **);
  extern void    debug_print_ids(uschar *);
  extern void    debug_print_string(uschar *);
@@@ -371,9 -370,9 +371,9 @@@ extern int     search_findtype_partial(
  extern void   *search_open(uschar *, int, int, uid_t *, gid_t *);
  extern void    search_tidyup(void);
  extern void    set_process_info(const char *, ...) PRINTF_FUNCTION(1,2);
 -extern void    sha1_end(sha1 *, const uschar *, int, uschar *);
 -extern void    sha1_mid(sha1 *, const uschar *);
 -extern void    sha1_start(sha1 *);
 +extern void    sha1_end(hctx *, const uschar *, int, uschar *);
 +extern void    sha1_mid(hctx *, const uschar *);
 +extern void    sha1_start(hctx *);
  extern int     sieve_interpret(uschar *, int, uschar *, uschar *, uschar *,
                   uschar *, address_item **, uschar **);
  extern void    sigalrm_handler(int);
@@@ -407,6 -406,8 +407,8 @@@ extern int     spam(const uschar **)
  extern FILE   *spool_mbox(unsigned long *, const uschar *);
  #endif
  extern BOOL    spool_move_message(uschar *, uschar *, uschar *, uschar *);
+ extern uschar *spool_fname(const uschar *, uschar *, uschar *, uschar *);
+ extern uschar *spool_sname(const uschar *, uschar *);
  extern int     spool_open_datafile(uschar *);
  extern int     spool_open_temp(uschar *);
  extern int     spool_read_header(uschar *, BOOL, BOOL);
diff --combined src/src/globals.c
index e7a54c4b8a1b4ede8b40e10e1a5868354b24f22f,3ba82e0a79821e2fa363841b4fdd0707f4a0a39f..4f5a922b4a32cca3331b40040025d9e6664e1b84
@@@ -144,6 -144,9 +144,6 @@@ uschar *dsn_advertise_hosts    = NULL
  #ifdef SUPPORT_TLS
  BOOL    gnutls_compat_mode     = FALSE;
  BOOL    gnutls_allow_auto_pkcs11 = FALSE;
 -uschar *gnutls_require_mac     = NULL;
 -uschar *gnutls_require_kx      = NULL;
 -uschar *gnutls_require_proto   = NULL;
  uschar *openssl_options        = NULL;
  const pcre *regex_STARTTLS     = NULL;
  uschar *tls_advertise_hosts    = US"*";
@@@ -861,7 -864,6 +861,7 @@@ bit_table log_options[]        = { /* m
    BIT_TABLE(L, deliver_time),
    BIT_TABLE(L, delivery_size),
    BIT_TABLE(L, dnslist_defer),
 +  BIT_TABLE(L, dnssec),
    BIT_TABLE(L, etrn),
    BIT_TABLE(L, host_lookup_failed),
    BIT_TABLE(L, ident_timeout),
@@@ -1018,6 -1020,7 +1018,7 @@@ BOOL    queue_2stage           = FALSE
  uschar *queue_domains          = NULL;
  int     queue_interval         = -1;
  BOOL    queue_list_requires_admin = TRUE;
+ uschar *queue_name             = US"";
  BOOL    queue_only             = FALSE;
  uschar *queue_only_file        = NULL;
  int     queue_only_load        = -1;
@@@ -1028,7 -1031,7 +1029,7 @@@ BOOL    queue_run_first_delivery = FALS
  BOOL    queue_run_force        = FALSE;
  BOOL    queue_run_in_order     = FALSE;
  BOOL    queue_run_local        = FALSE;
int     queue_run_max          = 5;
uschar *queue_run_max          = US"5";
  pid_t   queue_run_pid          = (pid_t)0;
  int     queue_run_pipe         = -1;
  BOOL    queue_running          = FALSE;
diff --combined src/src/globals.h
index 1caf8e8a92dcf2449e90ce8b9cb285b1d9a24842,362c2bfb9a557f6f71c45da6e2d36e44dcc5715e..6e42bc3d7d56769c5266652053059d0310939fa6
@@@ -108,6 -108,9 +108,6 @@@ extern tls_support tls_out
  #ifdef SUPPORT_TLS
  extern BOOL    gnutls_compat_mode;     /* Less security, more compatibility */
  extern BOOL    gnutls_allow_auto_pkcs11; /* Let GnuTLS autoload PKCS11 modules */
 -extern uschar *gnutls_require_mac;     /* So some can be avoided */
 -extern uschar *gnutls_require_kx;      /* So some can be avoided */
 -extern uschar *gnutls_require_proto;   /* So some can be avoided */
  extern uschar *openssl_options;        /* OpenSSL compatibility options */
  extern const pcre *regex_STARTTLS;     /* For recognizing STARTTLS settings */
  extern uschar *tls_certificate;        /* Certificate file */
@@@ -666,6 -669,7 +666,7 @@@ extern BOOL    queue_running;          
  extern pid_t   queue_run_pid;          /* PID of the queue running process or 0 */
  extern int     queue_run_pipe;         /* Pipe for synchronizing */
  extern int     queue_interval;         /* Queue running interval */
+ extern uschar *queue_name;             /* Name of queue, if nondefault spooling */
  extern BOOL    queue_only;             /* TRUE to disable immediate delivery */
  extern int     queue_only_load;        /* Max load before auto-queue */
  extern BOOL    queue_only_load_latch;  /* Latch queue_only_load TRUE */
@@@ -673,7 -677,7 +674,7 @@@ extern uschar *queue_only_file;        
  extern BOOL    queue_only_override;    /* Allow override from command line */
  extern BOOL    queue_only_policy;      /* ACL or local_scan wants queue_only */
  extern BOOL    queue_run_in_order;     /* As opposed to random */
- extern int     queue_run_max;          /* Max queue runners */
+ extern uschar *queue_run_max;          /* Max queue runners */
  extern BOOL    queue_smtp;             /* Disable all immediate STMP (-odqs)*/
  extern uschar *queue_smtp_domains;     /* Ditto, for these domains */
  
diff --combined src/src/readconf.c
index f4a9b2d2333ff029d6d277528eb0751f5afff28c,63a1641227dd4f5b57699c5147c6f70aa4c3e6e6..25ff58eb990824651d43ab243af06334952fc6e3
@@@ -271,6 -271,11 +271,6 @@@ static optionlist optionlist_config[] 
  #ifdef SUPPORT_TLS
    { "gnutls_allow_auto_pkcs11", opt_bool,        &gnutls_allow_auto_pkcs11 },
    { "gnutls_compat_mode",       opt_bool,        &gnutls_compat_mode },
 -  /* These three gnutls_require_* options stopped working in Exim 4.80 */
 -  /* From 4.83 we log a warning; a future relase will remove them */
 -  { "gnutls_require_kx",        opt_stringptr,   &gnutls_require_kx },
 -  { "gnutls_require_mac",       opt_stringptr,   &gnutls_require_mac },
 -  { "gnutls_require_protocols", opt_stringptr,   &gnutls_require_proto },
  #endif
    { "header_line_maxsize",      opt_int,         &header_line_maxsize },
    { "header_maxsize",           opt_int,         &header_maxsize },
    { "queue_only_load_latch",    opt_bool,        &queue_only_load_latch },
    { "queue_only_override",      opt_bool,        &queue_only_override },
    { "queue_run_in_order",       opt_bool,        &queue_run_in_order },
-   { "queue_run_max",            opt_int,         &queue_run_max },
+   { "queue_run_max",            opt_stringptr,   &queue_run_max },
    { "queue_smtp_domains",       opt_stringptr,   &queue_smtp_domains },
    { "receive_timeout",          opt_time,        &receive_timeout },
    { "received_header_text",     opt_stringptr,   &received_header_text },
@@@ -3492,6 -3497,11 +3492,6 @@@ if (openssl_options != NULL
        "openssl_options parse error: %s", openssl_options);
  # endif
    }
 -
 -if (!nowarn && (gnutls_require_kx || gnutls_require_mac || gnutls_require_proto))
 -  log_write(0, LOG_MAIN, "WARNING: main options"
 -      " gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols"
 -      " are obsolete\n");
  #endif        /*SUPPORT_TLS*/
  
  if (!nowarn && !keep_environment && environ && *environ)
diff --combined src/src/receive.c
index 92ec2cd87d59037e7e23d62576eeb0b82df48b3d,8e4384ae1b8953d052f68333f4bcf2a099081d1f..52e041c9019619d3394745a2607837180ff0f5bf
@@@ -23,7 -23,7 +23,7 @@@ extern int dcc_ok
  
  static FILE   *data_file = NULL;
  static int     data_fd = -1;
- static uschar  spool_name[256];
+ static uschar *spool_name = US"";
  
  
  
@@@ -1123,17 -1123,16 +1123,17 @@@ Returns:      the extended strin
  */
  
  static uschar *
 -add_host_info_for_log(uschar *s, int *sizeptr, int *ptrptr)
 +add_host_info_for_log(uschar * s, int * sizeptr, int * ptrptr)
  {
 -if (sender_fullhost != NULL)
 +if (sender_fullhost)
    {
 +  if (LOGGING(dnssec) && sender_host_dnssec)  /*XXX sender_helo_dnssec? */
 +    s = string_cat(s, sizeptr, ptrptr, US" DS");
    s = string_append(s, sizeptr, ptrptr, 2, US" H=", sender_fullhost);
    if (LOGGING(incoming_interface) && interface_address != NULL)
      {
 -    uschar *ss = string_sprintf(" I=[%s]:%d", interface_address,
 -      interface_port);
 -    s = string_cat(s, sizeptr, ptrptr, ss);
 +    s = string_cat(s, sizeptr, ptrptr,
 +      string_sprintf(" I=[%s]:%d", interface_address, interface_port));
      }
    }
  if (sender_ident != NULL)
@@@ -1543,7 -1542,7 +1543,7 @@@ yet, initialize the size and warning co
  message_id[0] = 0;
  data_file = NULL;
  data_fd = -1;
- spool_name[0] = 0;
+ spool_name = US"";
  message_size = 0;
  warning_count = 0;
  received_count = 1;            /* For the one we will add */
@@@ -2472,7 -2471,7 +2472,7 @@@ it will fit. *
  to be the least significant base-62 digit of the time of arrival. Otherwise
  ensure that it is an empty string. */
  
- message_subdir[0] = split_spool_directory? message_id[5] : 0;
+ message_subdir[0] = split_spool_directory ? message_id[5] : 0;
  
  /* Now that we have the message-id, if there is no message-id: header, generate
  one, but only for local (without suppress_local_fixups) or submission mode
@@@ -2861,20 -2860,18 +2861,18 @@@ if (cutthrough.fd >= 0
  
  /* Open a new spool file for the data portion of the message. We need
  to access it both via a file descriptor and a stream. Try to make the
- directory if it isn't there. Note re use of sprintf: spool_directory
- is checked on input to be < 200 characters long. */
+ directory if it isn't there. */
  
- sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory, message_subdir,
  message_id);
- data_fd = Uopen(spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE);
- if (data_fd < 0)
+ spool_name = spool_fname(US"input", message_subdir, message_id, US"-D");
DEBUG(D_receive) debug_printf("Data file name: %s\n", spool_name);
+ if ((data_fd = Uopen(spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE)) < 0)
    {
    if (errno == ENOENT)
      {
-     uschar temp[16];
-     sprintf(CS temp, "input/%s", message_subdir);
-     if (message_subdir[0] == 0) temp[5] = 0;
-     (void)directory_make(spool_directory, temp, INPUT_DIRECTORY_MODE, TRUE);
+     (void) directory_make(spool_directory,
+                       spool_sname(US"input", message_subdir),
+                       INPUT_DIRECTORY_MODE, TRUE);
      data_fd = Uopen(spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE);
      }
    if (data_fd < 0)
@@@ -3650,11 -3647,11 +3648,11 @@@ signal(SIGINT, SIG_IGN)
  deliver_firsttime = TRUE;
  
  #ifdef EXPERIMENTAL_BRIGHTMAIL
- if (bmi_run == 1) {
-   /* rewind data file */
+ if (bmi_run == 1)
+   /* rewind data file */
    lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
    bmi_verdicts = bmi_process_message(header_list, data_fd);
- };
+   }
  #endif
  
  /* Update the timstamp in our Received: header to account for any time taken by
@@@ -3692,7 -3689,6 +3690,6 @@@ if (host_checking || blackholed_by != N
  /* Write the -H file */
  
  else
-   {
    if ((msg_size = spool_write_header(message_id, SW_RECEIVING, &errmsg)) < 0)
      {
      log_write(0, LOG_MAIN, "Message abandoned: %s", errmsg);
        /* Does not return */
        }
      }
-   }
  
  
  /* The message has now been successfully received. */
@@@ -3806,6 -3801,9 +3802,9 @@@ if (LOGGING(8bitmime)
    s = string_append(s, &size, &sptr, 2, US" M8S=", big_buffer);
    }
  
+ if (*queue_name)
+   s = string_append(s, &size, &sptr, 2, US" Q=", queue_name);
  /* If an addr-spec in a message-id contains a quoted string, it can contain
  any characters except " \ and CR and so in particular it can contain NL!
  Therefore, make sure we use a printing-characters only version for the log.
@@@ -3860,16 -3858,15 +3859,15 @@@ if (message_logs && blackholed_by == NU
    {
    int fd;
  
-   sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory, message_subdir,
-     message_id);
-   fd = Uopen(spool_name, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE);
-   if (fd < 0 && errno == ENOENT)
+   spool_name = spool_fname(US"msglog", message_subdir, message_id, US"");
+   
+   if (  (fd = Uopen(spool_name, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE)) < 0
+      && errno == ENOENT
+      )
      {
-     uschar temp[16];
-     sprintf(CS temp, "msglog/%s", message_subdir);
-     if (message_subdir[0] == 0) temp[6] = 0;
-     (void)directory_make(spool_directory, temp, MSGLOG_DIRECTORY_MODE, TRUE);
+     (void)directory_make(spool_directory,
+                       spool_sname(US"msglog", message_subdir),
+                       MSGLOG_DIRECTORY_MODE, TRUE);
      fd = Uopen(spool_name, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE);
      }
  
@@@ -3954,17 -3951,9 +3952,9 @@@ if (smtp_input && sender_host_address !
  
        /* Delete the files for this aborted message. */
  
-       sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory,
-         message_subdir, message_id);
-       Uunlink(spool_name);
-       sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory,
-         message_subdir, message_id);
-       Uunlink(spool_name);
-       sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory,
-         message_subdir, message_id);
-       Uunlink(spool_name);
+       Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
+       Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
+       Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
  
        goto TIDYUP;
        }
@@@ -4118,16 -4107,11 +4108,11 @@@ if (smtp_input
      switch (cutthrough_done)
        {
        case ACCEPTED: log_write(0, LOG_MAIN, "Completed");/* Delivery was done */
-       case PERM_REJ: {                                        /* Delete spool files */
-             sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory,
-               message_subdir, message_id);
-             Uunlink(spool_name);
-             sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory,
-               message_subdir, message_id);
-             Uunlink(spool_name);
-             sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory,
-               message_subdir, message_id);
-             Uunlink(spool_name);
+       case PERM_REJ:
+             {                                          /* Delete spool files */
+             Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
+             Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
+             Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
              }
        case TMP_REJ: message_id[0] = 0;          /* Prevent a delivery from starting */
        default:break;
diff --combined src/src/smtp_in.c
index df66ed02f9bf8edcad3882f73828aee75a8e9e8c,565f4b32ed97ca34e01227b86ad9d56ac3f2e98a..53387011c80892eca22472605a58a88096634b35
@@@ -1729,15 -1729,16 +1729,15 @@@ while (done <= 0
      /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a
      recipient address */
  
 -    recipient = ((rewrite_existflags & rewrite_smtp) != 0)?
 -      rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
 -        global_rewrite_rules) : smtp_cmd_data;
 +    recipient = rewrite_existflags & rewrite_smtp
 +      ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
 +                  global_rewrite_rules)
 +      : smtp_cmd_data;
  
 -    /* rfc821_domains = TRUE; << no longer needed */
      recipient = parse_extract_address(recipient, &errmess, &start, &end,
        &recipient_domain, FALSE);
 -    /* rfc821_domains = FALSE; << no longer needed */
  
 -    if (recipient == NULL)
 +    if (!recipient)
        /* The function moan_smtp_batch() does not return. */
        moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
  
@@@ -2836,18 -2837,14 +2836,18 @@@ is closing if required and return 2.  *
  if (log_reject_target != 0)
    {
  #ifdef SUPPORT_TLS
 -  uschar * s = s_tlslog(NULL, NULL, NULL);
 -  if (!s) s = US"";
 +  uschar * tls = s_tlslog(NULL, NULL, NULL);
 +  if (!tls) tls = US"";
  #else
 -  uschar * s = US"";
 +  uschar * tls = US"";
  #endif
 -  log_write(0, log_reject_target, "%s%s %s%srejected %s%s",
 -    host_and_ident(TRUE), s,
 -    sender_info, (rc == FAIL)? US"" : US"temporarily ", what, log_msg);
 +  log_write(0, log_reject_target, "%s%s%s %s%srejected %s%s",
 +    LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"",
 +    host_and_ident(TRUE),
 +    tls,
 +    sender_info,
 +    rc == FAIL ? US"" : US"temporarily ",
 +    what, log_msg);
    }
  
  if (!drop) return 0;
@@@ -3214,31 -3211,6 +3214,31 @@@ return rc
  
  
  
 +
 +
 +static int
 +qualify_recipient(uschar ** recipient, uschar * smtp_cmd_data, uschar * tag)
 +{
 +int rd;
 +if (allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0)
 +  {
 +  DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
 +    *recipient);
 +  rd = Ustrlen(recipient) + 1;
 +  *recipient = rewrite_address_qualify(*recipient, TRUE);
 +  return rd;
 +  }
 +smtp_printf("501 %s: recipient address must contain a domain\r\n",
 +  smtp_cmd_data);
 +log_write(L_smtp_syntax_error,
 +  LOG_MAIN|LOG_REJECT, "unqualified %s rejected: <%s> %s%s",
 +  tag, *recipient, host_and_ident(TRUE), host_lookup_msg);
 +return 0;
 +}
 +
 +
 +
 +
  /*************************************************
  *       Initialize for SMTP incoming message     *
  *************************************************/
@@@ -4125,11 -4097,13 +4125,11 @@@ while (done <= 0
                    global_rewrite_rules)
        : smtp_cmd_data;
  
 -    /* rfc821_domains = TRUE; << no longer needed */
      raw_sender =
        parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain,
          TRUE);
 -    /* rfc821_domains = FALSE; << no longer needed */
  
 -    if (raw_sender == NULL)
 +    if (!raw_sender)
        {
        done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess);
        break;
      /* Apply SMTP rewriting then extract the working address. Don't allow "<>"
      as a recipient address */
  
 -    recipient = ((rewrite_existflags & rewrite_smtp) != 0)?
 -      rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
 -        global_rewrite_rules) : smtp_cmd_data;
 -
 -    /* rfc821_domains = TRUE; << no longer needed */
 -    recipient = parse_extract_address(recipient, &errmess, &start, &end,
 -      &recipient_domain, FALSE);
 -    /* rfc821_domains = FALSE; << no longer needed */
 +    recipient = rewrite_existflags & rewrite_smtp
 +      ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
 +        global_rewrite_rules)
 +      : smtp_cmd_data;
  
 -    if (recipient == NULL)
 +    if (!(recipient = parse_extract_address(recipient, &errmess, &start, &end,
 +      &recipient_domain, FALSE)))
        {
        done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess);
        rcpt_fail_count++;
      we must always qualify this address, regardless. */
  
      if (recipient_domain == 0)
 -      {
 -      if (allow_unqualified_recipient ||
 -          strcmpic(recipient, US"postmaster") == 0)
 -        {
 -        DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
 -          recipient);
 -        recipient_domain = Ustrlen(recipient) + 1;
 -        recipient = rewrite_address_qualify(recipient, TRUE);
 -        }
 -      else
 +      if (!(recipient_domain = qualify_recipient(&recipient, smtp_cmd_data,
 +                                US"recipient")))
          {
          rcpt_fail_count++;
 -        smtp_printf("501 %s: recipient address must contain a domain\r\n",
 -          smtp_cmd_data);
 -        log_write(L_smtp_syntax_error,
 -          LOG_MAIN|LOG_REJECT, "unqualified recipient rejected: "
 -          "<%s> %s%s", recipient, host_and_ident(TRUE),
 -          host_lookup_msg);
          break;
          }
 -      }
  
      /* Check maximum allowed */
  
  
        HAD(SCH_VRFY);
  
 -      if(!(address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end,
 -          &recipient_domain, FALSE)))
 +      if (!(address = parse_extract_address(smtp_cmd_data, &errmess,
 +            &start, &end, &recipient_domain, FALSE)))
 +      {
        smtp_printf("501 %s\r\n", errmess);
 +      break;
 +      }
 +
 +      if (recipient_domain == 0)
 +      if (!(recipient_domain = qualify_recipient(&address, smtp_cmd_data,
 +                                  US"verify")))
 +        break;
  
 -      else if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy,
 +      if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy,
                    &user_msg, &log_msg)) != OK)
        done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg);
        else
        {
 -      uschar *s = NULL;
 +      uschar * s = NULL;
 +      address_item * addr = deliver_make_addr(address, FALSE);
  
 -      address_item *addr = deliver_make_addr(address, FALSE);
        switch(verify_address(addr, NULL, vopt_is_recipient | vopt_qualify, -1,
               -1, -1, NULL, NULL, NULL))
          {
          break;
          }
        etrn_command = US"exim -R";
-       argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R",
-         smtp_cmd_data);
+       argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE,
+         *queue_name ? 4 : 2,
+       US"-R", smtp_cmd_data,
+       US"-MCG", queue_name);
        }
  
      /* If we are host-testing, don't actually do anything. */
diff --combined test/runtest
index 3403d78de314bdc463ecf14380cf9b8d8cb689be,fb93d8de37bd30c7d4c78f6f298f0b506590c318..6cb90fb7db0c91fdb94fc1e7d44fd07bde572918
@@@ -653,6 -653,6 +653,9 @@@ RESET_AFTER_EXTRA_LINE_READ
    s/waiting for children of \d+/waiting for children of pppp/;
    s/waiting for (\S+) \(\d+\)/waiting for $1 (pppp)/;
  
++  # The spool header file name varies with PID
++  s%^(Writing spool header file: .*/hdr).[0-9]{1,5}%$1.pppp%;
++
    # ======== Port numbers ========
    # Incoming port numbers may vary, but not in daemon startup line.
  
      # Environment cleaning
      next if /\w+ in keep_environment\? (yes|no)/;
  
 -    # The spool header file name varies with PID
 -    s%^(Writing spool header file: .*/hdr).[0-9]{1,5}%$1.pppp%;
 -
      # When Exim is checking the size of directories for maildir, it uses
      # the check_dir_size() function to scan directories. Of course, the order
      # of the files that are obtained using readdir() varies from system to
@@@ -1414,10 -1417,6 +1417,10 @@@ $munges 
      { 'mainlog'   => 's/^(.* SMTP protocol synchronization error .* next input=.{8}).*$/$1<suppressed>/',
        'rejectlog' => 's/^(.* SMTP protocol synchronization error .* next input=.{8}).*$/$1<suppressed>/'},
  
 +    'debuglog_stdout' =>
 +    { 'stdout' => 's/^\d\d:\d\d:\d\d\s+\d+ //;
 +                  s/Process \d+ is ready for new message/Process pppp is ready for new message/'
 +    },
    };
  
  
diff --combined test/stderr/0575
index 4c1a4c0990f599981879103b1f0ee3cfb7ba6388,0000000000000000000000000000000000000000..fb8282a6a6c02614538f8630394b32c477cf6f3c
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,61 @@@
 +Exim version x.yz ....
 +changed uid/gid: forcing real = effective
 +  uid=uuuu gid=CALLER_GID pid=pppp
 +configuration file is TESTSUITE/test-config
 +admin user
 +changed uid/gid: privilege not needed
 +  uid=EXIM_UID gid=EXIM_GID pid=pppp
 +originator: uid=CALLER_UID gid=CALLER_GID login=CALLER name=CALLER_NAME
 +sender address = CALLER@test.ex
 +sender_fullhost = [V4NET.0.0.0]
 +sender_rcvhost = [V4NET.0.0.0]
 +host in hosts_connection_nolog? no (option unset)
 +LOG: smtp_connection MAIN
 +  SMTP connection from [V4NET.0.0.0]
 +host in host_lookup? no (option unset)
 +set_process_info: pppp handling incoming connection from [V4NET.0.0.0]
 +host in host_reject_connection? no (option unset)
 +host in sender_unqualified_hosts? no (option unset)
 +host in recipient_unqualified_hosts? no (option unset)
 +host in helo_verify_hosts? no (option unset)
 +host in helo_try_verify_hosts? no (option unset)
 +host in helo_accept_junk_hosts? no (option unset)
 +SMTP>> 220 mail.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
 +smtp_setup_msg entered
 +SMTP<< mail from:<x@y>
 +SMTP>> 250 OK
 +SMTP<< rcpt to:<x@y>
 +processing "accept"
 +accept: condition test succeeded in inline ACL
 +end of inline ACL: ACCEPT
 +SMTP>> 250 Accepted
 +DSN: orcpt: NULL  flags: 0
 +SMTP<< data
 +SMTP>> 354 Enter message, ending with "." on a line by itself
 +search_tidyup called
 +host in ignore_fromline_hosts? no (option unset)
 +>>Headers received:
 +
 +search_tidyup called
 +>>Headers after rewriting and local additions:
 +
++Data file name: TESTSUITE/spool//input//10HmaX-0005vi-00-D
 +Data file written for message 10HmaX-0005vi-00
 +>>Generated Received: header line
 +P Received: from [V4NET.0.0.0]
 +      by mail.test.ex with smtp (Exim x.yz)
 +      (envelope-from <x@y>)
 +      id 10HmaX-0005vi-00
 +      for x@y; Tue, 2 Mar 1999 09:44:33 +0000
 +calling local_scan(); timeout=300
 +local_scan() returned 0 NULL
 +LOG: MAIN
 +  <= x@y H=[V4NET.0.0.0] P=smtp S=sss
 +SMTP>> 250 OK id=10HmaX-0005vi-00
 +smtp_setup_msg entered
 +SMTP<< quit
 +SMTP>> 221 mail.test.ex closing connection
 +LOG: smtp_connection MAIN
 +  SMTP connection from [V4NET.0.0.0] closed by QUIT
 +search_tidyup called
 +>>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
diff --combined test/stdout/0574
index 8b960505dbf3cd50469bf85500e76b1bca5c64b5,0000000000000000000000000000000000000000..aea0754b746d0a0f532231ce7a11077718a97831
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,61 @@@
- Writing spool header file
 +Connecting to 127.0.0.1 port 1225 ... connected
 +??? 220
 +<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
 +>>> EHLO test.ex
 +??? 250-
 +<<< 250-myhost.test.ex Hello test.ex [127.0.0.1]
 +??? 250-
 +<<< 250-SIZE 52428800
 +??? 250-
 +<<< 250-8BITMIME
 +??? 250-
 +<<< 250-PIPELINING
 +??? 250 
 +<<< 250 HELP
 +>>> MAIL FROM:<tester@test.ex>
 +??? 250
 +<<< 250 OK
 +>>> RCPT TO:<dest@test.ex>
 +??? 250
 +<<< 250 Accepted
 +>>> DATA
 +??? 354
 +<<< 354 Enter message, ending with "." on a line by itself
 +>>> Subject: test
 +>>> 
 +>>> body
 +>>> .
 +??? 250
 +<<< 250 OK id=10HmaX-0005vi-00
 +>>> QUIT
 +??? 221
 +<<< 221 myhost.test.ex closing connection
 +End of script
 +accept: condition test succeeded in ACL "chk_data"
 +end of ACL "chk_data": ACCEPT
 +calling local_scan(); timeout=300
 +local_scan() returned 0 NULL
 +considering: ${tod_full}
 +  expanding: ${tod_full}
 +     result: Tue, 2 Mar 1999 09:44:33 +0000
++Writing spool header file: TESTSUITE/spool//input//hdr.pppp
 +DSN: Write SPOOL :-dsn_envid NULL
 +DSN: Write SPOOL :-dsn_ret 0
 +DSN: Flags :0
 +DSN: **** SPOOL_OUT - address: |dest@test.ex| errorsto: |NULL| orcpt: |NULL| dsn_flags: 0
++Renaming spool header file: TESTSUITE/spool//input//10HmaX-0005vi-00-H
 +Size of headers = sss
 +LOG: MAIN
 +  <= tester@test.ex H=(test.ex) [127.0.0.1] P=esmtp S=sss
 +SMTP>> 250 OK id=10HmaX-0005vi-00
 +search_tidyup called
 +Sender: tester@test.ex
 +Recipients:
 +  dest@test.ex
 +Process pppp is ready for new message
 +smtp_setup_msg entered
 +SMTP<< QUIT
 +SMTP>> 221 myhost.test.ex closing connection
 +LOG: smtp_connection MAIN
 +  SMTP connection from (test.ex) [127.0.0.1] closed by QUIT
 +search_tidyup called