postfix/postfix-vda-v13-3.10.0.patch
Marcus Rueckert 4a313bda65 Accepting request 373635 from home:varkoly:branches:server:mail
- update to 3.1.0 
- Since version 3.0 postfix supports dynamic loading of cdb:, ldap:, 
  lmdb:, mysql:, pcre:, pgsql:, sdbm:, and sqlite: database clients.
  Thats why the patches dynamic_maps.patch and dynamic_maps_pie.patch
  could be removed.
- Adapting all the patches to postfix 3.1.0
- The patch postfix-db6.diff is not more neccessary
- Backwards-compatibility safety net.
  With NEW Postfix installs, you MUST install a main.cf file with
  the setting "compatibility_level = 2". See conf/main.cf for an
  example.
  
  With UPGRADES of existing Postfix systems, you MUST NOT change the
  main.cf compatibility_level setting, nor add this setting if it
  does not exist.
  
  Several Postfix default settings have changed with Postfix 3.0.  To
  avoid massive frustration with existing Postfix installations,
  Postfix 3.0 comes with a safety net that forces Postfix to keep
  running with backwards-compatible main.cf and master.cf default
  settings. This safety net depends on the main.cf compatibility_level
  setting (default: 0). Details are in COMPATIBILITY_README.
- Major changes - tls
* [Feature 20160207] A new "postfix tls" command to quickly enable
  opportunistic TLS in the Postfix SMTP client or server, and to
  manage SMTP server keys and certificates, including certificate
  signing requests and TLSA DNS records for DANE.
* As of the middle of 2015, all supported Postfix releases no longer
  nable "export" grade ciphers for opportunistic TLS, and no longer
  use the deprecated SSLv2 and SSLv3 protocols for mandatory or

OBS-URL: https://build.opensuse.org/request/show/373635
OBS-URL: https://build.opensuse.org/package/show/server:mail/postfix?expand=0&rev=232
2016-03-18 17:07:45 +00:00

1361 lines
53 KiB
Diff

diff -uNr postfix-2.10.0.orig/README_FILES/VDA_README postfix-2.10.0/README_FILES/VDA_README
--- postfix-2.10.0.orig/README_FILES/VDA_README 1970-01-01 01:00:00.000000000 +0100
+++ postfix-2.10.0/README_FILES/VDA_README 2013-06-07 13:21:22.837143270 +0200
@@ -0,0 +1,10 @@
+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.
+
+
diff -uNr postfix-2.10.0.orig/src/global/mail_params.h postfix-2.10.0/src/global/mail_params.h
--- postfix-2.10.0.orig/src/global/mail_params.h 2013-02-03 19:22:21.000000000 +0100
+++ postfix-2.10.0/src/global/mail_params.h 2013-06-07 13:21:22.838143270 +0200
@@ -2367,6 +2367,54 @@
#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;
diff -uNr postfix-2.10.0.orig/src/util/file_limit.c postfix-2.10.0/src/util/file_limit.c
--- postfix-2.10.0.orig/src/util/file_limit.c 2003-10-22 20:48:36.000000000 +0200
+++ postfix-2.10.0/src/util/file_limit.c 2013-06-07 13:21:22.839143270 +0200
@@ -85,7 +85,11 @@
#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
diff -uNr postfix-2.10.0.orig/src/virtual/mailbox.c postfix-2.10.0/src/virtual/mailbox.c
--- postfix-2.10.0.orig/src/virtual/mailbox.c 2011-12-24 03:13:32.000000000 +0100
+++ postfix-2.10.0/src/virtual/mailbox.c 2013-06-07 13:23:03.044139705 +0200
@@ -52,6 +52,7 @@
#include <mymalloc.h>
#include <stringops.h>
#include <set_eugid.h>
+#include <iostuff.h>
/* Global library. */
@@ -70,6 +71,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)
@@ -80,7 +145,6 @@
int mail_copy_status;
int deliver_status;
int copy_flags;
- long end;
struct stat st;
/*
@@ -132,7 +196,7 @@
msg_warn("specify \"%s = no\" to ignore mailbox ownership mismatch",
VAR_STRICT_MBOX_OWNER);
} else {
- end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
+ vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
copy_flags, "\n", why);
}
@@ -213,62 +277,72 @@
* 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.
diff -uNr postfix-2.10.0.orig/src/virtual/maildir.c postfix-2.10.0/src/virtual/maildir.c
--- postfix-2.10.0.orig/src/virtual/maildir.c 2012-01-25 01:41:08.000000000 +0100
+++ postfix-2.10.0/src/virtual/maildir.c 2013-06-07 13:21:22.840143270 +0200
@@ -64,28 +64,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;
+ }
+
+ 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);
-int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
+ 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=%li 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);
@@ -94,15 +486,14 @@
*/
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)));
}
/*
@@ -110,18 +501,116 @@
* 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 (%ld)",
+ 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 (%ld)",
+ 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 = %ld, 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.
* Create any missing directories on the fly. The file name is chosen
@@ -175,46 +664,288 @@
* [...]
*/
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);
@@ -224,31 +955,64 @@
* 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, NULL);
+ }
+ if (errno == EDQUOT) {
+ dsb_simple(why, "4.2.2", limit_message, NULL);
+ }
+ }
+ else {
+ errno = errnored;
+ if (errno == EFBIG) {
+ dsb_simple(why, "5.2.2", var_virt_maildir_limit_message, NULL);
+ }
+ if (errno == EDQUOT) {
+ dsb_simple(why, "4.2.2", var_virt_maildir_limit_message, NULL);
+ }
+ }
+
+ 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);
}
diff -uNr postfix-2.10.0.orig/src/virtual/virtual.h postfix-2.10.0/src/virtual/virtual.h
--- postfix-2.10.0.orig/src/virtual/virtual.h 2006-01-08 00:59:47.000000000 +0100
+++ postfix-2.10.0/src/virtual/virtual.h 2013-06-07 13:21:22.841143270 +0200
@@ -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
--- postfix-3.1.0/src/virtual/virtual.c.orig 2016-03-16 09:58:37.790856521 +0100
+++ postfix-3.1.0/src/virtual/virtual.c 2016-03-16 10:04:44.267207460 +0100
@@ -347,12 +347,28 @@
bool var_strict_mbox_owner;
char *var_virt_dsn_filter;
+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.
@@ -475,6 +491,19 @@
DICT_FLAG_LOCK | DICT_FLAG_PARANOID
| DICT_FLAG_UTF8_REQUEST);
+ virtual_mailbox_limit_maps =
+ maps_create(VAR_VIRT_MAILBOX_LIMIT_MAPS, var_virt_mailbox_limit_maps,
+ DICT_FLAG_LOCK | DICT_FLAG_UTF8_REQUEST );
+
+ virtual_maildir_limit_message_maps =
+ maps_create(VAR_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS, var_virt_maildir_limit_message_maps,
+ DICT_FLAG_LOCK | DICT_FLAG_UTF8_REQUEST );
+
+ virtual_maildir_filter_maps =
+ maps_create(VAR_VIRT_MAILDIR_FILTER_MAPS, var_virt_maildir_filter_maps,
+ DICT_FLAG_LOCK | DICT_FLAG_UTF8_REQUEST );
+
+
virtual_mbox_lock_mask = mbox_lock_mask(var_virt_mailbox_lock);
}
@@ -526,6 +555,12 @@
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,
VAR_VIRT_DSN_FILTER, DEF_VIRT_DSN_FILTER, &var_virt_dsn_filter, 0, 0,
+ VAR_VIRT_MAILBOX_LIMIT_MAPS, DEF_VIRT_MAILBOX_LIMIT_MAPS, &var_virt_mailbox_limit_maps, 0, 0,
+ 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,
};
static const CONFIG_BOOL_TABLE bool_table[] = {