SHA256
1
0
forked from pool/postfix
postfix/postfix-vda-v10-2.8.12.patch
Peter Varkoly dcdcda0a8e - update to 2,9.4
* tls support:
     Support to turn off the TLSv1.1 and TLSv1.2 protocols:
     To temporarily turn off problematic protocols globally:
     /etc/postfix/main.cf:
       smtp_tls_protocols = !SSLv2, !TLSv1.1, !TLSv1.2
       smtp_tls_mandatory_protocols = !SSLv2, !TLSv1.1, !TLSv1.2
     However, it may be better to temporarily turn off problematic
     protocols for broken sites only:
     /etc/postfix/main.cf:
       smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
     /etc/postfix/tls_policy:
       example.com         may protocols=!SSLv2:!TLSv1.1:!TLSv1.2
  * 20111012 To simplify integration with third-party
     applications, the Postfix sendmail command now always transforms
     all input lines ending in <CR><LF> into UNIX format (lines ending
     in <LF>). Specify "sendmail_fix_line_endings = strict" to restore
     historical Postfix behavior (i.e. convert all input lines ending
     in <CR><LF> only if the first line ends in <CR><LF>).
  * 20120114 Logfile-based alerting systems may need to be
     updated to look for "error" messages in addition to "fatal" messages.
     Specify "daemon_table_open_error_is_fatal = yes" to get the historical
     behavior (immediate termination with "fatal" message).
  * enable_long_queue_ids Postfix 2.9 introduces support for non-repeating queue IDs (also
     used as queue file names). These names are encoded in a mix of upper
     case, lower case and decimal digit characters.  Long queue IDs are
     disabled by default to avoid breaking tools that parse logfiles and
     that expect queue IDs with the smaller [A-F0-9] character set.
  * 20111209 memcache lookup and update support. This provides
     a way to share postscreen(8) or verify(8) caches between Postfix

OBS-URL: https://build.opensuse.org/package/show/server:mail/postfix?expand=0&rev=154
2013-01-10 15:05:52 +00:00

1385 lines
54 KiB
Diff

Index: README_FILES/VDA_README
===================================================================
--- /dev/null
+++ README_FILES/VDA_README
@@ -0,0 +1,9 @@
+Postfix VDA patch for maildir++ quota support by
+ Anderson Nadal <andernadal@gmail.com>
+ Tomas Macek <maca02@atlas.cz>
+ Lucca Longinotti
+
+See VDA patch official website http://vda.sf.net for instructions
+howto patch the Postfix's sourcetree and configure the options
+provided by this patch.
+
Index: src/global/mail_params.h
===================================================================
--- src/global/mail_params.h.orig
+++ src/global/mail_params.h
@@ -2333,6 +2333,54 @@ extern char *var_virt_uid_maps;
#define DEF_VIRT_GID_MAPS ""
extern char *var_virt_gid_maps;
+#define VAR_VIRT_MAILBOX_LIMIT_MAPS "virtual_mailbox_limit_maps"
+#define DEF_VIRT_MAILBOX_LIMIT_MAPS ""
+extern char *var_virt_mailbox_limit_maps;
+
+#define VAR_VIRT_MAILBOX_LIMIT_INBOX "virtual_mailbox_limit_inbox"
+#define DEF_VIRT_MAILBOX_LIMIT_INBOX 0
+extern bool var_virt_mailbox_limit_inbox;
+
+#define VAR_VIRT_MAILBOX_LIMIT_OVERRIDE "virtual_mailbox_limit_override"
+#define DEF_VIRT_MAILBOX_LIMIT_OVERRIDE 0
+extern bool var_virt_mailbox_limit_override;
+
+#define VAR_VIRT_MAILDIR_EXTENDED "virtual_maildir_extended"
+#define DEF_VIRT_MAILDIR_EXTENDED 0
+extern bool var_virt_maildir_extended;
+
+#define VAR_VIRT_OVERQUOTA_BOUNCE "virtual_overquota_bounce"
+#define DEF_VIRT_OVERQUOTA_BOUNCE 0
+extern bool var_virt_overquota_bounce;
+
+#define VAR_VIRT_MAILDIR_LIMIT_MESSAGE "virtual_maildir_limit_message"
+#define DEF_VIRT_MAILDIR_LIMIT_MESSAGE "Sorry, the user's maildir has overdrawn his diskspace quota, please try again later."
+extern char *var_virt_maildir_limit_message;
+
+#define VAR_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS "virtual_maildir_limit_message_maps"
+#define DEF_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS ""
+extern char *var_virt_maildir_limit_message_maps;
+
+#define VAR_VIRT_MAILDIR_SUFFIX "virtual_maildir_suffix"
+#define DEF_VIRT_MAILDIR_SUFFIX ""
+extern char *var_virt_maildir_suffix;
+
+#define VAR_VIRT_TRASH_COUNT "virtual_trash_count"
+#define DEF_VIRT_TRASH_COUNT 0
+extern bool var_virt_trash_count;
+
+#define VAR_VIRT_TRASH_NAME "virtual_trash_name"
+#define DEF_VIRT_TRASH_NAME ".Trash"
+extern char *var_virt_trash_name;
+
+#define VAR_VIRT_MAILDIR_FILTER "virtual_maildir_filter"
+#define DEF_VIRT_MAILDIR_FILTER 0
+extern bool var_virt_maildir_filter;
+
+#define VAR_VIRT_MAILDIR_FILTER_MAPS "virtual_maildir_filter_maps"
+#define DEF_VIRT_MAILDIR_FILTER_MAPS ""
+extern char *var_virt_maildir_filter_maps;
+
#define VAR_VIRT_MINUID "virtual_minimum_uid"
#define DEF_VIRT_MINUID 100
extern int var_virt_minimum_uid;
Index: src/util/file_limit.c
===================================================================
--- src/util/file_limit.c.orig
+++ src/util/file_limit.c
@@ -85,7 +85,11 @@ void set_file_limit(off_t limit)
#else
struct rlimit rlim;
- rlim.rlim_cur = rlim.rlim_max = limit;
+ /* rlim_max can only be changed by root. */
+ if (getrlimit(RLIMIT_FSIZE, &rlim) < 0)
+ msg_fatal("getrlimit: %m");
+ rlim.rlim_cur = limit;
+
if (setrlimit(RLIMIT_FSIZE, &rlim) < 0)
msg_fatal("setrlimit: %m");
#ifdef SIGXFSZ
Index: src/virtual/mailbox.c
===================================================================
--- src/virtual/mailbox.c.orig
+++ src/virtual/mailbox.c
@@ -70,6 +70,70 @@
#define YES 1
#define NO 0
+/* change_mailbox_limit - change limit for mailbox file */
+static int change_mailbox_limit(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ char *myname = "change_mailbox_limit";
+ const char *limit_res;
+ long n = 0;
+ int status = NO;
+
+ /*
+ * Look up the virtual mailbox limit size for this user.
+ * Fall back to virtual_mailbox_limit in case lookup failed.
+ * If virtual mailbox limit size is negative, fall back to virtual_mailbox_limit.
+ * If it's 0, set the mailbox limit to 0, which means unlimited.
+ * If it's more than 0 (positive int), check if the value is smaller than the maximum message size,
+ * if it is and the virtual mailbox limit can't be overridden, fall back to virtual_mailbox_limit and
+ * warn the user, else use the value directly as the mailbox limit.
+ */
+ if (*var_virt_mailbox_limit_maps != 0 && (limit_res = mail_addr_find(virtual_mailbox_limit_maps, state.msg_attr.user, (char **) NULL)) != 0) {
+ n = atol(limit_res);
+ if (n > 0) {
+ if ((n < var_message_limit) && (!var_virt_mailbox_limit_override)) {
+ set_file_limit(var_virt_mailbox_limit);
+ status = NO;
+
+ msg_warn("%s: recipient %s - virtual mailbox limit is "
+ "smaller than %s in %s - falling back to %s",
+ myname,
+ state.msg_attr.user,
+ VAR_MESSAGE_LIMIT,
+ virtual_mailbox_limit_maps->title,
+ VAR_VIRT_MAILBOX_LIMIT);
+ }
+ else {
+ set_file_limit((off_t) n);
+ status = YES;
+
+ if (msg_verbose)
+ msg_info("%s: set virtual mailbox limit size for %s to %ld",
+ myname, usr_attr.mailbox, n);
+ }
+ }
+ else if (n == 0) {
+ set_file_limit(OFF_T_MAX);
+ status = YES;
+
+ if (msg_verbose)
+ msg_info("%s: set virtual mailbox limit size for %s to %ld",
+ myname, usr_attr.mailbox, OFF_T_MAX);
+ }
+ else {
+ /* Invalid limit size (negative). Use default virtual_mailbox_limit. */
+ set_file_limit(var_virt_mailbox_limit);
+ status = NO;
+ }
+ }
+ else {
+ /* There is no limit in the maps. Use default virtual_mailbox_limit. */
+ set_file_limit(var_virt_mailbox_limit);
+ status = NO;
+ }
+
+ return(status);
+}
+
/* deliver_mailbox_file - deliver to recipient mailbox */
static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
@@ -213,62 +277,72 @@ int deliver_mailbox(LOCAL_STATE stat
* Look up the mailbox owner rights. Defer in case of trouble.
*/
uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user,
- IGNORE_EXTENSION);
- if (uid_res == 0) {
- msg_warn("recipient %s: not found in %s",
- state.msg_attr.user, virtual_uid_maps->title);
- dsb_simple(why, "4.3.5", "mail system configuration error");
- *statusp = defer_append(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr));
- RETURN(YES);
+ IGNORE_EXTENSION);
+
+ if ((uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user, (char **) 0)) == 0) {
+ if ((uid_res = maps_find(virtual_uid_maps, strchr(state.msg_attr.user, '@'), DICT_FLAG_FIXED)) == 0) {
+ msg_warn("recipient %s: not found in %s", state.msg_attr.user, virtual_uid_maps->title);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
+ RETURN(YES);
+ }
}
+
if ((n = atol(uid_res)) < var_virt_minimum_uid) {
- msg_warn("recipient %s: bad uid %s in %s",
- state.msg_attr.user, uid_res, virtual_uid_maps->title);
- dsb_simple(why, "4.3.5", "mail system configuration error");
- *statusp = defer_append(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr));
- RETURN(YES);
+ msg_warn("recipient %s: bad uid %s in %s", state.msg_attr.user, uid_res, virtual_uid_maps->title);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
+ RETURN(YES);
}
+
usr_attr.uid = (uid_t) n;
/*
* Look up the mailbox group rights. Defer in case of trouble.
*/
gid_res = mail_addr_find(virtual_gid_maps, state.msg_attr.user,
- IGNORE_EXTENSION);
- if (gid_res == 0) {
- msg_warn("recipient %s: not found in %s",
- state.msg_attr.user, virtual_gid_maps->title);
- dsb_simple(why, "4.3.5", "mail system configuration error");
- *statusp = defer_append(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr));
- RETURN(YES);
+ IGNORE_EXTENSION);
+
+ if ((gid_res = mail_addr_find(virtual_gid_maps, state.msg_attr.user, (char **) 0)) == 0) {
+ if ((gid_res = maps_find(virtual_gid_maps, strchr(state.msg_attr.user, '@'), DICT_FLAG_FIXED)) == 0) {
+ msg_warn("recipient %s: not found in %s", state.msg_attr.user, virtual_gid_maps->title);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
+ RETURN(YES);
+ }
}
+
if ((n = atol(gid_res)) <= 0) {
- msg_warn("recipient %s: bad gid %s in %s",
- state.msg_attr.user, gid_res, virtual_gid_maps->title);
- dsb_simple(why, "4.3.5", "mail system configuration error");
- *statusp = defer_append(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr));
- RETURN(YES);
+ msg_warn("recipient %s: bad gid %s in %s", state.msg_attr.user, gid_res, virtual_gid_maps->title);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
+ RETURN(YES);
}
+
usr_attr.gid = (gid_t) n;
if (msg_verbose)
- msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
- myname, state.level, usr_attr.mailbox,
- (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
+ msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
+ myname, state.level, usr_attr.mailbox,
+ (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
/*
* Deliver to mailbox or to maildir.
*/
#define LAST_CHAR(s) (s[strlen(s) - 1])
- if (LAST_CHAR(usr_attr.mailbox) == '/')
- *statusp = deliver_maildir(state, usr_attr);
- else
- *statusp = deliver_mailbox_file(state, usr_attr);
+ if (LAST_CHAR(usr_attr.mailbox) == '/') {
+ *statusp = deliver_maildir(state, usr_attr);
+ }
+ else {
+ int changed_limit;
+
+ changed_limit = change_mailbox_limit(state, usr_attr);
+ *statusp = deliver_mailbox_file(state, usr_attr);
+
+ if (changed_limit)
+ set_file_limit(var_virt_mailbox_limit);
+ }
/*
* Cleanup.
Index: src/virtual/maildir.c
===================================================================
--- src/virtual/maildir.c.orig
+++ src/virtual/maildir.c
@@ -63,28 +63,420 @@
#include <mbox_open.h>
#include <dsn_util.h>
+/* Patch library. */
+
+#include <sys/types.h> /* opendir(3), stat(2) */
+#include <sys/stat.h> /* stat(2) */
+#include <dirent.h> /* opendir(3) */
+#include <unistd.h> /* stat(2) */
+#include <stdlib.h> /* atol(3) */
+#include <string.h> /* strrchr(3) */
+#include <vstring_vstream.h>
+#include <dict.h>
+#include <dict_regexp.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <sys_defs.h>
+#include <mail_addr_find.h>
+
/* Application-specific. */
#include "virtual.h"
-/* deliver_maildir - delivery to maildir-style mailbox */
+/* Maildirsize maximal size. */
+
+#define SIZEFILE_MAX 5120
+
+/*
+ * Chris Stratford <chriss@pipex.net>
+ * Read the maildirsize file to get quota info.
+ *
+ * Arguments:
+ * dirname: the maildir
+ * countptr: number of messages
+ *
+ * Returns the size of all mails as read from maildirsize,
+ * zero if it couldn't read the file.
+ */
+static long read_maildirsize(char *filename, long *sumptr, long *countptr)
+{
+ char *myname = "read_maildirsize";
+ struct stat statbuf;
+ VSTREAM *sizefile;
+ char *p;
+ int len, first;
+ long sum = 0, count = 0, ret_value = -1;
+
+ if (msg_verbose)
+ msg_info("%s: we will use sizefile = '%s'", myname, filename);
+
+ sizefile = vstream_fopen(filename, O_RDONLY, 0);
+ if (!sizefile) {
+ if (msg_verbose)
+ msg_info("%s: cannot open %s: %m (maybe file does not exist)", myname, filename);
+
+ return -1;
+ } else if (stat(filename, &statbuf) < 0 || statbuf.st_size > SIZEFILE_MAX) {
+ if (sizefile) {
+ vstream_fclose(sizefile);
+ unlink(filename);
+ }
+
+ if (msg_verbose)
+ msg_info("%s: stat() returned < 0 or filesize > SIZEFILE_MAX (filename = %s, filesize = %ld)", myname, filename, statbuf.st_size);
+
+ return -1;
+ }
+
+ VSTRING *sizebuf = vstring_alloc(SIZEFILE_MAX);
+ len = vstream_fread(sizefile, STR(sizebuf), SIZEFILE_MAX);
+
+ p = STR(sizebuf);
+ *(p + len) = '\0';
+ first = 1;
+
+ while (*p) {
+ long n = 0, c = 0;
+ char *q = p;
+
+ while (*p) {
+ if (*p++ == '\n') {
+ p[-1] = 0;
+ break;
+ }
+ }
+
+ if (first) {
+ first = 0;
+ continue;
+ }
+
+ if (sscanf(q, "%ld %ld", &n, &c) == 2) {
+ sum += n;
+ count += c;
+ /* if (msg_verbose)
+ msg_info("%s: we read line '%s', totals: sum = %ld, count = %ld", myname, q, sum, count); */
+ }
+ else {
+ vstream_fclose(sizefile);
+ unlink(filename);
+ msg_warn("%s: invalid line '%s' found in %s, removing maildirsize file", myname, q, filename);
+ vstring_free(sizebuf);
+
+ return -1;
+ }
+ }
+
+ *countptr = count;
+ *sumptr = sum;
+
+ if (sum < 0 || count < 0 || (sum == 0 && count != 0) || (sum != 0 && count == 0)) {
+ if (msg_verbose) {
+ msg_info("%s: we will return -1 and unlink %s, because file count or sum is <= 0 (sum = %ld, count = %ld)", myname, filename, sum, count);
+ }
+
+ unlink(filename);
+ ret_value = -1;
+ } else {
+ if (msg_verbose)
+ msg_info("%s: we will return Maildir size = %ld, count = %ld", myname, *sumptr, *countptr);
+
+ ret_value = sum;
+ }
-int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
+ vstream_fclose(sizefile);
+ vstring_free(sizebuf);
+
+ return ret_value;
+}
+
+/*
+ * Gives the size of the file according to the Maildir++ extension
+ * present in the filename (code taken from courier-imap).
+ *
+ * Arguments:
+ * n: filename
+ *
+ * Returns the size given in ",S=<size>" in the filename,
+ * zero if it cannot find ",S=<size>" in the filename.
+ */
+static long maildir_parsequota(const char *n)
+{
+ const char *o;
+ int yes = 0;
+
+ if ((o = strrchr(n, '/')) == 0)
+ o = n;
+
+ for (; *o; o++) {
+ if (*o == ':')
+ break;
+ }
+
+ for (; o >= n; --o) {
+ if (*o == '/')
+ break;
+
+ if (*o == ',' && o[1] == 'S' && o[2] == '=') {
+ yes = 1;
+ o += 3;
+ break;
+ }
+ }
+
+ if (yes) {
+ long s = 0;
+
+ while (*o >= '0' && *o <= '9')
+ s = s*10 + (*o++ - '0');
+
+ return s;
+ }
+
+ return 0;
+}
+
+/*
+ * Computes quota usage for a directory (taken from exim).
+ *
+ * This function is called to determine the exact quota usage of a virtual
+ * maildir box. To achieve maximum possible speed while doing this, it takes
+ * advantage of the maildirsize file and the Maildir++ extensions to filenames,
+ * when applicable and configured to be used. In all other cases it simply
+ * stats all the files as needed to get the size information.
+ *
+ * Arguments:
+ * dirname: the name of the directory
+ * countptr: where to add the file count (because this function recurses)
+ *
+ * Returns the sum of the sizes of all measurable files,
+ * zero if the directory could not be opened.
+ */
+static long check_dir_size(char *dirname, long *countptr)
+{
+ char *myname = "check_dir_size";
+ DIR *dir;
+ long sum = 0;
+ struct dirent *ent;
+ struct stat statbuf;
+
+ dir = opendir(dirname);
+ if (dir == NULL) {
+ if (make_dirs(dirname, 0700) == 0) { /* Try to create the dirs. */
+ dir = opendir(dirname); /* Reopen the dir. */
+ if (dir == NULL) {
+ msg_warn("%s: cannot reopen directory: %s", myname, dirname);
+ return 0;
+ }
+ }
+ else {
+ msg_warn("%s: cannot open directory: %s", myname, dirname);
+ return 0;
+ }
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+ char *name = ent->d_name;
+ long tmpsum = 0;
+ VSTRING *buffer;
+
+ /* do not count dot a double-dot dirs */
+ if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ continue;
+ /* do not count if this is the trash subdir and if we should NOT count it */
+ else if (var_virt_trash_count == 0 && strcmp(name, var_virt_trash_name) == 0)
+ continue;
+
+ /*
+ * Here comes the real logic behind this function.
+ * Optimized to be the most efficient possible,
+ * depending on the settings given.
+ * See above for a more detailed description.
+ */
+ if (var_virt_mailbox_limit_inbox) {
+ if (var_virt_maildir_extended && (tmpsum = maildir_parsequota(name))) {
+ sum += tmpsum;
+ (*countptr)++;
+ }
+ else {
+ buffer = vstring_alloc(1024);
+ vstring_sprintf(buffer, "%s/%s", dirname, name);
+
+ if (stat(STR(buffer), &statbuf) < 0) {
+ vstring_free(buffer);
+ continue;
+ }
+ if ((statbuf.st_mode & S_IFREG) != 0) {
+ sum += (long) statbuf.st_size;
+ (*countptr)++;
+ }
+
+ vstring_free(buffer);
+ }
+ }
+ else {
+ buffer = vstring_alloc(1024);
+ vstring_sprintf(buffer, "%s/%s", dirname, name);
+
+ if (stat(STR(buffer), &statbuf) < 0) {
+ vstring_free(buffer);
+ continue;
+ }
+ if ((statbuf.st_mode & S_IFREG) != 0) {
+ if (strcmp(dirname + strlen(dirname) - 3, "new") == 0 || strcmp(dirname + strlen(dirname) - 3, "cur") == 0 || strcmp(dirname + strlen(dirname) - 3, "tmp") == 0) {
+ sum += (long) statbuf.st_size;
+ (*countptr)++;
+ }
+ }
+ else if ((statbuf.st_mode & S_IFDIR) != 0) {
+ sum += check_dir_size(STR(buffer), countptr);
+ }
+
+ vstring_free(buffer);
+ }
+ }
+ closedir(dir);
+
+ if (msg_verbose)
+ msg_info("%s: full scan done: dir=%s sum=%ld count=%ld", myname, dirname, sum, *countptr);
+
+ return sum;
+}
+
+/* Cut all occurrences of pattern from string. */
+static char *strcut(char *str, const char *pat)
+{
+ char *ptr, *loc, *ret;
+ ret = str;
+ loc = str;
+
+ /* No match, return original string. */
+ if (!strstr(loc, pat))
+ return(str);
+
+ while (*loc && (ptr = strstr(loc, pat))) {
+ while (loc < ptr)
+ *str++ = *loc++;
+ loc += strlen(pat);
+ }
+
+ while (*loc)
+ *str++ = *loc++;
+
+ *str = 0;
+
+ return(ret);
+}
+
+/* Check if maildirfilter file is up-to-date compared to SQL, (re)write it if not. */
+static long sql2file(char *filename, char *user)
+{
+ char *myname = "sql2file";
+ char *filter_sqlres;
+ char filter_fileres[128];
+ long sqlmtime = 0, filemtime = 0, retval = 0;
+ int filterfile, size_sqlres, i;
+ struct stat statbuf;
+
+ if (*var_virt_maildir_filter_maps != 0) {
+ filter_sqlres = (char *) mymalloc(16000);
+ filter_sqlres = (char *) mail_addr_find(virtual_maildir_filter_maps, user, (char **) 0);
+
+ if (filter_sqlres) {
+ strcut(filter_sqlres, "\r");
+ if (filter_sqlres[0] == '#' && filter_sqlres[1] == ' ' && filter_sqlres[2] == 'M') {
+ size_sqlres = strlen(filter_sqlres);
+
+ for (i = 4; i <= size_sqlres; i++) {
+ if(filter_sqlres[i] == '/' && filter_sqlres[i+1] == '^') {
+ filter_sqlres[i-1] = '\n';
+ }
+ }
+
+ filter_sqlres[(size_sqlres+1)] = '\0';
+
+ sqlmtime = atol(filter_sqlres+3);
+ retval = sqlmtime;
+
+ filterfile = open(filename, O_RDONLY, 0);
+ if (filterfile) {
+ read(filterfile, (void *) filter_fileres, 127);
+ close(filterfile);
+
+ filemtime = atol(filter_fileres+3);
+ }
+
+ if (msg_verbose)
+ msg_info("%s: filter data: sql_size=%d sql_mtime=%ld file_mtime=%ld", myname, strlen(filter_sqlres), sqlmtime, filemtime);
+ }
+ if (sqlmtime != filemtime && sqlmtime != 0) {
+ if ((filterfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0640))) {
+ if (msg_verbose)
+ msg_info("%s: updating filter file: %s", myname, filename);
+ write(filterfile, filter_sqlres, strlen(filter_sqlres));
+ close(filterfile);
+ }
+ else {
+ msg_warn("%s: can't create filter file: %s", myname, filename);
+ retval = 0;
+ }
+ }
+ }
+ }
+ else {
+ if (stat(filename, &statbuf) == 0)
+ retval = (long) statbuf.st_mtime;
+ if (msg_verbose)
+ msg_info("%s: processing filter file: file_mtime=%ld", myname, retval);
+ }
+
+ return retval;
+}
+
+/* deliver_maildir - delivery to maildir-style mailbox */
+int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
{
const char *myname = "deliver_maildir";
- char *newdir;
- char *tmpdir;
- char *curdir;
- char *tmpfile;
- char *newfile;
+ char *newdir;
+ char *tmpdir;
+ char *curdir;
+ char *newfile;
+ char *tmpfile;
DSN_BUF *why = state.msg_attr.why;
VSTRING *buf;
VSTREAM *dst;
- int mail_copy_status;
- int deliver_status;
- int copy_flags;
- struct stat st;
- struct timeval starttime;
+ int mail_copy_status;
+ int deliver_status;
+ int copy_flags;
+ struct stat st;
+ struct timeval starttime;
+
+ /* Maildir Quota. */
+ const char *limit_res; /* Limit from map. */
+ char *sizefilename = (char *) 0; /* Maildirsize file name. */
+ VSTRING *filequota; /* Quota setting from the maildirsize file. */
+ VSTREAM *sizefile; /* Maildirsize file handle. */
+ long n = 0; /* Limit in long integer format. */
+ long saved_count = 0; /* The total number of files. */
+ long saved_size = 0; /* The total quota of all files. */
+ struct stat mail_stat; /* To check the size of the mail to be written. */
+ struct stat sizefile_stat; /* To check the size of the maildirsize file. */
+ time_t tm; /* To check the age of the maildirsize file. */
+
+ /* Maildir Filters. */
+ const char *value, *cmd_text; /* Filter values. */
+ char *filtername;
+ char *header;
+ char *bkpnewfile;
+ char *mdffilename = (char *) 0; /* Maildirfolder file name. */
+ VSTRING *fltstr;
+ VSTREAM *tmpfilter;
+ VSTREAM *mdffile; /* Maildirfolder file handle. */
+ DICT *FILTERS;
+ long sqlmtime; /* Latest modification time from sql2file(). */
+ int cmd_len;
+ int read_mds = -1; /* read_maildirsize() returned value */
+ struct stat mdffile_stat; /* To check if the maildirfolder file exists. */
GETTIMEOFDAY(&starttime);
@@ -93,15 +485,14 @@ int deliver_maildir(LOCAL_STATE stat
*/
state.level++;
if (msg_verbose)
- MSG_LOG_STATE(myname, state);
+ MSG_LOG_STATE(myname, state);
/*
* Don't deliver trace-only requests.
*/
if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
- dsb_simple(why, "2.0.0", "delivers to maildir");
- return (sent(BOUNCE_FLAGS(state.request),
- SENT_ATTR(state.msg_attr)));
+ dsb_simple(why, "2.0.0", "delivers to maildir");
+ return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
}
/*
@@ -109,17 +500,115 @@ int deliver_maildir(LOCAL_STATE stat
* attribute to reflect the final recipient.
*/
if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
- msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
+ msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
state.msg_attr.delivered = state.msg_attr.rcpt.address;
mail_copy_status = MAIL_COPY_STAT_WRITE;
buf = vstring_alloc(100);
- copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH
- | MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT;
+ copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT;
- newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0);
- tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0);
- curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0);
+ /*
+ * Concatenate the maildir suffix (if set).
+ */
+ if (*var_virt_maildir_suffix == 0) {
+ newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0);
+ tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0);
+ curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0);
+ }
+ else {
+ newdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
+ tmpdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
+ curdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
+ newdir = concatenate(newdir, "new/", (char *) 0);
+ tmpdir = concatenate(tmpdir, "tmp/", (char *) 0);
+ curdir = concatenate(curdir, "cur/", (char *) 0);
+ }
+
+ /* get the sizefilename, no matter if we use var_virt_maildir_extended */
+ if (*var_virt_maildir_suffix == 0) {
+ sizefilename = concatenate(usr_attr.mailbox, "maildirsize", (char *) 0);
+ } else {
+ sizefilename = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
+ sizefilename = concatenate(sizefilename, "maildirsize", (char *) 0);
+ }
+
+ /*
+ * Look up the virtual maildir limit size for this user.
+ * Fall back to virtual_mailbox_limit in case lookup failed.
+ * If virtual maildir limit size is negative, fall back to virtual_mailbox_limit.
+ * If it's 0, set the mailbox limit to 0, which means unlimited.
+ * If it's more than 0 (positive int), check if the value is smaller than the maximum message size,
+ * if it is and the virtual maildir limit can't be overridden, fall back to virtual_mailbox_limit and
+ * warn the user, else use the value directly as the maildir limit.
+ */
+ if (*var_virt_mailbox_limit_maps != 0 && (limit_res = mail_addr_find(virtual_mailbox_limit_maps, state.msg_attr.user, (char **) NULL)) != 0) {
+ n = atol(limit_res);
+ if (n > 0) {
+ if ((n < var_message_limit) && (!var_virt_mailbox_limit_override)) {
+ n = var_virt_mailbox_limit;
+
+ msg_warn("%s: recipient %s - virtual maildir limit is smaller than %s in %s - falling back to %s",
+ myname, state.msg_attr.user, VAR_MESSAGE_LIMIT, virtual_mailbox_limit_maps->title,
+ VAR_VIRT_MAILBOX_LIMIT);
+ }
+ else {
+ if (msg_verbose)
+ msg_info("%s: set virtual maildir limit size for %s to %ld",
+ myname, usr_attr.mailbox, n);
+ }
+ }
+ else if (n == 0) {
+ if (msg_verbose)
+ msg_info("%s: set virtual maildir limit size for %s to %ld",
+ myname, usr_attr.mailbox, n);
+ }
+ else {
+ if (msg_verbose)
+ msg_info("%s: quota is negative (%ld), using default virtual_mailbox_limit (%d)",
+ myname, n, var_virt_mailbox_limit);
+ /* Invalid limit size (negative). Use default virtual_mailbox_limit. */
+ n = var_virt_mailbox_limit;
+ }
+ }
+ else {
+ if (msg_verbose)
+ msg_info("%s: no limit found in the maps, using default virtual_mailbox_limit (%d)",
+ myname, var_virt_mailbox_limit);
+ /* There is no limit in the maps. Use default virtual_mailbox_limit. */
+ n = var_virt_mailbox_limit;
+ }
+
+ /* If there should is a quota on maildir generaly, check it before delivering the mail */
+ if (n != 0) {
+ set_eugid(usr_attr.uid, usr_attr.gid);
+ /* try to read the quota from maildirsize file. Returned values by read_maildirsize:
+ x < 0 = something failed
+ x >= 0 = reading successfully finished - sum si returned, so sum size of Maildir was 0 or more */
+ if (!var_virt_mailbox_limit_inbox && var_virt_maildir_extended && (read_mds = read_maildirsize(sizefilename, &saved_size, &saved_count)) >= 0) {
+ if (msg_verbose)
+ msg_info("%s: maildirsize used=%s sum=%ld count=%ld", myname, sizefilename, saved_size, saved_count);
+ } else {
+ if (msg_verbose)
+ msg_info("%s: We will recount the quota (var_virt_mailbox_limit = %d, var_virt_maildir_extended = %d, read_maildirsize = %d)",
+ myname, var_virt_mailbox_limit, var_virt_maildir_extended, read_mds);
+
+ /* sanity */
+ saved_size = 0;
+ saved_count = 0;
+
+ if (var_virt_mailbox_limit_inbox) {
+ /* Check Inbox only (new, cur and tmp dirs). */
+ saved_size = check_dir_size(newdir, &saved_count);
+ saved_size += check_dir_size(curdir, &saved_count);
+ saved_size += check_dir_size(tmpdir, &saved_count);
+ } else {
+ /* Check all boxes. */
+ saved_size = check_dir_size(usr_attr.mailbox, &saved_count);
+ }
+
+ set_eugid(var_owner_uid, var_owner_gid);
+ }
+ }
/*
* Create and write the file as the recipient, so that file quota work.
@@ -174,46 +663,288 @@ int deliver_maildir(LOCAL_STATE stat
* [...]
*/
set_eugid(usr_attr.uid, usr_attr.gid);
- vstring_sprintf(buf, "%lu.P%d.%s",
- (unsigned long) starttime.tv_sec, var_pid, get_hostname());
+ vstring_sprintf(buf, "%lu.P%d.%s", (unsigned long) starttime.tv_sec, var_pid, get_hostname());
tmpfile = concatenate(tmpdir, STR(buf), (char *) 0);
newfile = 0;
+ bkpnewfile = 0;
if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0
- && (errno != ENOENT
- || make_dirs(tmpdir, 0700) < 0
- || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
- dsb_simple(why, mbox_dsn(errno, "4.2.0"),
- "create maildir file %s: %m", tmpfile);
- } else if (fstat(vstream_fileno(dst), &st) < 0) {
-
- /*
- * Coverity 200604: file descriptor leak in code that never executes.
- * Code replaced by msg_fatal(), as it is not worthwhile to continue
- * after an impossible error condition.
- */
- msg_fatal("fstat %s: %m", tmpfile);
- } else {
- vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
- (unsigned long) starttime.tv_sec,
- (unsigned long) st.st_dev,
- (unsigned long) st.st_ino,
- (unsigned long) starttime.tv_usec,
- get_hostname());
- newfile = concatenate(newdir, STR(buf), (char *) 0);
- if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
- dst, copy_flags, "\n",
- why)) == 0) {
- if (sane_link(tmpfile, newfile) < 0
- && (errno != ENOENT
- || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
- || sane_link(tmpfile, newfile) < 0)) {
- dsb_simple(why, mbox_dsn(errno, "4.2.0"),
- "create maildir file %s: %m", newfile);
- mail_copy_status = MAIL_COPY_STAT_WRITE;
- }
- }
- if (unlink(tmpfile) < 0)
- msg_warn("remove %s: %m", tmpfile);
+ && (errno != ENOENT
+ || make_dirs(tmpdir, 0700) < 0
+ || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
+ dsb_simple(why, mbox_dsn(errno, "4.2.0"), "create maildir file %s: %m", tmpfile);
+ }
+ else if (fstat(vstream_fileno(dst), &st) < 0) {
+ /*
+ * Coverity 200604: file descriptor leak in code that never executes.
+ * Code replaced by msg_fatal(), as it is not worthwhile to continue
+ * after an impossible error condition.
+ */
+ msg_fatal("fstat %s: %m", tmpfile);
+ }
+ else {
+ vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
+ (unsigned long) starttime.tv_sec,
+ (unsigned long) st.st_dev,
+ (unsigned long) st.st_ino,
+ (unsigned long) starttime.tv_usec,
+ get_hostname());
+ newfile = concatenate(newdir, STR(buf), (char *) 0);
+ bkpnewfile = concatenate(STR(buf), (char *) 0); /* Will need it later, if we MOVE to other folders. */
+
+ if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), dst, copy_flags, "\n", why)) == 0) {
+ /*
+ * Add a ",S=<sizeoffile>" to the newly written file according to the
+ * Maildir++ specifications: http://www.inter7.com/courierimap/README.maildirquota.html
+ * This needs a stat(2) of the tempfile and modification of the
+ * name of the file.
+ */
+ if (stat(tmpfile, &mail_stat) == 0) {
+ if (n != 0) {
+ saved_size += (long) mail_stat.st_size;
+ saved_count++;
+ }
+ if (var_virt_maildir_extended) {
+ /* Append the size of the file to newfile. */
+ vstring_sprintf(buf, ",S=%ld", (long) mail_stat.st_size);
+ newfile = concatenate(newfile, STR(buf), (char *) 0);
+ bkpnewfile = concatenate(bkpnewfile, STR(buf), (char *) 0);
+ }
+ }
+
+ /*
+ * Now we have the maildir size in saved_size, compare it to the max
+ * quota value and eventually issue a message that we've overdrawn it.
+ */
+ if (saved_size > n) {
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+ if (((long) mail_stat.st_size > n) || (var_virt_overquota_bounce))
+ errno = EFBIG;
+ else
+ errno = EDQUOT;
+ }
+ else {
+ /* Maildirfilter code by rk@demiurg.net. */
+ if (var_virt_maildir_filter) {
+ if (msg_verbose)
+ msg_info("%s: loading DICT filters", myname);
+
+#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
+#define MAIL_COPY_STAT_REJECT (1<<3)
+#define MAIL_COPY_STAT_DISCARD (1<<4)
+
+ /* Read filters. */
+ filtername = concatenate("regexp:", usr_attr.mailbox, "maildirfilter", (char *) 0);
+ sqlmtime = sql2file(strchr(filtername, '/'), state.msg_attr.user);
+
+ /* Check if this filter is already registered as dictionary. */
+ if (msg_verbose)
+ msg_info("%s: checking DICT filters for %s", myname, filtername);
+
+ if ((FILTERS = dict_handle(filtername))) {
+ if (msg_verbose)
+ msg_info("%s: DICT filter found", myname);
+
+ /*
+ * If we have mtime in our DICT structure, check it against sqlmtime
+ * and reload the filters if they differ.
+ */
+ if (FILTERS->mtime > 0 && sqlmtime > 0 && FILTERS->mtime != sqlmtime) {
+ if (msg_verbose)
+ msg_info("%s: reloading DICT filters (dict_mtime=%ld != sql_mtime=%ld)",
+ myname, FILTERS->mtime, sqlmtime);
+
+ dict_unregister(filtername);
+ FILTERS = dict_open(filtername, O_RDONLY, DICT_FLAG_LOCK);
+ dict_register(filtername, FILTERS);
+ FILTERS->mtime = sqlmtime;
+ }
+ }
+ else {
+ if (sqlmtime > 0) {
+ /* Registering filter as new dictionary. */
+ if (msg_verbose)
+ msg_info("%s: loading DICT filters from %s (mtime=%ld)",
+ myname, filtername, sqlmtime);
+
+ FILTERS = dict_open(filtername, O_RDONLY, DICT_FLAG_LOCK);
+ dict_register(filtername, FILTERS);
+ FILTERS->mtime = sqlmtime;
+ }
+ }
+
+ if (FILTERS && (tmpfilter = vstream_fopen(tmpfile, O_RDONLY, 0))) {
+ fltstr = vstring_alloc(1024);
+ header = (char *) malloc(8192); /* !!!INSECURE!!! See 7168-hack below. */
+ header[0] = 0;
+ vstring_get_nonl_bound(fltstr, tmpfilter, 1023);
+ header = concatenate(header, STR(fltstr), (char *) 0);
+
+ while(!vstream_feof(tmpfilter) && fltstr->vbuf.data[0] && strlen(header) < 7168 ) {
+ vstring_get_nonl_bound(fltstr, tmpfilter, 1023);
+ /* Glue multiline headers, replacing leading TAB with space. */
+ if (msg_verbose)
+ msg_info("%s: fltstr value: %s", myname, STR(fltstr));
+
+ if (fltstr->vbuf.data[0] == ' ' || fltstr->vbuf.data[0] == '\t' ) {
+ if (fltstr->vbuf.data[0] == '\t')
+ fltstr->vbuf.data[0] = ' ';
+ header = concatenate(header, STR(fltstr), (char *) 0);
+ }
+ else {
+ header = concatenate(header, "\n", STR(fltstr), (char *) 0);
+ }
+ }
+
+ if (msg_verbose)
+ msg_info("%s: checking filter CMD for %s", myname, filtername);
+
+ /* Check whole header part with regexp maps. */
+ if ((value = dict_get(FILTERS, lowercase(header))) != 0) {
+ if (msg_verbose)
+ msg_info("%s: preparing filter CMD", myname);
+
+ cmd_text = value + strcspn(value, " \t");
+ cmd_len = cmd_text - value;
+ while (*cmd_text && ISSPACE(*cmd_text))
+ cmd_text++;
+
+ if (msg_verbose)
+ msg_info("%s: executing filter CMD", myname);
+
+ if (STREQUAL(value, "REJECT", cmd_len)) {
+ if (msg_verbose)
+ msg_info("%s: executing filter CMD REJECT", myname);
+
+ mail_copy_status = MAIL_COPY_STAT_REJECT;
+ vstring_sprintf(why->reason, "%s", cmd_text);
+ dsb_simple(why, "5.0.0", "User filter - REJECT");
+ }
+
+ if (STREQUAL(value, "DISCARD", cmd_len)) {
+ if (msg_verbose)
+ msg_info("%s: executing filter CMD DISCARD", myname);
+
+ mail_copy_status = MAIL_COPY_STAT_DISCARD;
+ vstring_sprintf(why->reason, "%s", cmd_text);
+ dsb_simple(why, "5.0.0", "User filter - DISCARD");
+ }
+
+ if (var_virt_maildir_extended) {
+ if (STREQUAL(value, "MOVE", cmd_len)) {
+ if (msg_verbose)
+ msg_info("%s: executing filter CMD MOVE", myname);
+
+ strcut((char *) cmd_text, " ");
+ strcut((char *) cmd_text, "\t");
+ strcut((char *) cmd_text, "/");
+ strcut((char *) cmd_text, "..");
+
+ if (*var_virt_maildir_suffix == 0) {
+ newfile = concatenate(usr_attr.mailbox, (char *) 0);
+ }
+ else {
+ newfile = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
+ }
+
+ if (cmd_text[0] != '.') {
+ newfile = concatenate(newfile, ".", (char *) 0);
+ }
+ newdir = concatenate(newfile, cmd_text, "/", "new/", (char *) 0);
+ tmpdir = concatenate(newfile, cmd_text, "/", "tmp/", (char *) 0);
+ curdir = concatenate(newfile, cmd_text, "/", "cur/", (char *) 0);
+ mdffilename = concatenate(newfile, cmd_text, "/", "maildirfolder", (char *) 0);
+ newfile = concatenate(newfile, cmd_text, "/", "new/", bkpnewfile, (char *) 0);
+ }
+ }
+
+ if (STREQUAL(value, "LOG", cmd_len) || STREQUAL(value, "WARN", cmd_len)) {
+ msg_warn("%s: header check warning: %s", myname, cmd_text);
+ }
+
+ if (STREQUAL(value, "INFO", cmd_len)) {
+ msg_info("%s: header check info: %s", myname, cmd_text);
+ }
+
+ if (msg_verbose)
+ msg_info("%s: exiting filter CMD", myname);
+ } /* End-Of-Check */
+
+ myfree(header);
+ vstring_free(fltstr);
+ vstream_fclose(tmpfilter);
+ }
+
+ myfree(filtername);
+ } /* End-Of-Maildirfilter */
+
+ /* Deliver to curdir. */
+ if (mail_copy_status == 0) {
+ if (sane_link(tmpfile, newfile) < 0
+ && (errno != ENOENT
+ || (make_dirs(curdir, 0700), make_dirs(newdir, 0700), make_dirs(tmpdir, 0700)) < 0
+ || sane_link(tmpfile, newfile) < 0)) {
+ dsb_simple(why, mbox_dsn(errno, "4.2.0"), "create maildir file %s: %m", newfile);
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+ }
+
+ if (var_virt_maildir_extended) {
+ time(&tm);
+
+ /* Check if the quota in the file is the same as the current one, if not, delete the file. */
+ sizefile = vstream_fopen(sizefilename, O_RDONLY, 0);
+ if (sizefile) {
+ filequota = vstring_alloc(128);
+ vstring_get_null_bound(filequota, sizefile, 127);
+ vstream_fclose(sizefile);
+ if (atol(vstring_export(filequota)) != n)
+ unlink(sizefilename);
+ }
+
+ /* Open maildirsize file to append this transaction. */
+ sizefile = vstream_fopen(sizefilename, O_WRONLY | O_APPEND, 0640);
+
+ /* If the open fails (maildirsize doesn't exist), or it's too large, or too old, overwrite it. */
+ if(!sizefile || (stat(sizefilename, &sizefile_stat) < 0) || (sizefile_stat.st_size > SIZEFILE_MAX) || (sizefile_stat.st_mtime + 15*60 < tm)) {
+ /* If the file exists, sizefile has been opened above, so close it first. */
+ if (sizefile) {
+ vstream_fclose(sizefile);
+ sizefile = vstream_fopen(sizefilename, O_WRONLY | O_TRUNC, 0640);
+ }
+ else {
+ sizefile = vstream_fopen(sizefilename, O_WRONLY | O_CREAT, 0640);
+ }
+
+ /* If the creation worked, write to the file, otherwise just give up. */
+ if (sizefile) {
+ vstream_fprintf(sizefile, "%ldS\n%ld %ld\n", n, saved_size, saved_count);
+ vstream_fclose(sizefile);
+ }
+ }
+ else {
+ /* We opened maildirsize, so let's just append this transaction and close it. */
+ vstream_fprintf(sizefile, "%ld 1\n", (long) mail_stat.st_size);
+ vstream_fclose(sizefile);
+ }
+
+ /*
+ * 1) mdffilename != 0, so the maildirfilter code went through the MOVE to subfolder rule.
+ * 2) stat() failed, maybe the file does not exist? Try to create it.
+ */
+ if (mdffilename && (stat(mdffilename, &mdffile_stat) < 0)) {
+ mdffile = vstream_fopen(mdffilename, O_WRONLY | O_CREAT, 0600);
+ if (mdffile) {
+ vstream_fclose(mdffile);
+ }
+ else {
+ msg_warn("Cannot create maildirfolder file '%s': %s", mdffilename, strerror(errno));
+ }
+ }
+ }
+ }
+ }
+ }
+ if (unlink(tmpfile) < 0)
+ msg_warn("remove %s: %m", tmpfile);
}
set_eugid(var_owner_uid, var_owner_gid);
@@ -223,31 +954,64 @@ int deliver_maildir(LOCAL_STATE stat
* location possibly under user control.
*/
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
- deliver_status = DEL_STAT_DEFER;
- } else if (mail_copy_status != 0) {
- if (errno == EACCES) {
- msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
- (long) usr_attr.uid, (long) usr_attr.gid,
- STR(why->reason));
- msg_warn("perhaps you need to create the maildirs in advance");
- }
- vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
- deliver_status =
- (STR(why->status)[0] == '4' ?
- defer_append : bounce_append)
- (BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr));
- } else {
- dsb_simple(why, "2.0.0", "delivered to maildir");
- deliver_status = sent(BOUNCE_FLAGS(state.request),
- SENT_ATTR(state.msg_attr));
+ deliver_status = DEL_STAT_DEFER;
+ }
+ else if (mail_copy_status != 0) {
+ if (errno == EACCES) {
+ msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
+ (long) usr_attr.uid, (long) usr_attr.gid, STR(why->reason));
+ msg_warn("perhaps you need to create the maildirs in advance");
+ }
+
+ /* Support per-recipient bounce messages. */
+ const char *limit_message;
+ int errnored = errno; /* Seems like mail_addr_find resets errno ... */
+
+ if (*var_virt_maildir_limit_message_maps != 0 && (limit_message = mail_addr_find(virtual_maildir_limit_message_maps, state.msg_attr.user, (char **) NULL)) != 0) {
+ errno = errnored;
+ if (errno == EFBIG) {
+ dsb_simple(why, "5.2.2", limit_message);
+ }
+ if (errno == EDQUOT) {
+ dsb_simple(why, "4.2.2", limit_message);
+ }
+ }
+ else {
+ errno = errnored;
+ if (errno == EFBIG) {
+ dsb_simple(why, "5.2.2", var_virt_maildir_limit_message);
+ }
+ if (errno == EDQUOT) {
+ dsb_simple(why, "4.2.2", var_virt_maildir_limit_message);
+ }
+ }
+
+ vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
+ deliver_status =
+ (STR(why->status)[0] == '4' ? defer_append : bounce_append)
+ (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
+ }
+ else {
+ dsb_simple(why, "2.0.0", "delivered to maildir");
+ deliver_status = sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr));
}
+
vstring_free(buf);
+
myfree(newdir);
myfree(tmpdir);
myfree(curdir);
+
+ if (sizefilename)
+ myfree(sizefilename);
+ if (mdffilename)
+ myfree(mdffilename);
+
myfree(tmpfile);
if (newfile)
- myfree(newfile);
+ myfree(newfile);
+ if (bkpnewfile)
+ myfree(bkpnewfile);
+
return (deliver_status);
}
Index: src/virtual/virtual.c
===================================================================
--- src/virtual/virtual.c.orig 2011-02-19 01:46:06.000000000 +0100
+++ src/virtual/virtual.c 2013-01-10 15:33:33.000000000 +0100
@@ -335,12 +335,30 @@ long var_virt_mailbox_limit;
char *var_mail_spool_dir; /* XXX dependency fix */
bool var_strict_mbox_owner;
+char *var_virt_mailbox_limit_maps;
+bool var_virt_mailbox_limit_inbox;
+bool var_virt_mailbox_limit_override;
+bool var_virt_maildir_extended;
+bool var_virt_overquota_bounce;
+char *var_virt_maildir_limit_message;
+char *var_virt_maildir_limit_message_maps;
+char *var_virt_maildir_suffix;
+bool var_virt_trash_count;
+char *var_virt_trash_name;
+bool var_virt_maildir_filter;
+char *var_virt_maildir_filter_maps;
+
+
/*
* Mappings.
*/
MAPS *virtual_mailbox_maps;
MAPS *virtual_uid_maps;
MAPS *virtual_gid_maps;
+MAPS *virtual_mailbox_limit_maps;
+MAPS *virtual_maildir_limit_message_maps;
+MAPS *virtual_maildir_filter_maps;
+
/*
* Bit masks.
@@ -450,15 +468,28 @@ static void post_init(char *unused_name,
*/
virtual_mailbox_maps =
maps_create(VAR_VIRT_MAILBOX_MAPS, var_virt_mailbox_maps,
- DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
+ DICT_FLAG_LOCK);
virtual_uid_maps =
maps_create(VAR_VIRT_UID_MAPS, var_virt_uid_maps,
- DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
+ DICT_FLAG_LOCK);
virtual_gid_maps =
maps_create(VAR_VIRT_GID_MAPS, var_virt_gid_maps,
- DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
+ DICT_FLAG_LOCK);
+
+ virtual_mailbox_limit_maps =
+ maps_create(VAR_VIRT_MAILBOX_LIMIT_MAPS, var_virt_mailbox_limit_maps,
+ DICT_FLAG_LOCK);
+
+ virtual_maildir_limit_message_maps =
+ maps_create(VAR_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS, var_virt_maildir_limit_message_maps,
+ DICT_FLAG_LOCK);
+
+ virtual_maildir_filter_maps =
+ maps_create(VAR_VIRT_MAILDIR_FILTER_MAPS, var_virt_maildir_filter_maps,
+ DICT_FLAG_LOCK);
+
virtual_mbox_lock_mask = mbox_lock_mask(var_virt_mailbox_lock);
}
@@ -503,17 +534,29 @@ int main(int argc, char **argv)
VAR_VIRT_MAILBOX_LIMIT, DEF_VIRT_MAILBOX_LIMIT, &var_virt_mailbox_limit, 0, 0,
0,
};
+ static const CONFIG_BOOL_TABLE bool_table[] = {
+ VAR_STRICT_MBOX_OWNER, DEF_STRICT_MBOX_OWNER, &var_strict_mbox_owner,
+ VAR_VIRT_MAILBOX_LIMIT_INBOX, DEF_VIRT_MAILBOX_LIMIT_INBOX, &var_virt_mailbox_limit_inbox,
+ VAR_VIRT_MAILBOX_LIMIT_OVERRIDE, DEF_VIRT_MAILBOX_LIMIT_OVERRIDE, &var_virt_mailbox_limit_override,
+ VAR_VIRT_MAILDIR_EXTENDED, DEF_VIRT_MAILDIR_EXTENDED, &var_virt_maildir_extended,
+ VAR_VIRT_OVERQUOTA_BOUNCE, DEF_VIRT_OVERQUOTA_BOUNCE, &var_virt_overquota_bounce,
+ VAR_VIRT_TRASH_COUNT, DEF_VIRT_TRASH_COUNT, &var_virt_trash_count,
+ VAR_VIRT_MAILDIR_FILTER, DEF_VIRT_MAILDIR_FILTER, &var_virt_maildir_filter,
+ 0,
+ };
static const CONFIG_STR_TABLE str_table[] = {
VAR_MAIL_SPOOL_DIR, DEF_MAIL_SPOOL_DIR, &var_mail_spool_dir, 0, 0,
VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
VAR_VIRT_UID_MAPS, DEF_VIRT_UID_MAPS, &var_virt_uid_maps, 0, 0,
VAR_VIRT_GID_MAPS, DEF_VIRT_GID_MAPS, &var_virt_gid_maps, 0, 0,
+ VAR_VIRT_MAILBOX_LIMIT_MAPS, DEF_VIRT_MAILBOX_LIMIT_MAPS, &var_virt_mailbox_limit_maps, 0, 0,
VAR_VIRT_MAILBOX_BASE, DEF_VIRT_MAILBOX_BASE, &var_virt_mailbox_base, 1, 0,
VAR_VIRT_MAILBOX_LOCK, DEF_VIRT_MAILBOX_LOCK, &var_virt_mailbox_lock, 1, 0,
- 0,
- };
- static const CONFIG_BOOL_TABLE bool_table[] = {
- VAR_STRICT_MBOX_OWNER, DEF_STRICT_MBOX_OWNER, &var_strict_mbox_owner,
+ VAR_VIRT_MAILDIR_LIMIT_MESSAGE, DEF_VIRT_MAILDIR_LIMIT_MESSAGE, &var_virt_maildir_limit_message, 1, 0,
+ VAR_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS, DEF_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS, &var_virt_maildir_limit_message_maps, 0, 0,
+ VAR_VIRT_MAILDIR_SUFFIX, DEF_VIRT_MAILDIR_SUFFIX, &var_virt_maildir_suffix, 0, 0,
+ VAR_VIRT_TRASH_NAME, DEF_VIRT_TRASH_NAME, &var_virt_trash_name, 0, 0,
+ VAR_VIRT_MAILDIR_FILTER_MAPS, DEF_VIRT_MAILDIR_FILTER_MAPS, &var_virt_maildir_filter_maps, 0, 0,
0,
};
@@ -530,6 +573,7 @@ int main(int argc, char **argv)
MAIL_SERVER_PRE_INIT, pre_init,
MAIL_SERVER_POST_INIT, post_init,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ MAIL_SERVER_BOOL_TABLE, bool_table,
MAIL_SERVER_PRIVILEGED,
0);
}
Index: src/virtual/virtual.h
===================================================================
--- src/virtual/virtual.h.orig
+++ src/virtual/virtual.h
@@ -34,6 +34,9 @@
extern MAPS *virtual_mailbox_maps;
extern MAPS *virtual_uid_maps;
extern MAPS *virtual_gid_maps;
+extern MAPS *virtual_mailbox_limit_maps;
+extern MAPS *virtual_maildir_limit_message_maps;
+extern MAPS *virtual_maildir_filter_maps;
/*
* User attributes: these control the privileges for delivery to external