Update all copyright messages to cover 1995 - 2009. Remove tab from exim_checkaccess.src
[exim.git] / src / src / transports / appendfile.c
index 43575d15ba7a74323099e5f94947f060a8c45d21..df2ce1cd8772f2037456270031a81fe7298eb206 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/transports/appendfile.c,v 1.6 2005/04/27 10:06:00 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/appendfile.c,v 1.24 2009/11/16 19:50:39 nm4 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -107,6 +107,8 @@ optionlist appendfile_transport_options[] = {
       (void *)offsetof(appendfile_transport_options_block, maildir_tag) },
   { "maildir_use_size_file", opt_bool,
       (void *)offsetof(appendfile_transport_options_block, maildir_use_size_file ) } ,
       (void *)offsetof(appendfile_transport_options_block, maildir_tag) },
   { "maildir_use_size_file", opt_bool,
       (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,
@@ -184,16 +186,17 @@ appendfile_transport_options_block appendfile_transport_option_defaults = {
   NULL,           /* mailbox_filecount_string */
   US"^(?:cur|new|\\..*)$",  /* maildir_dir_regex */
   NULL,           /* maildir_tag */
   NULL,           /* mailbox_filecount_string */
   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 */
@@ -247,6 +250,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,26 +259,29 @@ 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;
+double default_value = 0.0;
 int i;
 
 addrlist = addrlist;    /* Keep picky compilers happy */
 dummy = dummy;
 int i;
 
 addrlist = addrlist;    /* Keep picky compilers happy */
 dummy = dummy;
+uid = uid;
+gid = gid;
 
 /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size,
 mailbox_filecount */
 
 for (i = 0; i < 5; i++)
   {
 
 /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size,
 mailbox_filecount */
 
 for (i = 0; i < 5; i++)
   {
-  if (q == NULL) *v = default_value; else
+  double d;
+  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);
 
@@ -314,33 +322,51 @@ 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:
+    if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4) which = US"quota";
+    ob->quota_value = (off_t)d;
     q = ob->quota_filecount;
     q = ob->quota_filecount;
-    v = &(ob->quota_filecount_value);
     break;
 
     case 1:
     break;
 
     case 1:
+    if (d >= 2.0*1024.0*1024.0*1024.0) which = US"quota_filecount";
+    ob->quota_filecount_value = (int)d;
     q = ob->quota_warn_threshold;
     q = ob->quota_warn_threshold;
-    v = &(ob->quota_warn_threshold_value);
     break;
 
     case 2:
     break;
 
     case 2:
+    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;
     q = ob->mailbox_size_string;
-    v = &(ob->mailbox_size_value);
-    default_value = -1;
+    default_value = -1.0;
     break;
 
     case 3:
     break;
 
     case 3:
+    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;
     q = ob->mailbox_filecount_string;
-    v = &(ob->mailbox_filecount_value);
+    break;
+
+    case 4:
+    if (d >= 2.0*1024.0*1024.0*1024.0) which = US"mailbox_filecount";
+    ob->mailbox_filecount_value = (int)d;
     break;
     }
     break;
     }
+
+  if (which != NULL)
+    {
+    *errmsg = string_sprintf("%s value %.10g is too large (overflow) in "
+      "%s transport", which, d, tblock->name);
+    return FAIL;
+    }
   }
 
 return OK;
   }
 
 return OK;
@@ -548,7 +574,7 @@ 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;
@@ -557,7 +583,7 @@ uschar buffer[256];
 
 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);
+sprintf(CS buffer, "%.200s@" OFF_T_FMT "\n", user, offset);
 
 if ((sp = getservbyname("biff", "udp")) == NULL)
   {
 
 if ((sp = getservbyname("biff", "udp")) == NULL)
   {
@@ -573,10 +599,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;
@@ -600,7 +626,7 @@ for (h = &host; h != NULL; h = h->next)
 
   (void)ip_connect(sock, host_af, h->address, ntohs(sp->s_port), 0);
   rc = send(sock, buffer, Ustrlen(buffer) + 1, 0);
 
   (void)ip_connect(sock, host_af, h->address, ntohs(sp->s_port), 0);
   rc = send(sock, buffer, Ustrlen(buffer) + 1, 0);
-  close(sock);
+  (void)close(sock);
 
   if (rc >= 0) break;
   DEBUG(D_transport)
 
   if (rc >= 0) break;
   DEBUG(D_transport)
@@ -705,11 +731,11 @@ 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
+off_t
 check_dir_size(uschar *dirname, int *countptr, const pcre *regex)
 {
 DIR *dir;
 check_dir_size(uschar *dirname, int *countptr, const pcre *regex)
 {
 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;
@@ -734,12 +760,13 @@ while ((ent = readdir(dir)) != NULL)
     if (pcre_exec(regex, NULL, CS name, Ustrlen(name), 0, 0, ovector,6) >= 2)
       {
       uschar *endptr;
     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;
         }
       }
@@ -773,7 +800,9 @@ while ((ent = readdir(dir)) != NULL)
 
 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;
 }
@@ -875,9 +904,10 @@ 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;
 
 /* If the current mailbox size is zero, write a header block */
 struct stat statbuf;
 
 /* If the current mailbox size is zero, write a header block */
@@ -903,8 +933,8 @@ 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. */
@@ -1199,6 +1229,7 @@ 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;
 BOOL isdirectory = FALSE;
 BOOL isfifo = FALSE;
 BOOL wait_for_tick = FALSE;
@@ -1206,8 +1237,8 @@ uid_t uid = geteuid();     /* See note above */
 gid_t gid = getegid();
 int mbformat;
 int mode = (addr->mode > 0)? addr->mode : ob->mode;
 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;
+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;
@@ -1306,10 +1337,15 @@ 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))
+  {
+  address_item *addr2;
+  for (addr2 = addr; addr2 != NULL; 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. */
@@ -1336,10 +1372,12 @@ else
 
 DEBUG(D_transport)
   {
 
 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
+    " 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,
+    mode, ob->notify_comsat, ob->quota_value,
+    ob->quota_warn_threshold_value,
     ob->quota_warn_threshold_is_percent? "%" : "",
     isdirectory? "directory" : "file",
     path, mailbox_formats[mbformat],
     ob->quota_warn_threshold_is_percent? "%" : "",
     isdirectory? "directory" : "file",
     path, mailbox_formats[mbformat],
@@ -1422,7 +1460,7 @@ if (!isdirectory)
     if (cfd >= 0)
       {
       transport_instance *tt = check_file_format(cfd, tblock, addr);
     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 another transport is indicated, call it and return; if no transport
       was found, just return - the error data will have been set up.*/
@@ -1614,7 +1652,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)
         {
@@ -1726,8 +1764,8 @@ 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);
+      (void)Uchown(filename, uid, gid);
+      (void)Uchmod(filename, mode);
       }
 
 
       }
 
 
@@ -1985,7 +2023,7 @@ if (!isdirectory)
             }
           }
 
             }
           }
 
-        mbx_lockfd = Uopen(mbx_lockname, O_RDWR | O_CREAT, 0600);
+        mbx_lockfd = Uopen(mbx_lockname, O_RDWR | O_CREAT, ob->lockfile_mode);
         if (mbx_lockfd < 0)
           {
           addr->basic_errno = ERRNO_LOCKFAILED;
         if (mbx_lockfd < 0)
           {
           addr->basic_errno = ERRNO_LOCKFAILED;
@@ -1994,7 +2032,7 @@ if (!isdirectory)
           goto RETURN;
           }
 
           goto RETURN;
           }
 
-        Uchmod(mbx_lockname, 0600);
+        (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)
@@ -2015,7 +2053,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
@@ -2031,7 +2069,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 */
 
@@ -2117,10 +2155,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 */
 
@@ -2136,7 +2175,7 @@ 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 != NULL)
       {
@@ -2149,11 +2188,8 @@ else
           ob->quota_size_regex);
         return FALSE;
         }
           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 */
@@ -2204,6 +2240,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);
             }
           }
         }
             }
           }
         }
@@ -2229,6 +2267,8 @@ else
 
     if (ob->maildir_dir_regex != NULL)
       {
 
     if (ob->maildir_dir_regex != NULL)
       {
+      int check_path_len = Ustrlen(check_path);
+
       dir_regex = pcre_compile(CS ob->maildir_dir_regex, PCRE_COPT,
         (const char **)&error, &offset, NULL);
       if (dir_regex == NULL)
       dir_regex = pcre_compile(CS ob->maildir_dir_regex, PCRE_COPT,
         (const char **)&error, &offset, NULL);
       if (dir_regex == NULL)
@@ -2238,11 +2278,27 @@ else
           ob->maildir_dir_regex);
         return FALSE;
         }
           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");
+          }
         }
       }
 
         }
       }
 
@@ -2253,8 +2309,10 @@ 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);
 
       maildirsize_fd = maildir_ensure_sizefile(check_path, ob, regex, dir_regex,
         &size, &filecount);
@@ -2287,15 +2345,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);
@@ -2391,6 +2451,8 @@ else
         addr->message = string_sprintf ("failed to open %s (%d tr%s)",
           filename, i, (i == 1)? "y" : "ies");
         addr->basic_errno = errno;
         addr->message = string_sprintf ("failed to open %s (%d tr%s)",
           filename, i, (i == 1)? "y" : "ies");
         addr->basic_errno = errno;
+        if (errno == errno_quota || errno == ENOSPC)
+          addr->user_message = US"mailbox is full";
         return FALSE;
         }
 
         return FALSE;
         }
 
@@ -2406,8 +2468,8 @@ 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);
+    (void)Uchown(filename, uid, gid);
+    (void)Uchmod(filename, mode);
     }
 
   #endif  /* SUPPORT_MAILDIR */
     }
 
   #endif  /* SUPPORT_MAILDIR */
@@ -2448,8 +2510,8 @@ 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);
+    (void)Uchown(filename, uid, gid);
+    (void)Uchmod(filename, mode);
 
     /* Built a C stream from the open file descriptor. */
 
 
     /* Built a C stream from the open file descriptor. */
 
@@ -2459,7 +2521,7 @@ else
       addr->transport_return = PANIC;
       addr->message = string_sprintf("fdopen of %s ("
         "for %s transport) failed", filename, tblock->name);
       addr->transport_return = PANIC;
       addr->message = string_sprintf("fdopen of %s ("
         "for %s transport) failed", filename, tblock->name);
-      close(fd);
+      (void)close(fd);
       Uunlink(filename);
       return FALSE;
       }
       Uunlink(filename);
       return FALSE;
       }
@@ -2477,7 +2539,7 @@ else
           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);
           return FALSE;
           }
           Uunlink(filename);
           return FALSE;
           }
@@ -2506,7 +2568,7 @@ else
           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);
           return FALSE;
           }
           Uunlink(filename);
           return FALSE;
           }
@@ -2540,8 +2602,8 @@ else
       Uunlink(filename);
       return FALSE;
       }
       Uunlink(filename);
       return FALSE;
       }
-    Uchown(dataname, uid, gid);
-    Uchmod(dataname, mode);
+    (void)Uchown(dataname, uid, gid);
+    (void)Uchmod(dataname, mode);
     }
 
   #endif  /* SUPPORT_MAILSTORE */
     }
 
   #endif  /* SUPPORT_MAILSTORE */
@@ -2550,8 +2612,8 @@ 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);
+  (void)Uchown(filename, uid, gid);
+  (void)Uchmod(filename, mode);
   }
 
 
   }
 
 
@@ -2569,12 +2631,13 @@ 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,
+    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);
       ob->quota_is_inclusive? "" : "not ");
     debug_printf("  file count quota = %d count = %d\n",
       ob->quota_filecount_value, mailbox_filecount);
@@ -2722,7 +2785,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 */
@@ -2730,7 +2793,7 @@ 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 to the accurate count of bytes written, including
 added headers. */
@@ -2738,29 +2801,36 @@ added headers. */
 message_size = transport_count;
 
 /* If using a maildir++ quota file, add this message's size to it, and
 message_size = transport_count;
 
 /* 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 */
+  (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;
 
@@ -2835,6 +2905,7 @@ if (yield != OK)
     #else
     addr->message = string_sprintf("mailbox is full");
     #endif  /* EDQUOT */
     #else
     addr->message = string_sprintf("mailbox is full");
     #endif  /* EDQUOT */
+    addr->user_message = US"mailbox is full";
     DEBUG(D_transport) debug_printf("System quota exceeded for %s%s%s\n",
       dataname,
       isdirectory? US"" : US": time since file read = ",
     DEBUG(D_transport) debug_printf("System quota exceeded for %s%s%s\n",
       dataname,
       isdirectory? US"" : US": time since file read = ",
@@ -2905,7 +2976,7 @@ 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) (void)ftruncate(fd, saved_size);
   }
 
 /* Handle successful writing - we want the modification time to be now for
   }
 
 /* Handle successful writing - we want the modification time to be now for
@@ -3116,7 +3187,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 */