-/* $Cambridge: exim/src/src/transports/tf_maildir.c,v 1.4 2005/02/17 11:58:27 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions in support of the use of maildirsize files for handling quotas in
*************************************************/
/* This function is called at the start of a maildir delivery, to ensure that
-all the relevant directories exist.
+all the relevant directories exist. It also creates a maildirfolder file if the
+base directory matches a given pattern.
Argument:
path the base directory name
addr the address item (for setting an error message)
create_directory true if we are allowed to create missing directories
dirmode the mode for created directories
+ maildirfolder_create_regex
+ the pattern to match for maildirfolder creation
Returns: TRUE on success; FALSE on failure
*/
BOOL maildir_ensure_directories(uschar *path, address_item *addr,
- BOOL create_directory, int dirmode)
+ BOOL create_directory, int dirmode, uschar *maildirfolder_create_regex)
{
int i;
struct stat statbuf;
-char *subdirs[] = { "/tmp", "/new", "/cur" };
+const char *subdirs[] = { "/tmp", "/new", "/cur" };
DEBUG(D_transport)
debug_printf("ensuring maildir directories exist in %s\n", path);
/* First ensure that the path we have is a directory; if it does not exist,
create it. Then make sure the tmp, new & cur subdirs of the maildir are
-there. If not, fail which aborts the delivery (even though the cur subdir is
+there. If not, fail. This aborts the delivery (even though the cur subdir is
not actually needed for delivery). Handle all 4 directory tests/creates in a
loop so that code can be shared. */
for (i = 0; i < 4; i++)
{
int j;
- uschar *dir, *mdir;
+ const uschar *dir, *mdir;
if (i == 0)
{
- mdir = US"";
+ mdir = CUS"";
dir = path;
}
else
{
- mdir = US subdirs[i-1];
+ mdir = CUS subdirs[i-1];
dir = mdir + 1;
}
}
}
-return TRUE; /* All directories exist */
+/* If the basic path matches maildirfolder_create_regex, we are dealing with
+a subfolder, and should ensure that a maildirfolder file exists. */
+
+if (maildirfolder_create_regex != NULL)
+ {
+ const uschar *error;
+ int offset;
+ const pcre *regex;
+
+ DEBUG(D_transport) debug_printf("checking for maildirfolder requirement\n");
+
+ if (!(regex = pcre_compile(CS maildirfolder_create_regex, PCRE_COPT,
+ CCSS &error, &offset, NULL)))
+ {
+ addr->message = string_sprintf("appendfile: regular expression "
+ "error: %s at offset %d while compiling %s", error, offset,
+ maildirfolder_create_regex);
+ return FALSE;
+ }
+
+ if (pcre_exec(regex, NULL, CS path, Ustrlen(path), 0, 0, NULL, 0) >= 0)
+ {
+ uschar *fname = string_sprintf("%s/maildirfolder", path);
+ if (Ustat(fname, &statbuf) == 0)
+ {
+ DEBUG(D_transport) debug_printf("maildirfolder already exists\n");
+ }
+ else
+ {
+ int fd = Uopen(fname, O_WRONLY|O_APPEND|O_CREAT, 0600);
+ if (fd < 0)
+ {
+ addr->message = string_sprintf("appendfile: failed to create "
+ "maildirfolder file in %s directory: %s", path, strerror(errno));
+ return FALSE;
+ }
+ (void)close(fd);
+ DEBUG(D_transport) debug_printf("created maildirfolder file\n");
+ }
+ }
+ else
+ {
+ DEBUG(D_transport) debug_printf("maildirfolder file not required\n");
+ }
+ }
+
+return TRUE; /* Everything exists that should exist */
}
uschar buffer[256];
sprintf(CS buffer, "%d 1\n", size);
len = Ustrlen(buffer);
-(void)lseek(fd, 0, SEEK_END);
-(void)write(fd, buffer, len);
-DEBUG(D_transport)
- debug_printf("added '%.*s' to maildirsize file\n", len-1, buffer);
+if (lseek(fd, 0, SEEK_END) >= 0)
+ {
+ len = write(fd, buffer, len);
+ DEBUG(D_transport)
+ debug_printf("added '%.*s' to maildirsize file\n", len-1, buffer);
+ }
}
Returns: the sum of the sizes of the messages
*/
-int
+off_t
maildir_compute_size(uschar *path, int *filecount, time_t *latest,
const pcre *regex, const pcre *dir_regex, BOOL timestamp_only)
{
DIR *dir;
-int sum = 0;
+off_t sum = 0;
struct dirent *ent;
struct stat statbuf;
-dir = opendir(CS path);
-if (dir == NULL) return 0;
+if (!(dir = opendir(CS path)))
+ return 0;
-while ((ent = readdir(dir)) != NULL)
+while ((ent = readdir(dir)))
{
- uschar *name = US ent->d_name;
- uschar buffer[1024];
+ uschar * s, * name = US ent->d_name;
if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue;
/* The name is OK; stat it. */
- if (!string_format(buffer, sizeof(buffer), "%s/%s", path, name))
- {
- DEBUG(D_transport)
- debug_printf("maildir_compute_size: name too long: dir=%s name=%s\n",
- path, name);
- continue;
- }
-
- if (Ustat(buffer, &statbuf) < 0)
+ s = string_sprintf("%s/%s", path, name);
+ if (Ustat(s, &statbuf) < 0)
{
DEBUG(D_transport)
debug_printf("maildir_compute_size: stat error %d for %s: %s\n", errno,
- buffer, strerror(errno));
+ s, strerror(errno));
continue;
}
if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
{
DEBUG(D_transport)
- debug_printf("skipping %s/%s: not a directory\n", path, name);
+ debug_printf("skipping %s/%s: not a directory\n", s, name);
continue;
}
/* If this is a maildir folder, call this function recursively. */
if (name[0] == '.')
- {
- sum += maildir_compute_size(buffer, filecount, latest, regex, dir_regex,
+ sum += maildir_compute_size(s, filecount, latest, regex, dir_regex,
timestamp_only);
- }
/* Otherwise it must be a folder that contains messages (e.g. new or cur), so
we need to get its size, unless all we are interested in is the timestamp. */
else if (!timestamp_only)
- {
- sum += check_dir_size(buffer, filecount, regex);
- }
+ sum += check_dir_size(s, filecount, regex);
}
closedir(dir);
debug_printf("maildir_compute_size (timestamp_only): %ld\n",
(long int) *latest);
else
- debug_printf("maildir_compute_size: path=%s\n sum=%d filecount=%d "
- "timestamp=%ld\n", path, sum, *filecount, (long int) *latest);
+ debug_printf("maildir_compute_size: path=%s\n sum=" OFF_T_FMT
+ " filecount=%d timestamp=%ld\n",
+ path, sum, *filecount, (long int) *latest);
}
return sum;
}
Arguments:
path the path to the maildir directory; this is already backed-up
- to the parent if the delivery diretory is a maildirfolder
+ to the parent if the delivery directory is a maildirfolder
ob the appendfile options block
regex a compiled regex for getting a file's size from its name
dir_regex a compiled regex for selecting maildir directories
int
maildir_ensure_sizefile(uschar *path, appendfile_transport_options_block *ob,
- const pcre *regex, const pcre *dir_regex, int *returned_size,
+ const pcre *regex, const pcre *dir_regex, off_t *returned_size,
int *returned_filecount)
{
int count, fd;
-int cached_quota = 0;
+off_t cached_quota = 0;
int cached_quota_filecount = 0;
-int size = 0;
int filecount = 0;
int linecount = 0;
+off_t size = 0;
uschar *filename;
uschar buffer[MAX_FILE_SIZE];
uschar *ptr = buffer;
filename = string_sprintf("%s/maildirsize", path);
DEBUG(D_transport) debug_printf("looking for maildirsize in %s\n", path);
-fd = Uopen(filename, O_RDWR|O_APPEND, 0);
-if (fd < 0)
+if ((fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600)) < 0)
{
if (errno != ENOENT) return -1;
DEBUG(D_transport)
still correct, and that the size of the file is still small enough. If so,
compute the maildir size from the file. */
-count = read(fd, buffer, sizeof(buffer));
-if (count >= sizeof(buffer))
+if ((count = read(fd, buffer, sizeof(buffer))) >= sizeof(buffer))
{
DEBUG(D_transport)
debug_printf("maildirsize file too big (%d): recalculating\n", count);
for (;;)
{
- long int n = Ustrtol(ptr, &endptr, 10);
+ off_t n = (off_t)Ustrtod(ptr, &endptr);
/* Only two data items are currently defined; ignore any others that
may be present. The spec is for a number followed by a letter. Anything
else we reject and recalculate. */
if (*endptr == 'S') cached_quota = n;
- else if (*endptr == 'C') cached_quota_filecount = n;
+ else if (*endptr == 'C') cached_quota_filecount = (int)n;
if (!isalpha(*endptr++))
{
DEBUG(D_transport)
{
DEBUG(D_transport)
debug_printf("cached quota is out of date: recalculating\n"
- " quota=%d cached_quota=%d filecount_quota=%d "
- "cached_quota_filecount=%d\n", ob->quota_value, cached_quota,
- ob->quota_filecount_value, cached_quota_filecount);
+ " quota=" OFF_T_FMT " cached_quota=" OFF_T_FMT " filecount_quota=%d "
+ "cached_quota_filecount=%d\n", ob->quota_value,
+ cached_quota, ob->quota_filecount_value, cached_quota_filecount);
goto RECALCULATE;
}
if (*endptr == 0) break;
linecount++;
ptr = endptr;
- size += Ustrtol(ptr, &endptr, 10);
+ size += (off_t)Ustrtod(ptr, &endptr);
if (*endptr != ' ') break;
ptr = endptr + 1;
filecount += Ustrtol(ptr, &endptr, 10);
if (size < 0 || filecount < 0)
{
DEBUG(D_transport) debug_printf("negative value in maildirsize "
- "(size=%d count=%d): recalculating\n", size, filecount);
+ "(size=" OFF_T_FMT " count=%d): recalculating\n", size, filecount);
goto RECALCULATE;
}
RECALCULATE:
- if (fd >= 0) close(fd);
+ if (fd >= 0) (void)close(fd);
old_latest = 0;
filecount = 0;
size = maildir_compute_size(path, &filecount, &old_latest, regex, dir_regex,
FALSE);
(void)gettimeofday(&tv, NULL);
- tempname = string_sprintf("%s/tmp/%lu.H%luP%lu.%s", path, tv.tv_sec,
- tv.tv_usec, getpid(), primary_hostname);
+ tempname = string_sprintf("%s/tmp/" TIME_T_FMT ".H%luP%lu.%s",
+ path, tv.tv_sec, tv.tv_usec, (long unsigned) getpid(), primary_hostname);
- fd = Uopen(tempname, O_RDWR|O_CREAT|O_EXCL, 0600);
+ fd = Uopen(tempname, O_RDWR|O_CREAT|O_EXCL, ob->mode ? ob->mode : 0600);
if (fd >= 0)
{
- (void)sprintf(CS buffer, "%dS,%dC\n%d %d\n", ob->quota_value,
- ob->quota_filecount_value, size, filecount);
+ (void)sprintf(CS buffer, OFF_T_FMT "S,%dC\n" OFF_T_FMT " %d\n",
+ ob->quota_value, ob->quota_filecount_value, size, filecount);
len = Ustrlen(buffer);
if (write(fd, buffer, len) != len || Urename(tempname, filename) < 0)
{
- close(fd);
+ (void)close(fd);
fd = -1;
}
}
DEBUG(D_transport) debug_printf("abandoning maildirsize because of "
"a later subdirectory modification\n");
(void)Uunlink(filename);
- close(fd);
- fd = -1;
+ (void)close(fd);
+ fd = -2;
}
}
/* Return the sizes and the file descriptor, if any */
-DEBUG(D_transport) debug_printf("returning maildir size=%d filecount=%d\n",
- size, filecount);
+DEBUG(D_transport) debug_printf("returning maildir size=" OFF_T_FMT
+ " filecount=%d\n", size, filecount);
*returned_size = size;
*returned_filecount = filecount;
return fd;