appendfile: refactor
[users/heiko/exim.git] / src / src / transports / appendfile.c
index bdda9047de15518755f67a961eb5de3a979092df..d99a1795679c1d42681706de96d20e6187193753 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/transports/appendfile.c,v 1.2 2004/10/11 09:49:43 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
 /* See the file NOTICE for conditions of use and distribution. */
 
 
 #endif
 
 
 #endif
 
 
-/* Encodings for mailbox formats, and their names. MBX format is actually
-supported only if SUPPORT_MBX is set. */
-
-enum { mbf_unix, mbf_mbx, mbf_smail, mbf_maildir, mbf_mailstore };
-
-static char *mailbox_formats[] = {
-  "unix", "mbx", "smail", "maildir", "mailstore" };
-
-
-/* Check warn threshold only if quota size set or not a percentage threshold
-   percentage check should only be done if quota > 0 */
-
-#define THRESHOLD_CHECK  (ob->quota_warn_threshold_value > 0 && \
-  (!ob->quota_warn_threshold_is_percent || ob->quota_value > 0))
-
-
 /* Options specific to the appendfile transport. They must be in alphabetic
 order (note that "_" comes before the lower case letters). Some of them are
 stored in the publicly visible instance block - these are flagged with the
 opt_public flag. */
 
 optionlist appendfile_transport_options[] = {
 /* Options specific to the appendfile transport. They must be in alphabetic
 order (note that "_" comes before the lower case letters). Some of them are
 stored in the publicly visible instance block - these are flagged with the
 opt_public flag. */
 
 optionlist appendfile_transport_options[] = {
+#ifdef SUPPORT_MAILDIR
+  { "*expand_maildir_use_size_file", opt_stringptr,
+      (void *)offsetof(appendfile_transport_options_block, expand_maildir_use_size_file) },
+#endif
   { "*set_use_fcntl_lock",opt_bool | opt_hidden,
       (void *)offsetof(appendfile_transport_options_block, set_use_fcntl) },
   { "*set_use_flock_lock",opt_bool | opt_hidden,
   { "*set_use_fcntl_lock",opt_bool | opt_hidden,
       (void *)offsetof(appendfile_transport_options_block, set_use_fcntl) },
   { "*set_use_flock_lock",opt_bool | opt_hidden,
@@ -105,8 +91,10 @@ optionlist appendfile_transport_options[] = {
       (void *)offsetof(appendfile_transport_options_block, maildir_retries) },
   { "maildir_tag",       opt_stringptr,
       (void *)offsetof(appendfile_transport_options_block, maildir_tag) },
       (void *)offsetof(appendfile_transport_options_block, maildir_retries) },
   { "maildir_tag",       opt_stringptr,
       (void *)offsetof(appendfile_transport_options_block, maildir_tag) },
-  { "maildir_use_size_file", opt_bool,
+  { "maildir_use_size_file", opt_expand_bool,
       (void *)offsetof(appendfile_transport_options_block, maildir_use_size_file ) } ,
       (void *)offsetof(appendfile_transport_options_block, maildir_use_size_file ) } ,
+  { "maildirfolder_create_regex", opt_stringptr,
+      (void *)offsetof(appendfile_transport_options_block, maildirfolder_create_regex ) },
 #endif  /* SUPPORT_MAILDIR */
 #ifdef SUPPORT_MAILSTORE
   { "mailstore_format",  opt_bool,
 #endif  /* SUPPORT_MAILDIR */
 #ifdef SUPPORT_MAILSTORE
   { "mailstore_format",  opt_bool,
@@ -166,6 +154,16 @@ address can appear in the tables drtables.c. */
 int appendfile_transport_options_count =
   sizeof(appendfile_transport_options)/sizeof(optionlist);
 
 int appendfile_transport_options_count =
   sizeof(appendfile_transport_options)/sizeof(optionlist);
 
+
+#ifdef MACRO_PREDEF
+
+/* Dummy values */
+appendfile_transport_options_block appendfile_transport_option_defaults = {0};
+void appendfile_transport_init(transport_instance *tblock) {}
+BOOL appendfile_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
+
+#else  /*!MACRO_PREDEF*/
+
 /* Default private options block for the appendfile transport. */
 
 appendfile_transport_options_block appendfile_transport_option_defaults = {
 /* Default private options block for the appendfile transport. */
 
 appendfile_transport_options_block appendfile_transport_option_defaults = {
@@ -182,18 +180,20 @@ appendfile_transport_options_block appendfile_transport_option_defaults = {
   NULL,           /* quota_warn_threshold */
   NULL,           /* mailbox_size_string */
   NULL,           /* mailbox_filecount_string */
   NULL,           /* quota_warn_threshold */
   NULL,           /* mailbox_size_string */
   NULL,           /* mailbox_filecount_string */
+  NULL,           /* expand_maildir_use_size_file */
   US"^(?:cur|new|\\..*)$",  /* maildir_dir_regex */
   NULL,           /* maildir_tag */
   US"^(?:cur|new|\\..*)$",  /* maildir_dir_regex */
   NULL,           /* maildir_tag */
+  NULL,           /* maildirfolder_create_regex */
   NULL,           /* mailstore_prefix */
   NULL,           /* mailstore_suffix */
   NULL,           /* check_string (default changed for non-bsmtp file)*/
   NULL,           /* escape_string (ditto) */
   NULL,           /* file_format */
   NULL,           /* mailstore_prefix */
   NULL,           /* mailstore_suffix */
   NULL,           /* check_string (default changed for non-bsmtp file)*/
   NULL,           /* escape_string (ditto) */
   NULL,           /* file_format */
+  0,              /* quota_value */
+  0,              /* quota_warn_threshold_value */
   -1,             /* mailbox_size_value */
   -1,             /* mailbox_filecount_value */
   -1,             /* mailbox_size_value */
   -1,             /* mailbox_filecount_value */
-  0,              /* quota_value */
   0,              /* quota_filecount_value */
   0,              /* quota_filecount_value */
-  0,              /* quota_warn_threshold_value */
   APPENDFILE_MODE,           /* mode */
   APPENDFILE_DIRECTORY_MODE, /* dirmode */
   APPENDFILE_LOCKFILE_MODE,  /* lockfile_mode */
   APPENDFILE_MODE,           /* mode */
   APPENDFILE_DIRECTORY_MODE, /* dirmode */
   APPENDFILE_LOCKFILE_MODE,  /* lockfile_mode */
@@ -228,10 +228,28 @@ appendfile_transport_options_block appendfile_transport_option_defaults = {
   FALSE,          /* mailstore_format */
   FALSE,          /* mbx_format */
   FALSE,          /* quota_warn_threshold_is_percent */
   FALSE,          /* mailstore_format */
   FALSE,          /* mbx_format */
   FALSE,          /* quota_warn_threshold_is_percent */
-  TRUE            /* quota_is_inclusive */
+  TRUE,           /* quota_is_inclusive */
+  FALSE,          /* quota_no_check */
+  FALSE           /* quota_filecount_no_check */
 };
 
 
 };
 
 
+/* Encodings for mailbox formats, and their names. MBX format is actually
+supported only if SUPPORT_MBX is set. */
+
+enum { mbf_unix, mbf_mbx, mbf_smail, mbf_maildir, mbf_mailstore };
+
+static const char *mailbox_formats[] = {
+  "unix", "mbx", "smail", "maildir", "mailstore" };
+
+
+/* Check warn threshold only if quota size set or not a percentage threshold
+   percentage check should only be done if quota > 0 */
+
+#define THRESHOLD_CHECK  (ob->quota_warn_threshold_value > 0 && \
+  (!ob->quota_warn_threshold_is_percent || ob->quota_value > 0))
+
+
 
 /*************************************************
 *              Setup entry point                 *
 
 /*************************************************
 *              Setup entry point                 *
@@ -247,6 +265,8 @@ Arguments:
   tblock     points to the transport instance
   addrlist   addresses about to be delivered (not used)
   dummy      not used (doesn't pass back data)
   tblock     points to the transport instance
   addrlist   addresses about to be delivered (not used)
   dummy      not used (doesn't pass back data)
+  uid        the uid that will be set (not used)
+  gid        the gid that will be set (not used)
   errmsg     where to put an error message
 
 Returns:     OK, FAIL, or DEFER
   errmsg     where to put an error message
 
 Returns:     OK, FAIL, or DEFER
@@ -254,47 +274,57 @@ Returns:     OK, FAIL, or DEFER
 
 static int
 appendfile_transport_setup(transport_instance *tblock, address_item *addrlist,
 
 static int
 appendfile_transport_setup(transport_instance *tblock, address_item *addrlist,
-  transport_feedback *dummy, uschar **errmsg)
+  transport_feedback *dummy, uid_t uid, gid_t gid, uschar **errmsg)
 {
 appendfile_transport_options_block *ob =
   (appendfile_transport_options_block *)(tblock->options_block);
 uschar *q = ob->quota;
 {
 appendfile_transport_options_block *ob =
   (appendfile_transport_options_block *)(tblock->options_block);
 uschar *q = ob->quota;
-int *v = &(ob->quota_value);
-int default_value = 0;
-int i;
+double default_value = 0.0;
 
 addrlist = addrlist;    /* Keep picky compilers happy */
 dummy = dummy;
 
 addrlist = addrlist;    /* Keep picky compilers happy */
 dummy = dummy;
+uid = uid;
+gid = gid;
+
+if (ob->expand_maildir_use_size_file)
+       ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file,
+               US"`maildir_use_size_file` in transport", tblock->name);
 
 /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size,
 mailbox_filecount */
 
 
 /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size,
 mailbox_filecount */
 
-for (i = 0; i < 5; i++)
+for (int i = 0; i < 5; i++)
   {
   {
-  if (q == NULL) *v = default_value; else
+  double d;
+  int no_check = 0;
+  uschar *which = NULL;
+
+  if (q == NULL) d = default_value;
+  else
     {
     {
-    double d;
     uschar *rest;
     uschar *s = expand_string(q);
 
     uschar *rest;
     uschar *s = expand_string(q);
 
-    if (s == NULL)
+    if (!s)
       {
       *errmsg = string_sprintf("Expansion of \"%s\" in %s transport failed: "
         "%s", q, tblock->name, expand_string_message);
       {
       *errmsg = string_sprintf("Expansion of \"%s\" in %s transport failed: "
         "%s", q, tblock->name, expand_string_message);
-      return search_find_defer? DEFER : FAIL;
+      return f.search_find_defer ? DEFER : FAIL;
       }
 
     d = Ustrtod(s, &rest);
 
       }
 
     d = Ustrtod(s, &rest);
 
-    /* Handle following characters K, M, %, the latter being permitted
+    /* Handle following characters K, M, G, %, the latter being permitted
     for quota_warn_threshold only. A threshold with no quota setting is
     just ignored. */
 
     if (tolower(*rest) == 'k') { d *= 1024.0; rest++; }
     else if (tolower(*rest) == 'm') { d *= 1024.0*1024.0; rest++; }
     for quota_warn_threshold only. A threshold with no quota setting is
     just ignored. */
 
     if (tolower(*rest) == 'k') { d *= 1024.0; rest++; }
     else if (tolower(*rest) == 'm') { d *= 1024.0*1024.0; rest++; }
+    else if (tolower(*rest) == 'g') { d *= 1024.0*1024.0*1024.0; rest++; }
     else if (*rest == '%' && i == 2)
       {
     else if (*rest == '%' && i == 2)
       {
-      if (ob->quota_value <= 0 && !ob->maildir_use_size_file) d = 0;
+      if (ob->quota_value <= 0 && !ob->maildir_use_size_file)
+       d = 0;
       else if ((int)d < 0 || (int)d > 100)
         {
         *errmsg = string_sprintf("Invalid quota_warn_threshold percentage (%d)"
       else if ((int)d < 0 || (int)d > 100)
         {
         *errmsg = string_sprintf("Invalid quota_warn_threshold percentage (%d)"
@@ -305,6 +335,15 @@ for (i = 0; i < 5; i++)
       rest++;
       }
 
       rest++;
       }
 
+
+    /* For quota and quota_filecount there may be options
+    appended. Currently only "no_check", so we can be lazy parsing it */
+    if (i < 2 && Ustrstr(rest, "/no_check") == rest)
+      {
+       no_check = 1;
+       rest += sizeof("/no_check") - 1;
+      }
+
     while (isspace(*rest)) rest++;
 
     if (*rest != 0)
     while (isspace(*rest)) rest++;
 
     if (*rest != 0)
@@ -313,32 +352,55 @@ for (i = 0; i < 5; i++)
         "in %s transport", s, q, tblock->name);
       return FAIL;
       }
         "in %s transport", s, q, tblock->name);
       return FAIL;
       }
-
-    *v = (int)d;
     }
 
     }
 
+  /* Set each value, checking for possible overflow. */
+
   switch (i)
     {
     case 0:
   switch (i)
     {
     case 0:
-    q = ob->quota_filecount;
-    v = &(ob->quota_filecount_value);
-    break;
+      if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4)
+       which = US"quota";
+      ob->quota_value = (off_t)d;
+      ob->quota_no_check = no_check;
+      q = ob->quota_filecount;
+      break;
 
     case 1:
 
     case 1:
-    q = ob->quota_warn_threshold;
-    v = &(ob->quota_warn_threshold_value);
-    break;
+      if (d >= 2.0*1024.0*1024.0*1024.0)
+       which = US"quota_filecount";
+      ob->quota_filecount_value = (int)d;
+      ob->quota_filecount_no_check = no_check;
+      q = ob->quota_warn_threshold;
+      break;
 
     case 2:
 
     case 2:
-    q = ob->mailbox_size_string;
-    v = &(ob->mailbox_size_value);
-    default_value = -1;
-    break;
+    if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4)
+       which = US"quota_warn_threshold";
+      ob->quota_warn_threshold_value = (off_t)d;
+      q = ob->mailbox_size_string;
+      default_value = -1.0;
+      break;
 
     case 3:
 
     case 3:
-    q = ob->mailbox_filecount_string;
-    v = &(ob->mailbox_filecount_value);
-    break;
+      if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4)
+       which = US"mailbox_size";;
+      ob->mailbox_size_value = (off_t)d;
+      q = ob->mailbox_filecount_string;
+      break;
+
+    case 4:
+      if (d >= 2.0*1024.0*1024.0*1024.0)
+       which = US"mailbox_filecount";
+      ob->mailbox_filecount_value = (int)d;
+      break;
+    }
+
+  if (which)
+    {
+    *errmsg = string_sprintf("%s value %.10g is too large (overflow) in "
+      "%s transport", which, d, tblock->name);
+    return FAIL;
     }
   }
 
     }
   }
 
@@ -371,19 +433,19 @@ if (ob->lock_retries == 0) ob->lock_retries = 1;
 
 /* Only one of a file name or directory name must be given. */
 
 
 /* Only one of a file name or directory name must be given. */
 
-if (ob->filename != NULL && ob->dirname != NULL)
+if (ob->filename && ob->dirname)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
   "only one of \"file\" or \"directory\" can be specified", tblock->name);
 
 /* If a file name was specified, neither quota_filecount nor quota_directory
 must be given. */
 
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
   "only one of \"file\" or \"directory\" can be specified", tblock->name);
 
 /* If a file name was specified, neither quota_filecount nor quota_directory
 must be given. */
 
-if (ob->filename != NULL)
+if (ob->filename)
   {
   {
-  if (ob->quota_filecount != NULL)
+  if (ob->quota_filecount)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
       "quota_filecount must not be set without \"directory\"", tblock->name);
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
       "quota_filecount must not be set without \"directory\"", tblock->name);
-  if (ob->quota_directory != NULL)
+  if (ob->quota_directory)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
       "quota_directory must not be set without \"directory\"", tblock->name);
   }
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
       "quota_directory must not be set without \"directory\"", tblock->name);
   }
@@ -408,7 +470,6 @@ if (ob->use_flock)
 
 #ifdef SUPPORT_MBX
 if (ob->mbx_format)
 
 #ifdef SUPPORT_MBX
 if (ob->mbx_format)
-  {
   if (!ob->set_use_lockfile && !ob->set_use_fcntl && !ob->set_use_flock &&
       !ob->set_use_mbx_lock)
     {
   if (!ob->set_use_lockfile && !ob->set_use_fcntl && !ob->set_use_flock &&
       !ob->set_use_mbx_lock)
     {
@@ -422,7 +483,6 @@ if (ob->mbx_format)
     if (!ob->set_use_flock) ob->use_flock = FALSE;
     if (!ob->use_fcntl && !ob->use_flock) ob->use_fcntl = TRUE;
     }
     if (!ob->set_use_flock) ob->use_flock = FALSE;
     if (!ob->use_fcntl && !ob->use_flock) ob->use_fcntl = TRUE;
     }
-  }
 #endif  /* SUPPORT_MBX */
 
 if (!ob->use_fcntl && !ob->use_flock && !ob->use_lockfile && !ob->use_mbx_lock)
 #endif  /* SUPPORT_MBX */
 
 if (!ob->use_fcntl && !ob->use_flock && !ob->use_lockfile && !ob->use_mbx_lock)
@@ -438,7 +498,7 @@ if (!ob->use_flock) ob->lock_flock_timeout = 0;
 specified, and if quota_filecount or quota_directory is given, quota must
 be set. */
 
 specified, and if quota_filecount or quota_directory is given, quota must
 be set. */
 
-if (ob->dirname != NULL)
+if (ob->dirname)
   {
   if (ob->maildir_format && ob->mailstore_format)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
   {
   if (ob->maildir_format && ob->mailstore_format)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
@@ -453,19 +513,20 @@ if (ob->dirname != NULL)
 
 /* If a fixed uid field is set, then a gid field must also be set. */
 
 
 /* If a fixed uid field is set, then a gid field must also be set. */
 
-if (tblock->uid_set && !tblock->gid_set && tblock->expand_gid == NULL)
+if (tblock->uid_set && !tblock->gid_set && !tblock->expand_gid)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "user set without group for the %s transport", tblock->name);
 
 /* If "create_file" is set, check that a valid option is given, and set the
 integer variable. */
 
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "user set without group for the %s transport", tblock->name);
 
 /* If "create_file" is set, check that a valid option is given, and set the
 integer variable. */
 
-if (ob->create_file_string != NULL)
+if (ob->create_file_string)
   {
   int value = 0;
   {
   int value = 0;
-  if (Ustrcmp(ob->create_file_string, "anywhere") == 0) value = create_anywhere;
-  else if (Ustrcmp(ob->create_file_string, "belowhome") == 0) value =
-    create_belowhome;
+  if (Ustrcmp(ob->create_file_string, "anywhere") == 0)
+    value = create_anywhere;
+  else if (Ustrcmp(ob->create_file_string, "belowhome") == 0)
+    value = create_belowhome;
   else if (Ustrcmp(ob->create_file_string, "inhome") == 0)
     value = create_inhome;
   else
   else if (Ustrcmp(ob->create_file_string, "inhome") == 0)
     value = create_inhome;
   else
@@ -479,9 +540,9 @@ if (ob->create_file_string != NULL)
 not be used if the actual threshold for a given delivery ends up as zero,
 of if it's given as a percentage and there's no quota setting. */
 
 not be used if the actual threshold for a given delivery ends up as zero,
 of if it's given as a percentage and there's no quota setting. */
 
-if (ob->quota_warn_threshold != NULL)
+if (ob->quota_warn_threshold)
   {
   {
-  if (tblock->warn_message == NULL) tblock->warn_message = US
+  if (!tblock->warn_message) tblock->warn_message = US
     "To: $local_part@$domain\n"
     "Subject: Your mailbox\n\n"
     "This message is automatically created by mail delivery software (Exim).\n\n"
     "To: $local_part@$domain\n"
     "Subject: Your mailbox\n\n"
     "This message is automatically created by mail delivery software (Exim).\n\n"
@@ -502,13 +563,13 @@ if (ob->use_bsmtp)
 /* If not batch SMTP, not maildir, not mailstore, and directory is not set,
 insert default values for for the affixes and the check/escape strings. */
 
 /* If not batch SMTP, not maildir, not mailstore, and directory is not set,
 insert default values for for the affixes and the check/escape strings. */
 
-else if (ob->dirname == NULL && !ob->maildir_format && !ob->mailstore_format)
+else if (!ob->dirname && !ob->maildir_format && !ob->mailstore_format)
   {
   {
-  if (ob->message_prefix == NULL) ob->message_prefix =
+  if (!ob->message_prefix) ob->message_prefix =
     US"From ${if def:return_path{$return_path}{MAILER-DAEMON}} ${tod_bsdinbox}\n";
     US"From ${if def:return_path{$return_path}{MAILER-DAEMON}} ${tod_bsdinbox}\n";
-  if (ob->message_suffix == NULL) ob->message_suffix = US"\n";
-  if (ob->check_string == NULL) ob->check_string = US"From ";
-  if (ob->escape_string == NULL) ob->escape_string = US">From ";
+  if (!ob->message_suffix) ob->message_suffix = US"\n";
+  if (!ob->check_string) ob->check_string = US"From ";
+  if (!ob->escape_string) ob->escape_string = US">From ";
 
   }
 
 
   }
 
@@ -516,12 +577,12 @@ else if (ob->dirname == NULL && !ob->maildir_format && !ob->mailstore_format)
 driver options. Only one of body_only and headers_only can be set. */
 
 ob->options |=
 driver options. Only one of body_only and headers_only can be set. */
 
 ob->options |=
-  (tblock->body_only? topt_no_headers : 0) |
-  (tblock->headers_only? topt_no_body : 0) |
-  (tblock->return_path_add? topt_add_return_path : 0) |
-  (tblock->delivery_date_add? topt_add_delivery_date : 0) |
-  (tblock->envelope_to_add? topt_add_envelope_to : 0) |
-  ((ob->use_crlf || ob->mbx_format)? topt_use_crlf : 0);
+  (tblock->body_only ? topt_no_headers : 0) |
+  (tblock->headers_only ? topt_no_body : 0) |
+  (tblock->return_path_add ? topt_add_return_path : 0) |
+  (tblock->delivery_date_add ? topt_add_delivery_date : 0) |
+  (tblock->envelope_to_add ? topt_add_envelope_to : 0) |
+  ((ob->use_crlf || ob->mbx_format) ? topt_use_crlf : 0);
 }
 
 
 }
 
 
@@ -547,16 +608,15 @@ Returns:     nothing
 */
 
 static void
 */
 
 static void
-notify_comsat(uschar *user, int offset)
+notify_comsat(uschar *user, off_t offset)
 {
 struct servent *sp;
 host_item host;
 {
 struct servent *sp;
 host_item host;
-host_item *h;
-uschar buffer[256];
+uschar * s;
 
 DEBUG(D_transport) debug_printf("notify_comsat called\n");
 
 
 DEBUG(D_transport) debug_printf("notify_comsat called\n");
 
-sprintf(CS buffer, "%.200s@%d\n", user, offset);
+s = string_sprintf("%.200s@" OFF_T_FMT "\n", user, offset);
 
 if ((sp = getservbyname("biff", "udp")) == NULL)
   {
 
 if ((sp = getservbyname("biff", "udp")) == NULL)
   {
@@ -572,10 +632,10 @@ host.next = NULL;
 until one succeeds. However, it appears that at least on some systems, comsat
 doesn't listen on the ::1 address. So for the moment, just force the address to
 be 127.0.0.1. At some future stage, when IPv6 really is superseding IPv4, this
 until one succeeds. However, it appears that at least on some systems, comsat
 doesn't listen on the ::1 address. So for the moment, just force the address to
 be 127.0.0.1. At some future stage, when IPv6 really is superseding IPv4, this
-can be changed. */
+can be changed. (But actually, comsat is probably dying out anyway.) */
 
 /******
 
 /******
-if (host_find_byname(&host, NULL, NULL, FALSE) == HOST_FIND_FAILED)
+if (host_find_byname(&host, NULL, 0, NULL, FALSE) == HOST_FIND_FAILED)
   {
   DEBUG(D_transport) debug_printf("\"localhost\" unknown\n");
   return;
   {
   DEBUG(D_transport) debug_printf("\"localhost\" unknown\n");
   return;
@@ -585,21 +645,20 @@ if (host_find_byname(&host, NULL, NULL, FALSE) == HOST_FIND_FAILED)
 host.address = US"127.0.0.1";
 
 
 host.address = US"127.0.0.1";
 
 
-for (h = &host; h != NULL; h = h->next)
+for (host_item * h = &host; h; h = h->next)
   {
   int sock, rc;
   {
   int sock, rc;
-  int host_af = (Ustrchr(h->address, ':') != NULL)? AF_INET6 : AF_INET;
+  int host_af = Ustrchr(h->address, ':') != NULL ? AF_INET6 : AF_INET;
 
   DEBUG(D_transport) debug_printf("calling comsat on %s\n", h->address);
 
 
   DEBUG(D_transport) debug_printf("calling comsat on %s\n", h->address);
 
-  sock = ip_socket(SOCK_DGRAM, host_af);
-  if (sock < 0) continue;
+  if ((sock = ip_socket(SOCK_DGRAM, host_af)) < 0) continue;
 
   /* Connect never fails for a UDP socket, so don't set a timeout. */
 
 
   /* Connect never fails for a UDP socket, so don't set a timeout. */
 
-  (void)ip_connect(sock, host_af, h->address, ntohs(sp->s_port), 0);
-  rc = send(sock, buffer, Ustrlen(buffer) + 1, 0);
-  close(sock);
+  (void)ip_connect(sock, host_af, h->address, ntohs(sp->s_port), 0, NULL);
+  rc = send(sock, s, Ustrlen(s) + 1, 0);
+  (void)close(sock);
 
   if (rc >= 0) break;
   DEBUG(D_transport)
 
   if (rc >= 0) break;
   DEBUG(D_transport)
@@ -630,7 +689,7 @@ Returns:       pointer to the required transport, or NULL
 transport_instance *
 check_file_format(int cfd, transport_instance *tblock, address_item *addr)
 {
 transport_instance *
 check_file_format(int cfd, transport_instance *tblock, address_item *addr)
 {
-uschar *format =
+const uschar *format =
   ((appendfile_transport_options_block *)(tblock->options_block))->file_format;
 uschar data[256];
 int len = read(cfd, data, sizeof(data));
   ((appendfile_transport_options_block *)(tblock->options_block))->file_format;
 uschar data[256];
 int len = read(cfd, data, sizeof(data));
@@ -645,15 +704,15 @@ if (len == 0) return tblock;
 
 /* Search the formats for a match */
 
 
 /* Search the formats for a match */
 
-while ((s = string_nextinlist(&format,&sep,big_buffer,big_buffer_size))!= NULL)
+while ((s = string_nextinlist(&format,&sep,big_buffer,big_buffer_size)))
   {
   int slen = Ustrlen(s);
   BOOL match = len >= slen && Ustrncmp(data, s, slen) == 0;
   uschar *tp = string_nextinlist(&format, &sep, big_buffer, big_buffer_size);
   {
   int slen = Ustrlen(s);
   BOOL match = len >= slen && Ustrncmp(data, s, slen) == 0;
   uschar *tp = string_nextinlist(&format, &sep, big_buffer, big_buffer_size);
-  if (match)
+
+  if (match && tp)
     {
     {
-    transport_instance *tt;
-    for (tt = transports; tt != NULL; tt = tt->next)
+    for (transport_instance * tt = transports; tt; tt = tt->next)
       if (Ustrcmp(tp, tt->name) == 0)
         {
         DEBUG(D_transport)
       if (Ustrcmp(tp, tt->name) == 0)
         {
         DEBUG(D_transport)
@@ -704,22 +763,20 @@ Returns:        the sum of the sizes of the stattable files
                 zero if the directory cannot be opened
 */
 
                 zero if the directory cannot be opened
 */
 
-int
-check_dir_size(uschar *dirname, int *countptr, const pcre *regex)
+off_t
+check_dir_size(const uschar * dirname, int *countptr, const pcre *regex)
 {
 DIR *dir;
 {
 DIR *dir;
-int sum = 0;
+off_t sum = 0;
 int count = *countptr;
 struct dirent *ent;
 struct stat statbuf;
 
 int count = *countptr;
 struct dirent *ent;
 struct stat statbuf;
 
-dir = opendir(CS dirname);
-if (dir == NULL) return 0;
+if (!(dir = opendir(CS dirname))) return 0;
 
 
-while ((ent = readdir(dir)) != NULL)
+while ((ent = readdir(dir)))
   {
   {
-  uschar *name = US ent->d_name;
-  uschar buffer[1024];
+  uschar * path, * name = US ent->d_name;
 
   if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue;
 
 
   if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue;
 
@@ -727,18 +784,19 @@ while ((ent = readdir(dir)) != NULL)
 
   /* If there's a regex, try to find the size using it */
 
 
   /* If there's a regex, try to find the size using it */
 
-  if (regex != NULL)
+  if (regex)
     {
     int ovector[6];
     if (pcre_exec(regex, NULL, CS name, Ustrlen(name), 0, 0, ovector,6) >= 2)
       {
       uschar *endptr;
     {
     int ovector[6];
     if (pcre_exec(regex, NULL, CS name, Ustrlen(name), 0, 0, ovector,6) >= 2)
       {
       uschar *endptr;
-      int size = Ustrtol(name + ovector[2], &endptr, 10);
+      off_t size = (off_t)Ustrtod(name + ovector[2], &endptr);
       if (endptr == name + ovector[3])
         {
         sum += size;
         DEBUG(D_transport)
       if (endptr == name + ovector[3])
         {
         sum += size;
         DEBUG(D_transport)
-          debug_printf("check_dir_size: size from %s is %d\n", name, size);
+          debug_printf("check_dir_size: size from %s is " OFF_T_FMT "\n", name,
+            size);
         continue;
         }
       }
         continue;
         }
       }
@@ -748,31 +806,26 @@ while ((ent = readdir(dir)) != NULL)
 
   /* No regex or no match for the regex, or captured non-digits */
 
 
   /* No regex or no match for the regex, or captured non-digits */
 
-  if (!string_format(buffer, sizeof(buffer), "%s/%s", dirname, name))
-    {
-    DEBUG(D_transport)
-      debug_printf("check_dir_size: name too long: dir=%s name=%s\n", dirname,
-        name);
-    continue;
-    }
+  path = string_sprintf("%s/%s", dirname, name);
 
 
-  if (Ustat(buffer, &statbuf) < 0)
+  if (Ustat(path, &statbuf) < 0)
     {
     DEBUG(D_transport)
     {
     DEBUG(D_transport)
-      debug_printf("check_dir_size: stat error %d for %s: %s\n", errno, buffer,
+      debug_printf("check_dir_size: stat error %d for %s: %s\n", errno, path,
         strerror(errno));
         strerror(errno));
-    continue;
     }
     }
-
-  if ((statbuf.st_mode & S_IFMT) == S_IFREG)
-    sum += statbuf.st_size;
-  else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
-    sum += check_dir_size(buffer, &count, regex);
+  else
+    if ((statbuf.st_mode & S_IFMT) == S_IFREG)
+      sum += statbuf.st_size / statbuf.st_nlink;
+    else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+      sum += check_dir_size(path, &count, regex);
   }
 
 closedir(dir);
 DEBUG(D_transport)
   }
 
 closedir(dir);
 DEBUG(D_transport)
-  debug_printf("check_dir_size: dir=%s sum=%d count=%d\n", dirname, sum, count);
+  debug_printf("check_dir_size: dir=%s sum=" OFF_T_FMT " count=%d\n", dirname,
+    sum, count);
+
 *countptr = count;
 return sum;
 }
 *countptr = count;
 return sum;
 }
@@ -818,10 +871,10 @@ if (dofcntl)
   {
   if (fcntltime > 0)
     {
   {
   if (fcntltime > 0)
     {
-    alarm(fcntltime);
+    ALARM(fcntltime);
     yield = fcntl(fd, F_SETLKW, &lock_data);
     save_errno = errno;
     yield = fcntl(fd, F_SETLKW, &lock_data);
     save_errno = errno;
-    alarm(0);
+    ALARM_CLR(0);
     errno = save_errno;
     }
   else yield = fcntl(fd, F_SETLK, &lock_data);
     errno = save_errno;
     }
   else yield = fcntl(fd, F_SETLK, &lock_data);
@@ -830,13 +883,13 @@ if (dofcntl)
 #ifndef NO_FLOCK
 if (doflock && (yield >= 0))
   {
 #ifndef NO_FLOCK
 if (doflock && (yield >= 0))
   {
-  int flocktype = (fcntltype == F_WRLCK)? LOCK_EX : LOCK_SH;
+  int flocktype = (fcntltype == F_WRLCK) ? LOCK_EX : LOCK_SH;
   if (flocktime > 0)
     {
   if (flocktime > 0)
     {
-    alarm(flocktime);
+    ALARM(flocktime);
     yield = flock(fd, flocktype);
     save_errno = errno;
     yield = flock(fd, flocktype);
     save_errno = errno;
-    alarm(0);
+    ALARM_CLR(0);
     errno = save_errno;
     }
   else yield = flock(fd, flocktype | LOCK_NB);
     errno = save_errno;
     }
   else yield = flock(fd, flocktype | LOCK_NB);
@@ -874,23 +927,24 @@ Returns:       OK if all went well, DEFER otherwise, with errno preserved
 #define MBX_NUSERFLAGS           30
 
 static int
 #define MBX_NUSERFLAGS           30
 
 static int
-copy_mbx_message(int to_fd, int from_fd, int saved_size)
+copy_mbx_message(int to_fd, int from_fd, off_t saved_size)
 {
 {
-int used, size;
+int used;
+off_t size;
 struct stat statbuf;
 struct stat statbuf;
+transport_ctx tctx = { .u={.fd = to_fd}, .options = topt_not_socket };
 
 /* If the current mailbox size is zero, write a header block */
 
 if (saved_size == 0)
   {
 
 /* If the current mailbox size is zero, write a header block */
 
 if (saved_size == 0)
   {
-  int i;
   uschar *s;
   memset (deliver_out_buffer, '\0', MBX_HDRSIZE);
   sprintf(CS(s = deliver_out_buffer), "*mbx*\015\012%08lx00000000\015\012",
     (long int)time(NULL));
   uschar *s;
   memset (deliver_out_buffer, '\0', MBX_HDRSIZE);
   sprintf(CS(s = deliver_out_buffer), "*mbx*\015\012%08lx00000000\015\012",
     (long int)time(NULL));
-  for (i = 0; i < MBX_NUSERFLAGS; i++)
+  for (int i = 0; i < MBX_NUSERFLAGS; i++)
     sprintf (CS(s += Ustrlen(s)), "\015\012");
     sprintf (CS(s += Ustrlen(s)), "\015\012");
-  if (!transport_write_block (to_fd, deliver_out_buffer, MBX_HDRSIZE))
+  if (!transport_write_block (&tctx, deliver_out_buffer, MBX_HDRSIZE, FALSE))
     return DEFER;
   }
 
     return DEFER;
   }
 
@@ -902,13 +956,13 @@ size, including CRLFs, which is the size of the input (temporary) file. */
 if (fstat(from_fd, &statbuf) < 0) return DEFER;
 size = statbuf.st_size;
 
 if (fstat(from_fd, &statbuf) < 0) return DEFER;
 size = statbuf.st_size;
 
-sprintf (CS deliver_out_buffer, "%s,%lu;%08lx%04x-%08x\015\012",
-  tod_stamp(tod_mbx), (long unsigned int)size, 0L, 0, 0);
+sprintf (CS deliver_out_buffer, "%s," OFF_T_FMT ";%08lx%04x-%08x\015\012",
+  tod_stamp(tod_mbx), size, 0L, 0, 0);
 used = Ustrlen(deliver_out_buffer);
 
 /* Rewind the temporary file, and copy it over in chunks. */
 
 used = Ustrlen(deliver_out_buffer);
 
 /* Rewind the temporary file, and copy it over in chunks. */
 
-lseek(from_fd, 0 , SEEK_SET);
+if (lseek(from_fd, 0 , SEEK_SET) < 0) return DEFER;
 
 while (size > 0)
   {
 
 while (size > 0)
   {
@@ -919,7 +973,7 @@ while (size > 0)
     if (len == 0) errno = ERRNO_MBXLENGTH;
     return DEFER;
     }
     if (len == 0) errno = ERRNO_MBXLENGTH;
     return DEFER;
     }
-  if (!transport_write_block(to_fd, deliver_out_buffer, used + len))
+  if (!transport_write_block(&tctx, deliver_out_buffer, used + len, FALSE))
     return DEFER;
   size -= len;
   used = 0;
     return DEFER;
   size -= len;
   used = 0;
@@ -951,7 +1005,7 @@ check_creation(uschar *filename, int create_file)
 {
 BOOL yield = TRUE;
 
 {
 BOOL yield = TRUE;
 
-if (deliver_home != NULL && create_file != create_anywhere)
+if (deliver_home  &&  create_file != create_anywhere)
   {
   int len = Ustrlen(deliver_home);
   uschar *file = filename;
   {
   int len = Ustrlen(deliver_home);
   uschar *file = filename;
@@ -979,10 +1033,10 @@ if (deliver_home != NULL && create_file != create_anywhere)
   #ifndef NO_REALPATH
   if (yield && create_file == create_belowhome)
     {
   #ifndef NO_REALPATH
   if (yield && create_file == create_belowhome)
     {
-    uschar *slash, *next;
+    uschar *next;
     uschar *rp = NULL;
     uschar *rp = NULL;
-    for (slash = Ustrrchr(file, '/');       /* There is known to be one */
-         rp == NULL && slash > file;        /* Stop if reached beginning */
+    for (uschar * slash = Ustrrchr(file, '/');  /* There is known to be one */
+         !rp && slash > file;                  /* Stop if reached beginning */
          slash = next)
       {
       *slash = 0;
          slash = next)
       {
       *slash = 0;
@@ -998,14 +1052,13 @@ if (deliver_home != NULL && create_file != create_anywhere)
     contain symbolic links, so we have to "realpath" it as well, if
     possible. */
 
     contain symbolic links, so we have to "realpath" it as well, if
     possible. */
 
-    if (rp != NULL)
+    if (rp)
       {
       uschar hdbuffer[PATH_MAX+1];
       uschar *rph = deliver_home;
       int rlen = Ustrlen(big_buffer);
 
       {
       uschar hdbuffer[PATH_MAX+1];
       uschar *rph = deliver_home;
       int rlen = Ustrlen(big_buffer);
 
-      rp = US realpath(CS deliver_home, CS hdbuffer);
-      if (rp != NULL)
+      if ((rp = US realpath(CS deliver_home, CS hdbuffer)))
         {
         rph = hdbuffer;
         len = Ustrlen(rph);
         {
         rph = hdbuffer;
         len = Ustrlen(rph);
@@ -1108,7 +1161,7 @@ directory name) is given, that is, when appending to a single file:
 
      Open with O_WRONLY + O_EXCL + O_CREAT with configured mode, unless we know
      this is via a symbolic link (only possible if allow_symlinks is set), in
 
      Open with O_WRONLY + O_EXCL + O_CREAT with configured mode, unless we know
      this is via a symbolic link (only possible if allow_symlinks is set), in
-     which case don't use O_EXCL, as it dosn't work.
+     which case don't use O_EXCL, as it doesn't work.
 
      If open fails because the file already exists, go to (6f). To avoid
      looping for ever in a situation where the file is continuously being
 
      If open fails because the file already exists, go to (6f). To avoid
      looping for ever in a situation where the file is continuously being
@@ -1198,15 +1251,16 @@ uschar *filecount_msg = US"";
 uschar *path;
 struct utimbuf times;
 struct timeval msg_tv;
 uschar *path;
 struct utimbuf times;
 struct timeval msg_tv;
+BOOL disable_quota = FALSE;
 BOOL isdirectory = FALSE;
 BOOL isfifo = FALSE;
 BOOL wait_for_tick = FALSE;
 uid_t uid = geteuid();     /* See note above */
 gid_t gid = getegid();
 int mbformat;
 BOOL isdirectory = FALSE;
 BOOL isfifo = FALSE;
 BOOL wait_for_tick = FALSE;
 uid_t uid = geteuid();     /* See note above */
 gid_t gid = getegid();
 int mbformat;
-int mode = (addr->mode > 0)? addr->mode : ob->mode;
-int saved_size = -1;
-int mailbox_size = ob->mailbox_size_value;
+int mode = (addr->mode > 0) ? addr->mode : ob->mode;
+off_t saved_size = -1;
+off_t mailbox_size = ob->mailbox_size_value;
 int mailbox_filecount = ob->mailbox_filecount_value;
 int hd = -1;
 int fd = -1;
 int mailbox_filecount = ob->mailbox_filecount_value;
 int hd = -1;
 int fd = -1;
@@ -1243,9 +1297,7 @@ variable (that holds the parent local part). It is, however, in the
 $address_file variable. Below, we update the local part in the address if it
 changes by expansion, so that the final path ends up in the log. */
 
 $address_file variable. Below, we update the local part in the address if it
 changes by expansion, so that the final path ends up in the log. */
 
-if (testflag(addr, af_file) &&
-    ob->filename == NULL &&
-    ob->dirname == NULL)
+if (testflag(addr, af_file) && !ob->filename && !ob->dirname)
   {
   fdname = US"$address_file";
   if (address_file[Ustrlen(address_file)-1] == '/' ||
   {
   fdname = US"$address_file";
   if (address_file[Ustrlen(address_file)-1] == '/' ||
@@ -1258,20 +1310,18 @@ if (testflag(addr, af_file) &&
 explicitly set and (b) a non-address_file delivery, where one of "file" or
 "directory" must be set; initialization ensures that they are not both set. */
 
 explicitly set and (b) a non-address_file delivery, where one of "file" or
 "directory" must be set; initialization ensures that they are not both set. */
 
-if (fdname == NULL)
+if (!fdname)
   {
   {
-  fdname = ob->filename;
-  if (fdname == NULL)
+  if (!(fdname = ob->filename))
     {
     fdname = ob->dirname;
     isdirectory = TRUE;
     }
     {
     fdname = ob->dirname;
     isdirectory = TRUE;
     }
-  if (fdname == NULL)
+  if (!fdname)
     {
     {
-    addr->transport_return = PANIC;
     addr->message = string_sprintf("Mandatory file or directory option "
       "missing from %s transport", tblock->name);
     addr->message = string_sprintf("Mandatory file or directory option "
       "missing from %s transport", tblock->name);
-    return FALSE;
+    goto ret_panic;
     }
   }
 
     }
   }
 
@@ -1279,22 +1329,18 @@ if (fdname == NULL)
 
 if ((ob->maildir_format || ob->mailstore_format) && !isdirectory)
   {
 
 if ((ob->maildir_format || ob->mailstore_format) && !isdirectory)
   {
-  addr->transport_return = PANIC;
   addr->message = string_sprintf("mail%s_format requires \"directory\" "
     "to be specified for the %s transport",
   addr->message = string_sprintf("mail%s_format requires \"directory\" "
     "to be specified for the %s transport",
-    ob->maildir_format? "dir" : "store", tblock->name);
-  return FALSE;
+    ob->maildir_format ? "dir" : "store", tblock->name);
+  goto ret_panic;
   }
 
   }
 
-path = expand_string(fdname);
-
-if (path == NULL)
+if (!(path = expand_string(fdname)))
   {
   {
-  addr->transport_return = PANIC;
   addr->message = string_sprintf("Expansion of \"%s\" (file or directory "
     "name for %s transport) failed: %s", fdname, tblock->name,
     expand_string_message);
   addr->message = string_sprintf("Expansion of \"%s\" (file or directory "
     "name for %s transport) failed: %s", fdname, tblock->name,
     expand_string_message);
-  return FALSE;
+  goto ret_panic;
   }
 
 if (path[0] != '/')
   }
 
 if (path[0] != '/')
@@ -1305,10 +1351,12 @@ if (path[0] != '/')
   return FALSE;
   }
 
   return FALSE;
   }
 
-/* For a file delivery, make sure the local part in the address is updated to
-the true local part. */
+/* For a file delivery, make sure the local part in the address(es) is updated
+to the true local part. */
 
 
-if (testflag(addr, af_file)) addr->local_part = string_copy(path);
+if (testflag(addr, af_file))
+  for (address_item * addr2 = addr; addr2; addr2 = addr2->next)
+    addr2->local_part = string_copy(path);
 
 /* The available mailbox formats depend on whether it is a directory or a file
 delivery. */
 
 /* The available mailbox formats depend on whether it is a directory or a file
 delivery. */
@@ -1317,10 +1365,10 @@ if (isdirectory)
   {
   mbformat =
   #ifdef SUPPORT_MAILDIR
   {
   mbformat =
   #ifdef SUPPORT_MAILDIR
-    (ob->maildir_format)? mbf_maildir :
+    ob->maildir_format ? mbf_maildir :
   #endif
   #ifdef SUPPORT_MAILSTORE
   #endif
   #ifdef SUPPORT_MAILSTORE
-    (ob->mailstore_format)? mbf_mailstore :
+    ob->mailstore_format ? mbf_mailstore :
   #endif
     mbf_smail;
   }
   #endif
     mbf_smail;
   }
@@ -1328,35 +1376,40 @@ else
   {
   mbformat =
   #ifdef SUPPORT_MBX
   {
   mbformat =
   #ifdef SUPPORT_MBX
-    (ob->mbx_format)? mbf_mbx :
+    ob->mbx_format ? mbf_mbx :
   #endif
     mbf_unix;
   }
 
 DEBUG(D_transport)
   {
   #endif
     mbf_unix;
   }
 
 DEBUG(D_transport)
   {
-  debug_printf("appendfile: mode=%o notify_comsat=%d quota=%d warning=%d%s\n"
+  debug_printf("appendfile: mode=%o notify_comsat=%d quota=" OFF_T_FMT
+    "%s%s"
+    " warning=" OFF_T_FMT "%s\n"
     "  %s=%s format=%s\n  message_prefix=%s\n  message_suffix=%s\n  "
     "maildir_use_size_file=%s\n",
     "  %s=%s format=%s\n  message_prefix=%s\n  message_suffix=%s\n  "
     "maildir_use_size_file=%s\n",
-    mode, ob->notify_comsat, ob->quota_value, ob->quota_warn_threshold_value,
-    ob->quota_warn_threshold_is_percent? "%" : "",
-    isdirectory? "directory" : "file",
+    mode, ob->notify_comsat, ob->quota_value,
+    ob->quota_no_check ? " (no_check)" : "",
+    ob->quota_filecount_no_check ? " (no_check_filecount)" : "",
+    ob->quota_warn_threshold_value,
+    ob->quota_warn_threshold_is_percent ? "%" : "",
+    isdirectory ? "directory" : "file",
     path, mailbox_formats[mbformat],
     path, mailbox_formats[mbformat],
-    (ob->message_prefix == NULL)? US"null" : string_printing(ob->message_prefix),
-    (ob->message_suffix == NULL)? US"null" : string_printing(ob->message_suffix),
-    (ob->maildir_use_size_file)? "yes" : "no");
+    !ob->message_prefix ? US"null" : string_printing(ob->message_prefix),
+    !ob->message_suffix ? US"null" : string_printing(ob->message_suffix),
+    ob->maildir_use_size_file ? "yes" : "no");
 
   if (!isdirectory) debug_printf("  locking by %s%s%s%s%s\n",
 
   if (!isdirectory) debug_printf("  locking by %s%s%s%s%s\n",
-    ob->use_lockfile? "lockfile " : "",
-    ob->use_mbx_lock? "mbx locking (" : "",
-    ob->use_fcntl? "fcntl " : "",
-    ob->use_flock? "flock" : "",
-    ob->use_mbx_lock? ")" : "");
+    ob->use_lockfile ? "lockfile " : "",
+    ob->use_mbx_lock ? "mbx locking (" : "",
+    ob->use_fcntl ? "fcntl " : "",
+    ob->use_flock ? "flock" : "",
+    ob->use_mbx_lock ? ")" : "");
   }
 
 /* If the -N option is set, can't do any more. */
 
   }
 
 /* If the -N option is set, can't do any more. */
 
-if (dont_deliver)
+if (f.dont_deliver)
   {
   DEBUG(D_transport)
     debug_printf("*** delivery by %s transport bypassed by -N option\n",
   {
   DEBUG(D_transport)
     debug_printf("*** delivery by %s transport bypassed by -N option\n",
@@ -1415,20 +1468,20 @@ if (!isdirectory)
   failures because if an existing file fails to open here, it will also fail
   again later when O_RDWR is used. */
 
   failures because if an existing file fails to open here, it will also fail
   again later when O_RDWR is used. */
 
-  if (ob->file_format != NULL)
+  if (ob->file_format)
     {
     int cfd = Uopen(path, O_RDONLY, 0);
     if (cfd >= 0)
       {
       transport_instance *tt = check_file_format(cfd, tblock, addr);
     {
     int cfd = Uopen(path, O_RDONLY, 0);
     if (cfd >= 0)
       {
       transport_instance *tt = check_file_format(cfd, tblock, addr);
-      close(cfd);
+      (void)close(cfd);
 
       /* If another transport is indicated, call it and return; if no transport
       was found, just return - the error data will have been set up.*/
 
       if (tt != tblock)
         {
 
       /* If another transport is indicated, call it and return; if no transport
       was found, just return - the error data will have been set up.*/
 
       if (tt != tblock)
         {
-        if (tt != NULL)
+        if (tt)
           {
           set_process_info("delivering %s to %s using %s", message_id,
             addr->local_part, tt->name);
           {
           set_process_info("delivering %s to %s using %s", message_id,
             addr->local_part, tt->name);
@@ -1574,6 +1627,7 @@ if (!isdirectory)
 
   if (ob->use_lockfile)
     {
 
   if (ob->use_lockfile)
     {
+    /* cf. exim_lock.c */
     lockname = string_sprintf("%s.lock", filename);
     hitchname = string_sprintf( "%s.%s.%08x.%08x", lockname, primary_hostname,
       (unsigned int)(time(NULL)), (unsigned int)getpid());
     lockname = string_sprintf("%s.lock", filename);
     hitchname = string_sprintf( "%s.%s.%08x.%08x", lockname, primary_hostname,
       (unsigned int)(time(NULL)), (unsigned int)getpid());
@@ -1586,8 +1640,8 @@ if (!isdirectory)
     for (i = 0; i < ob->lock_retries; sleep(ob->lock_interval), i++)
       {
       int rc;
     for (i = 0; i < ob->lock_retries; sleep(ob->lock_interval), i++)
       {
       int rc;
-      hd = Uopen(hitchname, O_WRONLY | O_CREAT | O_EXCL, ob->lockfile_mode);
 
 
+      hd = Uopen(hitchname, O_WRONLY | O_CREAT | O_EXCL, ob->lockfile_mode);
       if (hd < 0)
         {
         addr->basic_errno = errno;
       if (hd < 0)
         {
         addr->basic_errno = errno;
@@ -1613,7 +1667,7 @@ if (!isdirectory)
       sufficiently worried. */
 
       if ((rc = Ulink(hitchname, lockname)) != 0) fstat(hd, &statbuf);
       sufficiently worried. */
 
       if ((rc = Ulink(hitchname, lockname)) != 0) fstat(hd, &statbuf);
-      close(hd);
+      (void)close(hd);
       Uunlink(hitchname);
       if (rc != 0 && statbuf.st_nlink != 2)
         {
       Uunlink(hitchname);
       if (rc != 0 && statbuf.st_nlink != 2)
         {
@@ -1662,7 +1716,7 @@ if (!isdirectory)
     int sleep_before_retry = TRUE;
     file_opened = FALSE;
 
     int sleep_before_retry = TRUE;
     file_opened = FALSE;
 
-    if((use_lstat? Ulstat(filename, &statbuf) : Ustat(filename, &statbuf)) != 0)
+    if ((use_lstat ? Ulstat(filename, &statbuf) : Ustat(filename, &statbuf)) != 0)
       {
       /* Let's hope that failure to stat (other than non-existence) is a
       rare event. */
       {
       /* Let's hope that failure to stat (other than non-existence) is a
       rare event. */
@@ -1709,7 +1763,7 @@ if (!isdirectory)
       get a shared lock. */
 
       fd = Uopen(filename, O_RDWR | O_APPEND | O_CREAT |
       get a shared lock. */
 
       fd = Uopen(filename, O_RDWR | O_APPEND | O_CREAT |
-        (use_lstat? O_EXCL : 0), mode);
+        (use_lstat ? O_EXCL : 0), mode);
       if (fd < 0)
         {
         if (errno == EEXIST) continue;
       if (fd < 0)
         {
         if (errno == EEXIST) continue;
@@ -1725,8 +1779,14 @@ if (!isdirectory)
       /* We have successfully created and opened the file. Ensure that the group
       and the mode are correct. */
 
       /* We have successfully created and opened the file. Ensure that the group
       and the mode are correct. */
 
-      Uchown(filename, uid, gid);
-      Uchmod(filename, mode);
+      if (exim_chown(filename, uid, gid) || Uchmod(filename, mode))
+        {
+        addr->basic_errno = errno;
+        addr->message = string_sprintf("while setting perms on mailbox %s",
+          filename);
+        addr->transport_return = FAIL;
+        goto RETURN;
+        }
       }
 
 
       }
 
 
@@ -1752,7 +1812,7 @@ if (!isdirectory)
         addr->basic_errno = ERRNO_BADUGID;
         addr->message = string_sprintf("mailbox %s%s has wrong uid "
           "(%ld != %ld)", filename,
         addr->basic_errno = ERRNO_BADUGID;
         addr->message = string_sprintf("mailbox %s%s has wrong uid "
           "(%ld != %ld)", filename,
-          islink? " (symlink)" : "",
+          islink ? " (symlink)" : "",
           (long int)(statbuf.st_uid), (long int)uid);
         goto RETURN;
         }
           (long int)(statbuf.st_uid), (long int)uid);
         goto RETURN;
         }
@@ -1763,10 +1823,22 @@ if (!isdirectory)
         {
         addr->basic_errno = ERRNO_BADUGID;
         addr->message = string_sprintf("mailbox %s%s has wrong gid (%d != %d)",
         {
         addr->basic_errno = ERRNO_BADUGID;
         addr->message = string_sprintf("mailbox %s%s has wrong gid (%d != %d)",
-          filename, islink? " (symlink)" : "", statbuf.st_gid, gid);
+          filename, islink ? " (symlink)" : "", statbuf.st_gid, gid);
         goto RETURN;
         }
 
         goto RETURN;
         }
 
+      /* Just in case this is a sticky-bit mail directory, we don't want
+      users to be able to create hard links to other users' files. */
+
+      if (statbuf.st_nlink != 1)
+        {
+        addr->basic_errno = ERRNO_NOTREGULAR;
+        addr->message = string_sprintf("mailbox %s%s has too many links (%d)",
+          filename, islink ? " (symlink)" : "", statbuf.st_nlink);
+        goto RETURN;
+
+        }
+
       /* If symlinks are permitted (not recommended), the lstat() above will
       have found the symlink. Its ownership has just been checked; go round
       the loop again, using stat() instead of lstat(). That will never yield a
       /* If symlinks are permitted (not recommended), the lstat() above will
       have found the symlink. Its ownership has just been checked; go round
       the loop again, using stat() instead of lstat(). That will never yield a
@@ -1788,7 +1860,7 @@ if (!isdirectory)
         {
         addr->basic_errno = ERRNO_NOTREGULAR;
         addr->message = string_sprintf("mailbox %s is not a regular file%s",
         {
         addr->basic_errno = ERRNO_NOTREGULAR;
         addr->message = string_sprintf("mailbox %s is not a regular file%s",
-          filename, ob->allow_fifo? " or named pipe" : "");
+          filename, ob->allow_fifo ? " or named pipe" : "");
         goto RETURN;
         }
 
         goto RETURN;
         }
 
@@ -1798,7 +1870,7 @@ if (!isdirectory)
       permissions are greater than the existing permissions, don't change
       things when the mode is not from the address. */
 
       permissions are greater than the existing permissions, don't change
       things when the mode is not from the address. */
 
-      if ((oldmode = (oldmode & 07777)) != mode)
+      if ((oldmode &= 07777) != mode)
         {
         int diffs = oldmode ^ mode;
         if (addr->mode > 0 || (diffs & oldmode) == diffs)
         {
         int diffs = oldmode ^ mode;
         if (addr->mode > 0 || (diffs & oldmode) == diffs)
@@ -1837,7 +1909,7 @@ if (!isdirectory)
       a FIFO is opened WRONLY + NDELAY so that it fails if there is no process
       reading the pipe. */
 
       a FIFO is opened WRONLY + NDELAY so that it fails if there is no process
       reading the pipe. */
 
-      fd = Uopen(filename, isfifo? (O_WRONLY|O_NDELAY) : (O_RDWR|O_APPEND),
+      fd = Uopen(filename, isfifo ? (O_WRONLY|O_NDELAY) : (O_RDWR|O_APPEND),
         mode);
       if (fd < 0)
         {
         mode);
       if (fd < 0)
         {
@@ -1848,14 +1920,10 @@ if (!isdirectory)
           }
         addr->basic_errno = errno;
         if (isfifo)
           }
         addr->basic_errno = errno;
         if (isfifo)
-          {
           addr->message = string_sprintf("while opening named pipe %s "
             "(could mean no process is reading it)", filename);
           addr->message = string_sprintf("while opening named pipe %s "
             "(could mean no process is reading it)", filename);
-          }
         else if (errno != EWOULDBLOCK)
         else if (errno != EWOULDBLOCK)
-          {
           addr->message = string_sprintf("while opening mailbox %s", filename);
           addr->message = string_sprintf("while opening mailbox %s", filename);
-          }
         goto RETURN;
         }
 
         goto RETURN;
         }
 
@@ -1884,7 +1952,7 @@ if (!isdirectory)
         {
         addr->basic_errno = ERRNO_INODECHANGED;
         addr->message = string_sprintf("opened mailbox %s inode number changed "
         {
         addr->basic_errno = ERRNO_INODECHANGED;
         addr->message = string_sprintf("opened mailbox %s inode number changed "
-          "from %d to %ld", filename, inode, statbuf.st_ino);
+          "from " INO_T_FMT " to " INO_T_FMT, filename, inode, statbuf.st_ino);
         addr->special_action = SPECIAL_FREEZE;
         goto RETURN;
         }
         addr->special_action = SPECIAL_FREEZE;
         goto RETURN;
         }
@@ -1898,7 +1966,7 @@ if (!isdirectory)
         addr->basic_errno = ERRNO_NOTREGULAR;
         addr->message =
           string_sprintf("opened mailbox %s is no longer a %s", filename,
         addr->basic_errno = ERRNO_NOTREGULAR;
         addr->message =
           string_sprintf("opened mailbox %s is no longer a %s", filename,
-            isfifo? "named pipe" : "regular file");
+            isfifo ? "named pipe" : "regular file");
         addr->special_action = SPECIAL_FREEZE;
         goto RETURN;
         }
         addr->special_action = SPECIAL_FREEZE;
         goto RETURN;
         }
@@ -1925,8 +1993,7 @@ if (!isdirectory)
       /* If file_format is set, check that the format of the file has not
       changed. Error data is set by the testing function. */
 
       /* If file_format is set, check that the format of the file has not
       changed. Error data is set by the testing function. */
 
-      if (ob->file_format != NULL &&
-          check_file_format(fd, tblock, addr) != tblock)
+      if (ob->file_format  &&  check_file_format(fd, tblock, addr) != tblock)
         {
         addr->message = US"open mailbox has changed format";
         goto RETURN;
         {
         addr->message = US"open mailbox has changed format";
         goto RETURN;
@@ -1959,6 +2026,8 @@ if (!isdirectory)
     #ifdef SUPPORT_MBX
     else if (ob->use_mbx_lock)
       {
     #ifdef SUPPORT_MBX
     else if (ob->use_mbx_lock)
       {
+      int mbx_tmp_oflags;
+      struct stat lstatbuf, statbuf2;
       if (apply_lock(fd, F_RDLCK, ob->use_fcntl, ob->lock_fcntl_timeout,
            ob->use_flock, ob->lock_flock_timeout) >= 0 &&
            fstat(fd, &statbuf) >= 0)
       if (apply_lock(fd, F_RDLCK, ob->use_fcntl, ob->lock_fcntl_timeout,
            ob->use_flock, ob->lock_flock_timeout) >= 0 &&
            fstat(fd, &statbuf) >= 0)
@@ -1966,6 +2035,21 @@ if (!isdirectory)
         sprintf(CS mbx_lockname, "/tmp/.%lx.%lx", (long)statbuf.st_dev,
           (long)statbuf.st_ino);
 
         sprintf(CS mbx_lockname, "/tmp/.%lx.%lx", (long)statbuf.st_dev,
           (long)statbuf.st_ino);
 
+        /*
+         * 2010-05-29: SECURITY
+         * Dan Rosenberg reported the presence of a race-condition in the
+         * original code here.  Beware that many systems still allow symlinks
+         * to be followed in /tmp so an attacker can create a symlink pointing
+         * elsewhere between a stat and an open, which we should avoid
+         * following.
+         *
+         * It's unfortunate that we can't just use all the heavily debugged
+         * locking from above.
+         *
+         * Also: remember to mirror changes into exim_lock.c */
+
+        /* first leave the old pre-check in place, it provides better
+         * diagnostics for common cases */
         if (Ulstat(mbx_lockname, &statbuf) >= 0)
           {
           if ((statbuf.st_mode & S_IFMT) == S_IFLNK)
         if (Ulstat(mbx_lockname, &statbuf) >= 0)
           {
           if ((statbuf.st_mode & S_IFMT) == S_IFLNK)
@@ -1984,7 +2068,19 @@ if (!isdirectory)
             }
           }
 
             }
           }
 
-        mbx_lockfd = Uopen(mbx_lockname, O_RDWR | O_CREAT, 0600);
+        /* If we could just declare "we must be the ones who create this
+         * file" then a hitching post in a subdir would work, since a
+         * subdir directly in /tmp/ which we create wouldn't follow links
+         * but this isn't our locking logic, so we can't safely change the
+         * file existence rules. */
+
+        /* On systems which support O_NOFOLLOW, it's the easiest and most
+         * obviously correct security fix */
+        mbx_tmp_oflags = O_RDWR | O_CREAT;
+#ifdef O_NOFOLLOW
+        mbx_tmp_oflags |= O_NOFOLLOW;
+#endif
+        mbx_lockfd = Uopen(mbx_lockname, mbx_tmp_oflags, ob->lockfile_mode);
         if (mbx_lockfd < 0)
           {
           addr->basic_errno = ERRNO_LOCKFAILED;
         if (mbx_lockfd < 0)
           {
           addr->basic_errno = ERRNO_LOCKFAILED;
@@ -1993,7 +2089,61 @@ if (!isdirectory)
           goto RETURN;
           }
 
           goto RETURN;
           }
 
-        Uchmod(mbx_lockname, 0600);
+        if (Ulstat(mbx_lockname, &lstatbuf) < 0)
+          {
+          addr->basic_errno = ERRNO_LOCKFAILED;
+          addr->message = string_sprintf("attempting to lstat open MBX "
+             "lock file %s: %s", mbx_lockname, strerror(errno));
+          goto RETURN;
+          }
+        if (fstat(mbx_lockfd, &statbuf2) < 0)
+          {
+          addr->basic_errno = ERRNO_LOCKFAILED;
+          addr->message = string_sprintf("attempting to stat fd of open MBX "
+              "lock file %s: %s", mbx_lockname, strerror(errno));
+          goto RETURN;
+          }
+
+        /*
+         * At this point:
+         *  statbuf: if exists, is file which existed prior to opening the
+         *           lockfile, might have been replaced since then
+         *  statbuf2: result of stat'ing the open fd, is what was actually
+         *            opened
+         *  lstatbuf: result of lstat'ing the filename immediately after
+         *            the open but there's a race condition again between
+         *            those two steps: before open, symlink to foo, after
+         *            open but before lstat have one of:
+         *             * was no symlink, so is the opened file
+         *               (we created it, no messing possible after that point)
+         *             * hardlink to foo
+         *             * symlink elsewhere
+         *             * hardlink elsewhere
+         *             * new file/other
+         * Don't want to compare to device of /tmp because some modern systems
+         * have regressed to having /tmp be the safe actual filesystem as
+         * valuable data, so is mostly worthless, unless we assume that *only*
+         * Linux systems do this and that all Linux has O_NOFOLLOW.  Something
+         * for further consideration.
+         * No point in doing a readlink on the lockfile as that will always be
+         * at a different point in time from when we open it, so tells us
+         * nothing; attempts to clean up and delete after ourselves would risk
+         * deleting a *third* filename.
+         */
+        if ((statbuf2.st_nlink > 1) ||
+            (lstatbuf.st_nlink > 1) ||
+            (!S_ISREG(lstatbuf.st_mode)) ||
+            (lstatbuf.st_dev != statbuf2.st_dev) ||
+            (lstatbuf.st_ino != statbuf2.st_ino))
+          {
+          addr->basic_errno = ERRNO_LOCKFAILED;
+          addr->message = string_sprintf("RACE CONDITION detected: "
+              "mismatch post-initial-checks between \"%s\" and opened "
+              "fd lead us to abort!", mbx_lockname);
+          goto RETURN;
+          }
+
+        (void)Uchmod(mbx_lockname, ob->lockfile_mode);
 
         if (apply_lock(mbx_lockfd, F_WRLCK, ob->use_fcntl,
             ob->lock_fcntl_timeout, ob->use_flock, ob->lock_flock_timeout) >= 0)
 
         if (apply_lock(mbx_lockfd, F_WRLCK, ob->use_fcntl,
             ob->lock_fcntl_timeout, ob->use_flock, ob->lock_flock_timeout) >= 0)
@@ -2014,7 +2164,7 @@ if (!isdirectory)
 
         DEBUG(D_transport) debug_printf("failed to lock %s: %s\n", mbx_lockname,
           strerror(errno));
 
         DEBUG(D_transport) debug_printf("failed to lock %s: %s\n", mbx_lockname,
           strerror(errno));
-        close(mbx_lockfd);
+        (void)close(mbx_lockfd);
         mbx_lockfd = -1;
         }
       else
         mbx_lockfd = -1;
         }
       else
@@ -2030,7 +2180,7 @@ if (!isdirectory)
     DEBUG(D_transport)
       debug_printf("fcntl(), flock(), or MBX locking failed - retrying\n");
 
     DEBUG(D_transport)
       debug_printf("fcntl(), flock(), or MBX locking failed - retrying\n");
 
-    close(fd);
+    (void)close(fd);
     fd = -1;
     use_lstat = TRUE;             /* Reset to use lstat first */
 
     fd = -1;
     use_lstat = TRUE;             /* Reset to use lstat first */
 
@@ -2116,10 +2266,11 @@ else
     }
 
   #ifdef SUPPORT_MAILDIR
     }
 
   #ifdef SUPPORT_MAILDIR
-  /* For a maildir delivery, ensure that all the relevant directories exist */
+  /* For a maildir delivery, ensure that all the relevant directories exist,
+  and a maildirfolder file if necessary. */
 
   if (mbformat == mbf_maildir && !maildir_ensure_directories(path, addr,
 
   if (mbformat == mbf_maildir && !maildir_ensure_directories(path, addr,
-    ob->create_directory, ob->dirmode))
+    ob->create_directory, ob->dirmode, ob->maildirfolder_create_regex))
       return FALSE;
   #endif  /* SUPPORT_MAILDIR */
 
       return FALSE;
   #endif  /* SUPPORT_MAILDIR */
 
@@ -2135,38 +2286,32 @@ else
     const uschar *error;
     int offset;
 
     const uschar *error;
     int offset;
 
-    /* Compile the regex if there is one */
+    /* Compile the regex if there is one. */
 
 
-    if (ob->quota_size_regex != NULL)
+    if (ob->quota_size_regex)
       {
       {
-      regex = pcre_compile(CS ob->quota_size_regex, PCRE_COPT,
-        (const char **)&error, &offset, NULL);
-      if (regex == NULL)
+      if (!(regex = pcre_compile(CS ob->quota_size_regex, PCRE_COPT,
+         CCSS &error, &offset, NULL)))
         {
         addr->message = string_sprintf("appendfile: regular expression "
           "error: %s at offset %d while compiling %s", error, offset,
           ob->quota_size_regex);
         return FALSE;
         }
         {
         addr->message = string_sprintf("appendfile: regular expression "
           "error: %s at offset %d while compiling %s", error, offset,
           ob->quota_size_regex);
         return FALSE;
         }
-      else
-        {
-        DEBUG(D_transport) debug_printf("using regex for file sizes: %s\n",
-          ob->quota_size_regex);
-        }
+      DEBUG(D_transport) debug_printf("using regex for file sizes: %s\n",
+        ob->quota_size_regex);
       }
 
     /* Use an explicitly configured directory if set */
 
       }
 
     /* Use an explicitly configured directory if set */
 
-    if (ob->quota_directory != NULL)
+    if (ob->quota_directory)
       {
       {
-      check_path = expand_string(ob->quota_directory);
-      if (check_path == NULL)
+      if (!(check_path = expand_string(ob->quota_directory)))
         {
         {
-        addr->transport_return = PANIC;
         addr->message = string_sprintf("Expansion of \"%s\" (quota_directory "
          "name for %s transport) failed: %s", ob->quota_directory,
           tblock->name, expand_string_message);
         addr->message = string_sprintf("Expansion of \"%s\" (quota_directory "
          "name for %s transport) failed: %s", ob->quota_directory,
           tblock->name, expand_string_message);
-        return FALSE;
+        goto ret_panic;
         }
 
       if (check_path[0] != '/')
         }
 
       if (check_path[0] != '/')
@@ -2203,6 +2348,8 @@ else
             {
             *slash = 0;
             check_path = new_check_path;
             {
             *slash = 0;
             check_path = new_check_path;
+            DEBUG(D_transport) debug_printf("maildirfolder file exists: "
+              "quota check directory changed to %s\n", check_path);
             }
           }
         }
             }
           }
         }
@@ -2226,22 +2373,39 @@ else
     const uschar *error;
     int offset;
 
     const uschar *error;
     int offset;
 
-    if (ob->maildir_dir_regex != NULL)
+    if (ob->maildir_dir_regex)
       {
       {
-      dir_regex = pcre_compile(CS ob->maildir_dir_regex, PCRE_COPT,
-        (const char **)&error, &offset, NULL);
-      if (dir_regex == NULL)
+      int check_path_len = Ustrlen(check_path);
+
+      if (!(dir_regex = pcre_compile(CS ob->maildir_dir_regex, PCRE_COPT,
+         CCSS &error, &offset, NULL)))
         {
         addr->message = string_sprintf("appendfile: regular expression "
           "error: %s at offset %d while compiling %s", error, offset,
           ob->maildir_dir_regex);
         return FALSE;
         }
         {
         addr->message = string_sprintf("appendfile: regular expression "
           "error: %s at offset %d while compiling %s", error, offset,
           ob->maildir_dir_regex);
         return FALSE;
         }
-      else
+
+      DEBUG(D_transport)
+        debug_printf("using regex for maildir directory selection: %s\n",
+          ob->maildir_dir_regex);
+
+      /* Check to see if we are delivering into an ignored directory, that is,
+      if the delivery path starts with the quota check path, and the rest
+      of the deliver path matches the regex; if so, set a flag to disable quota
+      checking and maildirsize updating. */
+
+      if (Ustrncmp(path, check_path, check_path_len) == 0)
         {
         {
-        DEBUG(D_transport)
-          debug_printf("using regex for maildir directory selection: %s\n",
-            ob->maildir_dir_regex);
+        uschar *s = path + check_path_len;
+        while (*s == '/') s++;
+        s = (*s == 0) ? US "new" : string_sprintf("%s/new", s);
+        if (pcre_exec(dir_regex, NULL, CS s, Ustrlen(s), 0, 0, NULL, 0) < 0)
+          {
+          disable_quota = TRUE;
+          DEBUG(D_transport) debug_printf("delivery directory does not match "
+            "maildir_quota_directory_regex: disabling quota\n");
+          }
         }
       }
 
         }
       }
 
@@ -2252,19 +2416,22 @@ else
 
 /*  if (???? || ob->quota_value > 0) */
 
 
 /*  if (???? || ob->quota_value > 0) */
 
+    if (!disable_quota)
       {
       {
-      int size, filecount;
+      off_t size;
+      int filecount;
 
 
-      maildirsize_fd = maildir_ensure_sizefile(check_path, ob, regex, dir_regex,
-        &size, &filecount);
-
-      if (maildirsize_fd == -1)
+      if ((maildirsize_fd = maildir_ensure_sizefile(check_path, ob, regex, dir_regex,
+         &size, &filecount)) == -1)
         {
         addr->basic_errno = errno;
         addr->message = string_sprintf("while opening or reading "
           "%s/maildirsize", check_path);
         return FALSE;
         }
         {
         addr->basic_errno = errno;
         addr->message = string_sprintf("while opening or reading "
           "%s/maildirsize", check_path);
         return FALSE;
         }
+      /* can also return -2, which means that the file was removed because of
+      raciness; but in this case, the size & filecount will still have been
+      updated. */
 
       if (mailbox_size < 0) mailbox_size = size;
       if (mailbox_filecount < 0) mailbox_filecount = filecount;
 
       if (mailbox_size < 0) mailbox_size = size;
       if (mailbox_filecount < 0) mailbox_filecount = filecount;
@@ -2286,15 +2453,17 @@ else
     }
   #endif  /* SUPPORT_MAILDIR */
 
     }
   #endif  /* SUPPORT_MAILDIR */
 
-  /* Otherwise (mailbox_size is not yet set), if we are going to do a quota
-  check later on, find the current size of the mailbox. (We don't need to check
-  ob->quota_filecount_value, because it can only be set if ob->quota_value is
-  set.) */
+  /* Otherwise if we are going to do a quota check later on, and the mailbox
+  size is not set, find the current size of the mailbox. Ditto for the file
+  count. Note that ob->quota_filecount_value cannot be set without
+  ob->quota_value being set. */
 
 
-  if ((mailbox_size < 0 || mailbox_filecount < 0) &&
-      (ob->quota_value > 0 || THRESHOLD_CHECK))
+  if (!disable_quota &&
+      (ob->quota_value > 0 || THRESHOLD_CHECK) &&
+      (mailbox_size < 0 ||
+        (mailbox_filecount < 0 && ob->quota_filecount_value > 0)))
     {
     {
-    int size;
+    off_t size;
     int filecount = 0;
     DEBUG(D_transport)
       debug_printf("quota checks on directory %s\n", check_path);
     int filecount = 0;
     DEBUG(D_transport)
       debug_printf("quota checks on directory %s\n", check_path);
@@ -2344,14 +2513,12 @@ else
     return. The actual expansion for use happens again later, when
     $message_size is accurately known. */
 
     return. The actual expansion for use happens again later, when
     $message_size is accurately known. */
 
-    if (nametag != NULL && expand_string(nametag) == NULL &&
-        !expand_string_forcedfail)
+    if (nametag && !expand_string(nametag) && !f.expand_string_forcedfail)
       {
       {
-      addr->transport_return = PANIC;
       addr->message = string_sprintf("Expansion of \"%s\" (maildir_tag "
         "for %s transport) failed: %s", nametag, tblock->name,
         expand_string_message);
       addr->message = string_sprintf("Expansion of \"%s\" (maildir_tag "
         "for %s transport) failed: %s", nametag, tblock->name,
         expand_string_message);
-      return FALSE;
+      goto ret_panic;
       }
 
     /* We ensured the existence of all the relevant directories above. Attempt
       }
 
     /* We ensured the existence of all the relevant directories above. Attempt
@@ -2362,13 +2529,13 @@ else
     checked at the end, to make sure we don't release this process until the
     clock has ticked. */
 
     checked at the end, to make sure we don't release this process until the
     clock has ticked. */
 
-    for (i = 1;; i++)
+    for (int i = 1;; i++)
       {
       uschar *basename;
 
       (void)gettimeofday(&msg_tv, NULL);
       {
       uschar *basename;
 
       (void)gettimeofday(&msg_tv, NULL);
-      basename = string_sprintf("%lu.H%luP%lu.%s", msg_tv.tv_sec,
-        msg_tv.tv_usec, getpid(), primary_hostname);
+      basename = string_sprintf(TIME_T_FMT ".H%luP" PID_T_FMT ".%s",
+               msg_tv.tv_sec, msg_tv.tv_usec, getpid(), primary_hostname);
 
       filename = dataname = string_sprintf("tmp/%s", basename);
       newname = string_sprintf("new/%s", basename);
 
       filename = dataname = string_sprintf("tmp/%s", basename);
       newname = string_sprintf("new/%s", basename);
@@ -2377,8 +2544,8 @@ else
         errno = EEXIST;
       else if (errno == ENOENT)
         {
         errno = EEXIST;
       else if (errno == ENOENT)
         {
-        fd = Uopen(filename, O_WRONLY | O_CREAT | O_EXCL, mode);
-        if (fd >= 0) break;
+        if ((fd = Uopen(filename, O_WRONLY | O_CREAT | O_EXCL, mode)) >= 0)
+         break;
         DEBUG (D_transport) debug_printf ("open failed for %s: %s\n",
           filename, strerror(errno));
         }
         DEBUG (D_transport) debug_printf ("open failed for %s: %s\n",
           filename, strerror(errno));
         }
@@ -2388,8 +2555,10 @@ else
       if (i >= ob->maildir_retries)
         {
         addr->message = string_sprintf ("failed to open %s (%d tr%s)",
       if (i >= ob->maildir_retries)
         {
         addr->message = string_sprintf ("failed to open %s (%d tr%s)",
-          filename, i, (i == 1)? "y" : "ies");
+          filename, i, (i == 1) ? "y" : "ies");
         addr->basic_errno = errno;
         addr->basic_errno = errno;
+        if (errno == errno_quota || errno == ENOSPC)
+          addr->user_message = US"mailbox is full";
         return FALSE;
         }
 
         return FALSE;
         }
 
@@ -2405,8 +2574,13 @@ else
     /* Why are these here? Put in because they are present in the non-maildir
     directory case above. */
 
     /* Why are these here? Put in because they are present in the non-maildir
     directory case above. */
 
-    Uchown(filename, uid, gid);
-    Uchmod(filename, mode);
+    if (exim_chown(filename, uid, gid) || Uchmod(filename, mode))
+      {
+      addr->basic_errno = errno;
+      addr->message = string_sprintf("while setting perms on maildir %s",
+        filename);
+      return FALSE;
+      }
     }
 
   #endif  /* SUPPORT_MAILDIR */
     }
 
   #endif  /* SUPPORT_MAILDIR */
@@ -2421,7 +2595,6 @@ else
   else
     {
     FILE *env_file;
   else
     {
     FILE *env_file;
-    address_item *taddr;
     mailstore_basename = string_sprintf("%s/%s-%s", path, message_id,
       string_base62((long int)getpid()));
 
     mailstore_basename = string_sprintf("%s/%s-%s", path, message_id,
       string_base62((long int)getpid()));
 
@@ -2447,38 +2620,41 @@ else
     /* Why are these here? Put in because they are present in the non-maildir
     directory case above. */
 
     /* Why are these here? Put in because they are present in the non-maildir
     directory case above. */
 
-    Uchown(filename, uid, gid);
-    Uchmod(filename, mode);
+    if (exim_chown(filename, uid, gid) || Uchmod(filename, mode))
+      {
+      addr->basic_errno = errno;
+      addr->message = string_sprintf("while setting perms on file %s",
+        filename);
+      return FALSE;
+      }
 
     /* Built a C stream from the open file descriptor. */
 
 
     /* Built a C stream from the open file descriptor. */
 
-    if ((env_file = fdopen(fd, "wb")) == NULL)
+    if (!(env_file = fdopen(fd, "wb")))
       {
       addr->basic_errno = errno;
       {
       addr->basic_errno = errno;
-      addr->transport_return = PANIC;
       addr->message = string_sprintf("fdopen of %s ("
         "for %s transport) failed", filename, tblock->name);
       addr->message = string_sprintf("fdopen of %s ("
         "for %s transport) failed", filename, tblock->name);
-      close(fd);
+      (void)close(fd);
       Uunlink(filename);
       Uunlink(filename);
-      return FALSE;
+      goto ret_panic;
       }
 
     /* Write the envelope file, then close it. */
 
       }
 
     /* Write the envelope file, then close it. */
 
-    if (ob->mailstore_prefix != NULL)
+    if (ob->mailstore_prefix)
       {
       uschar *s = expand_string(ob->mailstore_prefix);
       {
       uschar *s = expand_string(ob->mailstore_prefix);
-      if (s == NULL)
+      if (!s)
         {
         {
-        if (!expand_string_forcedfail)
+        if (!f.expand_string_forcedfail)
           {
           {
-          addr->transport_return = PANIC;
           addr->message = string_sprintf("Expansion of \"%s\" (mailstore "
             "prefix for %s transport) failed: %s", ob->mailstore_prefix,
             tblock->name, expand_string_message);
           addr->message = string_sprintf("Expansion of \"%s\" (mailstore "
             "prefix for %s transport) failed: %s", ob->mailstore_prefix,
             tblock->name, expand_string_message);
-          fclose(env_file);
+          (void)fclose(env_file);
           Uunlink(filename);
           Uunlink(filename);
-          return FALSE;
+          goto ret_panic;
           }
         }
       else
           }
         }
       else
@@ -2491,23 +2667,22 @@ else
 
     fprintf(env_file, "%s\n", sender_address);
 
 
     fprintf(env_file, "%s\n", sender_address);
 
-    for (taddr = addr; taddr!= NULL; taddr = taddr->next)
+    for (address_item * taddr = addr; taddr; taddr = taddr->next)
       fprintf(env_file, "%s@%s\n", taddr->local_part, taddr->domain);
 
       fprintf(env_file, "%s@%s\n", taddr->local_part, taddr->domain);
 
-    if (ob->mailstore_suffix != NULL)
+    if (ob->mailstore_suffix)
       {
       uschar *s = expand_string(ob->mailstore_suffix);
       {
       uschar *s = expand_string(ob->mailstore_suffix);
-      if (s == NULL)
+      if (!s)
         {
         {
-        if (!expand_string_forcedfail)
+        if (!f.expand_string_forcedfail)
           {
           {
-          addr->transport_return = PANIC;
           addr->message = string_sprintf("Expansion of \"%s\" (mailstore "
             "suffix for %s transport) failed: %s", ob->mailstore_suffix,
             tblock->name, expand_string_message);
           addr->message = string_sprintf("Expansion of \"%s\" (mailstore "
             "suffix for %s transport) failed: %s", ob->mailstore_suffix,
             tblock->name, expand_string_message);
-          fclose(env_file);
+          (void)fclose(env_file);
           Uunlink(filename);
           Uunlink(filename);
-          return FALSE;
+          goto ret_panic;
           }
         }
       else
           }
         }
       else
@@ -2531,16 +2706,20 @@ else
     /* Now open the data file, and ensure that it has the correct ownership and
     mode. */
 
     /* Now open the data file, and ensure that it has the correct ownership and
     mode. */
 
-    fd = Uopen(dataname, O_WRONLY|O_CREAT|O_EXCL, mode);
-    if (fd < 0)
+    if ((fd = Uopen(dataname, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
       {
       addr->basic_errno = errno;
       addr->message = string_sprintf("while creating file %s", dataname);
       Uunlink(filename);
       return FALSE;
       }
       {
       addr->basic_errno = errno;
       addr->message = string_sprintf("while creating file %s", dataname);
       Uunlink(filename);
       return FALSE;
       }
-    Uchown(dataname, uid, gid);
-    Uchmod(dataname, mode);
+    if (exim_chown(dataname, uid, gid) || Uchmod(dataname, mode))
+      {
+      addr->basic_errno = errno;
+      addr->message = string_sprintf("while setting perms on file %s",
+        dataname);
+      return FALSE;
+      }
     }
 
   #endif  /* SUPPORT_MAILSTORE */
     }
 
   #endif  /* SUPPORT_MAILSTORE */
@@ -2549,8 +2728,13 @@ else
   /* In all cases of writing to a new file, ensure that the file which is
   going to be renamed has the correct ownership and mode. */
 
   /* In all cases of writing to a new file, ensure that the file which is
   going to be renamed has the correct ownership and mode. */
 
-  Uchown(filename, uid, gid);
-  Uchmod(filename, mode);
+  if (exim_chown(filename, uid, gid) || Uchmod(filename, mode))
+    {
+    addr->basic_errno = errno;
+    addr->message = string_sprintf("while setting perms on file %s",
+      filename);
+    return FALSE;
+    }
   }
 
 
   }
 
 
@@ -2568,31 +2752,41 @@ with this message if quota_is_inclusive is set; if it is not set, the check
 is for the mailbox already being over quota (i.e. the current message is not
 included in the check). */
 
 is for the mailbox already being over quota (i.e. the current message is not
 included in the check). */
 
-if (ob->quota_value > 0)
+if (!disable_quota && ob->quota_value > 0)
   {
   DEBUG(D_transport)
     {
   {
   DEBUG(D_transport)
     {
-    debug_printf("Exim quota = %d old size = %d this message = %d "
-      "(%sincluded)\n", ob->quota_value, mailbox_size, message_size,
-      ob->quota_is_inclusive? "" : "not ");
+    debug_printf("Exim quota = " OFF_T_FMT " old size = " OFF_T_FMT
+      " this message = %d (%sincluded)\n",
+      ob->quota_value, mailbox_size, message_size,
+      ob->quota_is_inclusive ? "" : "not ");
     debug_printf("  file count quota = %d count = %d\n",
       ob->quota_filecount_value, mailbox_filecount);
     }
     debug_printf("  file count quota = %d count = %d\n",
       ob->quota_filecount_value, mailbox_filecount);
     }
-  if (mailbox_size + (ob->quota_is_inclusive? message_size:0) > ob->quota_value)
-    {
-    DEBUG(D_transport) debug_printf("mailbox quota exceeded\n");
-    yield = DEFER;
-    errno = ERRNO_EXIMQUOTA;
-    }
-  else if (ob->quota_filecount_value > 0 &&
-           mailbox_filecount + (ob->quota_is_inclusive ? 1:0) >
-             ob->quota_filecount_value)
-    {
-    DEBUG(D_transport) debug_printf("mailbox file count quota exceeded\n");
-    yield = DEFER;
-    errno = ERRNO_EXIMQUOTA;
-    filecount_msg = US" filecount";
-    }
+
+  if (mailbox_size + (ob->quota_is_inclusive ? message_size:0) > ob->quota_value)
+    if (!ob->quota_no_check)
+      {
+      DEBUG(D_transport) debug_printf("mailbox quota exceeded\n");
+      yield = DEFER;
+      errno = ERRNO_EXIMQUOTA;
+      }
+    else
+      DEBUG(D_transport) debug_printf("mailbox quota exceeded but ignored\n");
+
+  if (ob->quota_filecount_value > 0
+           && mailbox_filecount + (ob->quota_is_inclusive ? 1:0) >
+              ob->quota_filecount_value)
+    if (!ob->quota_filecount_no_check)
+      {
+      DEBUG(D_transport) debug_printf("mailbox file count quota exceeded\n");
+      yield = DEFER;
+      errno = ERRNO_EXIMQUOTA;
+      filecount_msg = US" filecount";
+      }
+    else DEBUG(D_transport) if (ob->quota_filecount_no_check)
+      debug_printf("mailbox file count quota exceeded but ignored\n");
+
   }
 
 /* If we are writing in MBX format, what we actually do is to write the message
   }
 
 /* If we are writing in MBX format, what we actually do is to write the message
@@ -2605,8 +2799,7 @@ opened, so that it goes away on closure. */
 #ifdef SUPPORT_MBX
 if (yield == OK && ob->mbx_format)
   {
 #ifdef SUPPORT_MBX
 if (yield == OK && ob->mbx_format)
   {
-  temp_file = tmpfile();
-  if (temp_file == NULL)
+  if (!(temp_file = tmpfile()))
     {
     addr->basic_errno = errno;
     addr->message = US"while setting up temporary file";
     {
     addr->basic_errno = errno;
     addr->message = US"while setting up temporary file";
@@ -2623,13 +2816,14 @@ if (yield == OK && ob->mbx_format)
 functions. */
 
 transport_count = 0;
 functions. */
 
 transport_count = 0;
+transport_newlines = 0;
 
 /* Write any configured prefix text first */
 
 
 /* Write any configured prefix text first */
 
-if (yield == OK && ob->message_prefix != NULL && ob->message_prefix[0] != 0)
+if (yield == OK && ob->message_prefix && *ob->message_prefix)
   {
   uschar *prefix = expand_string(ob->message_prefix);
   {
   uschar *prefix = expand_string(ob->message_prefix);
-  if (prefix == NULL)
+  if (!prefix)
     {
     errno = ERRNO_EXPANDFAIL;
     addr->transport_return = PANIC;
     {
     errno = ERRNO_EXPANDFAIL;
     addr->transport_return = PANIC;
@@ -2648,21 +2842,25 @@ file, use its parent in the RCPT TO. */
 if (yield == OK && ob->use_bsmtp)
   {
   transport_count = 0;
 if (yield == OK && ob->use_bsmtp)
   {
   transport_count = 0;
+  transport_newlines = 0;
   if (ob->use_crlf) cr = US"\r";
   if (!transport_write_string(fd, "MAIL FROM:<%s>%s\n", return_path, cr))
     yield = DEFER;
   else
     {
   if (ob->use_crlf) cr = US"\r";
   if (!transport_write_string(fd, "MAIL FROM:<%s>%s\n", return_path, cr))
     yield = DEFER;
   else
     {
-    address_item *a;
-    for (a = addr; a != NULL; a = a->next)
+    transport_newlines++;
+    for (address_item * a = addr; a; a = a->next)
       {
       {
-      address_item *b = testflag(a, af_pfr)? a->parent: a;
+      address_item * b = testflag(a, af_pfr) ? a->parent : a;
       if (!transport_write_string(fd, "RCPT TO:<%s>%s\n",
         transport_rcpt_address(b, tblock->rcpt_include_affixes), cr))
           { yield = DEFER; break; }
       if (!transport_write_string(fd, "RCPT TO:<%s>%s\n",
         transport_rcpt_address(b, tblock->rcpt_include_affixes), cr))
           { yield = DEFER; break; }
+      transport_newlines++;
       }
     if (yield == OK && !transport_write_string(fd, "DATA%s\n", cr))
       yield = DEFER;
       }
     if (yield == OK && !transport_write_string(fd, "DATA%s\n", cr))
       yield = DEFER;
+    else
+      transport_newlines++;
     }
   }
 
     }
   }
 
@@ -2671,18 +2869,24 @@ at initialization time. */
 
 if (yield == OK)
   {
 
 if (yield == OK)
   {
-  if (!transport_write_message(addr, fd, ob->options, 0, tblock->add_headers,
-      tblock->remove_headers, ob->check_string, ob->escape_string,
-      tblock->rewrite_rules, tblock->rewrite_existflags))
+  transport_ctx tctx = {
+    .u = {.fd=fd},
+    .tblock = tblock,
+    .addr = addr,
+    .check_string = ob->check_string,
+    .escape_string = ob->escape_string,
+    .options =  ob->options | topt_not_socket
+  };
+  if (!transport_write_message(&tctx, 0))
     yield = DEFER;
   }
 
 /* Now a configured suffix. */
 
     yield = DEFER;
   }
 
 /* Now a configured suffix. */
 
-if (yield == OK && ob->message_suffix != NULL && ob->message_suffix[0] != 0)
+if (yield == OK && ob->message_suffix && *ob->message_suffix)
   {
   uschar *suffix = expand_string(ob->message_suffix);
   {
   uschar *suffix = expand_string(ob->message_suffix);
-  if (suffix == NULL)
+  if (!suffix)
     {
     errno = ERRNO_EXPANDFAIL;
     addr->transport_return = PANIC;
     {
     errno = ERRNO_EXPANDFAIL;
     addr->transport_return = PANIC;
@@ -2695,8 +2899,9 @@ if (yield == OK && ob->message_suffix != NULL && ob->message_suffix[0] != 0)
 
 /* If batch smtp, write the terminating dot. */
 
 
 /* If batch smtp, write the terminating dot. */
 
-if (yield == OK && ob->use_bsmtp &&
-  !transport_write_string(fd, ".%s\n", cr)) yield = DEFER;
+if (yield == OK && ob->use_bsmtp)
+  if (!transport_write_string(fd, ".%s\n", cr)) yield = DEFER;
+  else transport_newlines++;
 
 /* If MBX format is being used, all that writing was to the temporary file.
 However, if there was an earlier failure (Exim quota exceeded, for example),
 
 /* If MBX format is being used, all that writing was to the temporary file.
 However, if there was an earlier failure (Exim quota exceeded, for example),
@@ -2706,7 +2911,7 @@ message in MBX format into the real file. Otherwise use the temporary name in
 any messages. */
 
 #ifdef SUPPORT_MBX
 any messages. */
 
 #ifdef SUPPORT_MBX
-if (temp_file != NULL && ob->mbx_format)
+if (temp_file && ob->mbx_format)
   {
   int mbx_save_errno;
   fd = save_fd;
   {
   int mbx_save_errno;
   fd = save_fd;
@@ -2714,6 +2919,8 @@ if (temp_file != NULL && ob->mbx_format)
   if (yield == OK)
     {
     transport_count = 0;   /* Reset transport count for actual write */
   if (yield == OK)
     {
     transport_count = 0;   /* Reset transport count for actual write */
+    /* No need to reset transport_newlines as we're just using a block copy
+     * routine so the number won't be affected */
     yield = copy_mbx_message(fd, fileno(temp_file), saved_size);
     }
   else if (errno >= 0) dataname = US"temporary file";
     yield = copy_mbx_message(fd, fileno(temp_file), saved_size);
     }
   else if (errno >= 0) dataname = US"temporary file";
@@ -2721,7 +2928,7 @@ if (temp_file != NULL && ob->mbx_format)
   /* Preserve errno while closing the temporary file. */
 
   mbx_save_errno = errno;
   /* Preserve errno while closing the temporary file. */
 
   mbx_save_errno = errno;
-  fclose(temp_file);
+  (void)fclose(temp_file);
   errno = mbx_save_errno;
   }
 #endif  /* SUPPORT_MBX */
   errno = mbx_save_errno;
   }
 #endif  /* SUPPORT_MBX */
@@ -2729,37 +2936,48 @@ if (temp_file != NULL && ob->mbx_format)
 /* Force out the remaining data to check for any errors; some OS don't allow
 fsync() to be called for a FIFO. */
 
 /* Force out the remaining data to check for any errors; some OS don't allow
 fsync() to be called for a FIFO. */
 
-if (yield == OK && !isfifo && fsync(fd) < 0) yield = DEFER;
+if (yield == OK && !isfifo && EXIMfsync(fd) < 0) yield = DEFER;
 
 
-/* Update message_size to the accurate count of bytes written, including
-added headers. */
+/* Update message_size and message_linecount to the accurate count of bytes
+written, including added headers. Note; we subtract 1 from message_linecount as
+this variable doesn't count the new line between the header and the body of the
+message. */
 
 message_size = transport_count;
 
 message_size = transport_count;
+message_linecount = transport_newlines - 1;
 
 /* If using a maildir++ quota file, add this message's size to it, and
 
 /* If using a maildir++ quota file, add this message's size to it, and
-close the file descriptor. */
+close the file descriptor, except when the quota has been disabled because we
+are delivering into an uncounted folder. */
 
 #ifdef SUPPORT_MAILDIR
 
 #ifdef SUPPORT_MAILDIR
-if (yield == OK && maildirsize_fd >= 0)
-  maildir_record_length(maildirsize_fd, message_size);
-
-maildir_save_errno = errno;       /* Preserve errno while closing the file */
-close(maildirsize_fd);
-errno = maildir_save_errno;
+if (!disable_quota)
+  {
+  if (yield == OK && maildirsize_fd >= 0)
+    maildir_record_length(maildirsize_fd, message_size);
+  maildir_save_errno = errno;    /* Preserve errno while closing the file */
+  if (maildirsize_fd >= 0)
+    (void)close(maildirsize_fd);
+  errno = maildir_save_errno;
+  }
 #endif  /* SUPPORT_MAILDIR */
 
 /* If there is a quota warning threshold and we are have crossed it with this
 message, set the SPECIAL_WARN flag in the address, to cause a warning message
 to be sent. */
 
 #endif  /* SUPPORT_MAILDIR */
 
 /* If there is a quota warning threshold and we are have crossed it with this
 message, set the SPECIAL_WARN flag in the address, to cause a warning message
 to be sent. */
 
-if (THRESHOLD_CHECK)
+if (!disable_quota && THRESHOLD_CHECK)
   {
   {
-  int threshold = ob->quota_warn_threshold_value;
+  off_t threshold = ob->quota_warn_threshold_value;
   if (ob->quota_warn_threshold_is_percent)
   if (ob->quota_warn_threshold_is_percent)
-    threshold = (int)(((double)ob->quota_value * threshold) / 100);
+    threshold = (off_t)(((double)ob->quota_value * threshold) / 100);
   DEBUG(D_transport)
   DEBUG(D_transport)
-    debug_printf("quota = %d threshold = %d old size = %d message size = %d\n",
-      ob->quota_value, threshold, mailbox_size, message_size);
+    debug_printf("quota = " OFF_T_FMT
+      " threshold = " OFF_T_FMT
+      " old size = " OFF_T_FMT
+      " message size = %d\n",
+      ob->quota_value, threshold, mailbox_size,
+      message_size);
   if (mailbox_size <= threshold && mailbox_size + message_size > threshold)
     addr->special_action = SPECIAL_WARN;
 
   if (mailbox_size <= threshold && mailbox_size + message_size > threshold)
     addr->special_action = SPECIAL_WARN;
 
@@ -2811,10 +3029,9 @@ if (yield != OK)
           "stat error %d for \"new\": %s\n", errno, strerror(errno));
         }
       else   /* Want a repeatable time when in test harness */
           "stat error %d for \"new\": %s\n", errno, strerror(errno));
         }
       else   /* Want a repeatable time when in test harness */
-        {
-        addr->more_errno = running_in_test_harness? 10 :
+        addr->more_errno = f.running_in_test_harness ? 10 :
           (int)time(NULL) - statbuf.st_mtime;
           (int)time(NULL) - statbuf.st_mtime;
-        }
+
       DEBUG(D_transport)
         debug_printf("maildir: time since \"new\" directory modified = %s\n",
         readconf_printtime(addr->more_errno));
       DEBUG(D_transport)
         debug_printf("maildir: time since \"new\" directory modified = %s\n",
         readconf_printtime(addr->more_errno));
@@ -2832,12 +3049,13 @@ if (yield != OK)
     addr->message = string_sprintf("mailbox is full "
       "(quota exceeded while writing to file %s)", filename);
     #else
     addr->message = string_sprintf("mailbox is full "
       "(quota exceeded while writing to file %s)", filename);
     #else
-    addr->message = string_sprintf("mailbox is full");
+    addr->message = US"mailbox is full";
     #endif  /* EDQUOT */
     #endif  /* EDQUOT */
+    addr->user_message = US"mailbox is full";
     DEBUG(D_transport) debug_printf("System quota exceeded for %s%s%s\n",
       dataname,
     DEBUG(D_transport) debug_printf("System quota exceeded for %s%s%s\n",
       dataname,
-      isdirectory? US"" : US": time since file read = ",
-      isdirectory? US"" : readconf_printtime(addr->more_errno));
+      isdirectory ? US"" : US": time since file read = ",
+      isdirectory ? US"" : readconf_printtime(addr->more_errno));
     }
 
   /* Handle Exim's own quota-imposition */
     }
 
   /* Handle Exim's own quota-imposition */
@@ -2850,16 +3068,20 @@ if (yield != OK)
     addr->user_message = US"mailbox is full";
     DEBUG(D_transport) debug_printf("Exim%s quota exceeded for %s%s%s\n",
       filecount_msg, dataname,
     addr->user_message = US"mailbox is full";
     DEBUG(D_transport) debug_printf("Exim%s quota exceeded for %s%s%s\n",
       filecount_msg, dataname,
-      isdirectory? US"" : US": time since file read = ",
-      isdirectory? US"" : readconf_printtime(addr->more_errno));
+      isdirectory ? US"" : US": time since file read = ",
+      isdirectory ? US"" : readconf_printtime(addr->more_errno));
     }
 
   /* Handle a process failure while writing via a filter; the return
   from child_close() is in more_errno. */
 
   else if (errno == ERRNO_FILTER_FAIL)
     }
 
   /* Handle a process failure while writing via a filter; the return
   from child_close() is in more_errno. */
 
   else if (errno == ERRNO_FILTER_FAIL)
-    addr->message = string_sprintf("filter process failure %d while writing "
-      "to %s", addr->more_errno, dataname);
+    {
+    yield = PANIC;
+    addr->message = string_sprintf("transport filter process failed (%d) "
+      "while writing to %s%s", addr->more_errno, dataname,
+      (addr->more_errno == EX_EXECFAILED) ? ": unable to execute command" : "");
+    }
 
   /* Handle failure to expand header changes */
 
 
   /* Handle failure to expand header changes */
 
@@ -2874,19 +3096,15 @@ if (yield != OK)
   /* Handle failure to complete writing of a data block */
 
   else if (errno == ERRNO_WRITEINCOMPLETE)
   /* Handle failure to complete writing of a data block */
 
   else if (errno == ERRNO_WRITEINCOMPLETE)
-    {
     addr->message = string_sprintf("failed to write data block while "
       "writing to %s", dataname);
     addr->message = string_sprintf("failed to write data block while "
       "writing to %s", dataname);
-    }
 
   /* Handle length mismatch on MBX copying */
 
   #ifdef SUPPORT_MBX
   else if (errno == ERRNO_MBXLENGTH)
 
   /* Handle length mismatch on MBX copying */
 
   #ifdef SUPPORT_MBX
   else if (errno == ERRNO_MBXLENGTH)
-    {
     addr->message = string_sprintf("length mismatch while copying MBX "
       "temporary file to %s", dataname);
     addr->message = string_sprintf("length mismatch while copying MBX "
       "temporary file to %s", dataname);
-    }
   #endif  /* SUPPORT_MBX */
 
   /* For other errors, a general-purpose explanation, if the message is
   #endif  /* SUPPORT_MBX */
 
   /* For other errors, a general-purpose explanation, if the message is
@@ -2900,7 +3118,8 @@ if (yield != OK)
   investigated so far have ftruncate(), whereas not all have the F_FREESP
   fcntl() call (BSDI & FreeBSD do not). */
 
   investigated so far have ftruncate(), whereas not all have the F_FREESP
   fcntl() call (BSDI & FreeBSD do not). */
 
-  if (!isdirectory) ftruncate(fd, saved_size);
+  if (!isdirectory && ftruncate(fd, saved_size))
+    DEBUG(D_transport) debug_printf("Error resetting file size\n");
   }
 
 /* Handle successful writing - we want the modification time to be now for
   }
 
 /* Handle successful writing - we want the modification time to be now for
@@ -2931,7 +3150,7 @@ else
       {
       addr->basic_errno = errno;
       addr->message = string_sprintf("close() error for %s",
       {
       addr->basic_errno = errno;
       addr->message = string_sprintf("close() error for %s",
-        (ob->mailstore_format)? dataname : filename);
+        (ob->mailstore_format) ? dataname : filename);
       yield = DEFER;
       }
 
       yield = DEFER;
       }
 
@@ -2963,19 +3182,18 @@ else
       This makes it possible to build values that are based on the time, and
       still cope with races from multiple simultaneous deliveries. */
 
       This makes it possible to build values that are based on the time, and
       still cope with races from multiple simultaneous deliveries. */
 
-      if (newname == NULL)
+      if (!newname)
         {
         {
-        int i;
         uschar *renameleaf;
         uschar *old_renameleaf = US"";
 
         uschar *renameleaf;
         uschar *old_renameleaf = US"";
 
-        for (i = 0; ; sleep(1), i++)
+        for (int i = 0; ; sleep(1), i++)
           {
           deliver_inode = statbuf.st_ino;
           renameleaf = expand_string(ob->dirfilename);
           deliver_inode = 0;
 
           {
           deliver_inode = statbuf.st_ino;
           renameleaf = expand_string(ob->dirfilename);
           deliver_inode = 0;
 
-          if (renameleaf == NULL)
+          if (!renameleaf)
             {
             addr->transport_return = PANIC;
             addr->message = string_sprintf("Expansion of \"%s\" "
             {
             addr->transport_return = PANIC;
             addr->message = string_sprintf("Expansion of \"%s\" "
@@ -3022,22 +3240,19 @@ else
 
       else
         {
 
       else
         {
-        if (nametag != NULL)
+        if (nametag)
           {
           uschar *iptr = expand_string(nametag);
           {
           uschar *iptr = expand_string(nametag);
-          if (iptr != NULL)
+          if (iptr)
             {
             {
-            uschar *etag = store_get(Ustrlen(iptr) + 2);
+            uschar *etag = store_get(Ustrlen(iptr) + 2, is_tainted(iptr));
             uschar *optr = etag;
             uschar *optr = etag;
-            while (*iptr != 0)
-              {
+            for ( ; *iptr; iptr++)
               if (mac_isgraph(*iptr) && *iptr != '/')
                 {
                 if (optr == etag && isalnum(*iptr)) *optr++ = ':';
                 *optr++ = *iptr;
                 }
               if (mac_isgraph(*iptr) && *iptr != '/')
                 {
                 if (optr == etag && isalnum(*iptr)) *optr++ = ':';
                 *optr++ = *iptr;
                 }
-              iptr++;
-              }
             *optr = 0;
             renamename = string_sprintf("%s%s", newname, etag);
             }
             *optr = 0;
             renamename = string_sprintf("%s%s", newname, etag);
             }
@@ -3078,7 +3293,7 @@ if (!isdirectory) utime(CS filename, &times);
 /* Notify comsat if configured to do so. It only makes sense if the configured
 file is the one that the comsat daemon knows about. */
 
 /* Notify comsat if configured to do so. It only makes sense if the configured
 file is the one that the comsat daemon knows about. */
 
-if (ob->notify_comsat && yield == OK && deliver_localpart != NULL)
+if (ob->notify_comsat && yield == OK && deliver_localpart)
   notify_comsat(deliver_localpart, saved_size);
 
 /* Pass back the final return code in the address structure */
   notify_comsat(deliver_localpart, saved_size);
 
 /* Pass back the final return code in the address structure */
@@ -3111,7 +3326,7 @@ if (mbx_lockfd >= 0)
       debug_printf("unlinking MBX lock file %s\n", mbx_lockname);
     Uunlink(mbx_lockname);
     }
       debug_printf("unlinking MBX lock file %s\n", mbx_lockname);
     Uunlink(mbx_lockname);
     }
-  close(mbx_lockfd);
+  (void)close(mbx_lockfd);
   }
 #endif  /* SUPPORT_MBX */
 
   }
 #endif  /* SUPPORT_MBX */
 
@@ -3126,7 +3341,7 @@ if (hd >= 0) Uunlink(lockname);
 
 /* We get here with isdirectory and filename set only in error situations. */
 
 
 /* We get here with isdirectory and filename set only in error situations. */
 
-if (isdirectory && filename != NULL)
+if (isdirectory && filename)
   {
   Uunlink(filename);
   if (dataname != filename) Uunlink(dataname);
   {
   Uunlink(filename);
   if (dataname != filename) Uunlink(dataname);
@@ -3143,6 +3358,11 @@ if (wait_for_tick) exim_wait_tick(&msg_tv, 1);
 put in the first address of a batch. */
 
 return FALSE;
 put in the first address of a batch. */
 
 return FALSE;
+
+ret_panic:
+  addr->transport_return = PANIC;
+  return FALSE;
 }
 
 }
 
+#endif /*!MACRO_PREDEF*/
 /* End of transport/appendfile.c */
 /* End of transport/appendfile.c */