]> git.pld-linux.org Git - packages/postfix.git/blame - postfix-vda.patch
- up to 3.5.0
[packages/postfix.git] / postfix-vda.patch
CommitLineData
17cc8889
AO
1diff -ruNp postfix-2.11.10.orig/README_FILES/VDA_README postfix-2.11.10/README_FILES/VDA_README
2--- postfix-2.11.10.orig/README_FILES/VDA_README 1970-01-01 01:00:00.000000000 +0100
3+++ postfix-2.11.10/README_FILES/VDA_README 2017-07-02 00:13:25.773285426 +0200
4@@ -0,0 +1,10 @@
5+Postfix VDA patch for maildir++ quota support by
6+ Anderson Nadal <andernadal@gmail.com>
7+ Tomas Macek <maca02@atlas.cz>
8+ Lucca Longinotti
9+
10+See VDA patch official website http://vda.sf.net for instructions
11+howto patch the Postfix's sourcetree and configure the options
12+provided by this patch.
13+
14+
15diff -ruNp postfix-2.11.10.orig/src/global/mail_params.h postfix-2.11.10/src/global/mail_params.h
16--- postfix-2.11.10.orig/src/global/mail_params.h 2015-07-20 00:39:31.000000000 +0200
17+++ postfix-2.11.10/src/global/mail_params.h 2017-07-02 00:13:25.775285421 +0200
18@@ -2413,6 +2413,54 @@ extern char *var_virt_uid_maps;
19 #define DEF_VIRT_GID_MAPS ""
20 extern char *var_virt_gid_maps;
21
22+#define VAR_VIRT_MAILBOX_LIMIT_MAPS "virtual_mailbox_limit_maps"
23+#define DEF_VIRT_MAILBOX_LIMIT_MAPS ""
24+extern char *var_virt_mailbox_limit_maps;
25+
26+#define VAR_VIRT_MAILBOX_LIMIT_INBOX "virtual_mailbox_limit_inbox"
27+#define DEF_VIRT_MAILBOX_LIMIT_INBOX 0
28+extern bool var_virt_mailbox_limit_inbox;
29+
30+#define VAR_VIRT_MAILBOX_LIMIT_OVERRIDE "virtual_mailbox_limit_override"
31+#define DEF_VIRT_MAILBOX_LIMIT_OVERRIDE 0
32+extern bool var_virt_mailbox_limit_override;
33+
34+#define VAR_VIRT_MAILDIR_EXTENDED "virtual_maildir_extended"
35+#define DEF_VIRT_MAILDIR_EXTENDED 0
36+extern bool var_virt_maildir_extended;
37+
38+#define VAR_VIRT_OVERQUOTA_BOUNCE "virtual_overquota_bounce"
39+#define DEF_VIRT_OVERQUOTA_BOUNCE 0
40+extern bool var_virt_overquota_bounce;
41+
42+#define VAR_VIRT_MAILDIR_LIMIT_MESSAGE "virtual_maildir_limit_message"
43+#define DEF_VIRT_MAILDIR_LIMIT_MESSAGE "Sorry, the user's maildir has overdrawn his diskspace quota, please try again later."
44+extern char *var_virt_maildir_limit_message;
45+
46+#define VAR_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS "virtual_maildir_limit_message_maps"
47+#define DEF_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS ""
48+extern char *var_virt_maildir_limit_message_maps;
49+
50+#define VAR_VIRT_MAILDIR_SUFFIX "virtual_maildir_suffix"
51+#define DEF_VIRT_MAILDIR_SUFFIX ""
52+extern char *var_virt_maildir_suffix;
53+
54+#define VAR_VIRT_TRASH_COUNT "virtual_trash_count"
55+#define DEF_VIRT_TRASH_COUNT 0
56+extern bool var_virt_trash_count;
57+
58+#define VAR_VIRT_TRASH_NAME "virtual_trash_name"
59+#define DEF_VIRT_TRASH_NAME ".Trash"
60+extern char *var_virt_trash_name;
61+
62+#define VAR_VIRT_MAILDIR_FILTER "virtual_maildir_filter"
63+#define DEF_VIRT_MAILDIR_FILTER 0
64+extern bool var_virt_maildir_filter;
65+
66+#define VAR_VIRT_MAILDIR_FILTER_MAPS "virtual_maildir_filter_maps"
67+#define DEF_VIRT_MAILDIR_FILTER_MAPS ""
68+extern char *var_virt_maildir_filter_maps;
69+
70 #define VAR_VIRT_MINUID "virtual_minimum_uid"
71 #define DEF_VIRT_MINUID 100
72 extern int var_virt_minimum_uid;
73diff -ruNp postfix-2.11.10.orig/src/util/file_limit.c postfix-2.11.10/src/util/file_limit.c
74--- postfix-2.11.10.orig/src/util/file_limit.c 2003-10-22 20:48:36.000000000 +0200
75+++ postfix-2.11.10/src/util/file_limit.c 2017-07-02 00:13:25.775285421 +0200
76@@ -85,7 +85,11 @@ void set_file_limit(off_t limit)
77 #else
78 struct rlimit rlim;
79
80- rlim.rlim_cur = rlim.rlim_max = limit;
81+ /* rlim_max can only be changed by root. */
82+ if (getrlimit(RLIMIT_FSIZE, &rlim) < 0)
83+ msg_fatal("getrlimit: %m");
84+ rlim.rlim_cur = limit;
85+
86 if (setrlimit(RLIMIT_FSIZE, &rlim) < 0)
87 msg_fatal("setrlimit: %m");
88 #ifdef SIGXFSZ
89diff -ruNp postfix-2.11.10.orig/src/virtual/mailbox.c postfix-2.11.10/src/virtual/mailbox.c
90--- postfix-2.11.10.orig/src/virtual/mailbox.c 2016-08-22 23:24:31.000000000 +0200
91+++ postfix-2.11.10/src/virtual/mailbox.c 2017-07-02 00:13:25.776285418 +0200
92@@ -52,6 +52,7 @@
93 #include <mymalloc.h>
94 #include <stringops.h>
95 #include <set_eugid.h>
96+#include <iostuff.h>
97
98 /* Global library. */
99
100@@ -70,6 +71,70 @@
101 #define YES 1
102 #define NO 0
103
104+/* change_mailbox_limit - change limit for mailbox file */
105+static int change_mailbox_limit(LOCAL_STATE state, USER_ATTR usr_attr)
106+{
107+ char *myname = "change_mailbox_limit";
108+ const char *limit_res;
109+ long n = 0;
110+ int status = NO;
111+
112+ /*
113+ * Look up the virtual mailbox limit size for this user.
114+ * Fall back to virtual_mailbox_limit in case lookup failed.
115+ * If virtual mailbox limit size is negative, fall back to virtual_mailbox_limit.
116+ * If it's 0, set the mailbox limit to 0, which means unlimited.
117+ * If it's more than 0 (positive int), check if the value is smaller than the maximum message size,
118+ * if it is and the virtual mailbox limit can't be overridden, fall back to virtual_mailbox_limit and
119+ * warn the user, else use the value directly as the mailbox limit.
120+ */
121+ if (*var_virt_mailbox_limit_maps != 0 && (limit_res = mail_addr_find(virtual_mailbox_limit_maps, state.msg_attr.user, (char **) NULL)) != 0) {
122+ n = atol(limit_res);
123+ if (n > 0) {
124+ if ((n < var_message_limit) && (!var_virt_mailbox_limit_override)) {
125+ set_file_limit(var_virt_mailbox_limit);
126+ status = NO;
127+
128+ msg_warn("%s: recipient %s - virtual mailbox limit is "
129+ "smaller than %s in %s - falling back to %s",
130+ myname,
131+ state.msg_attr.user,
132+ VAR_MESSAGE_LIMIT,
133+ virtual_mailbox_limit_maps->title,
134+ VAR_VIRT_MAILBOX_LIMIT);
135+ }
136+ else {
137+ set_file_limit((off_t) n);
138+ status = YES;
139+
140+ if (msg_verbose)
141+ msg_info("%s: set virtual mailbox limit size for %s to %ld",
142+ myname, usr_attr.mailbox, n);
143+ }
144+ }
145+ else if (n == 0) {
146+ set_file_limit(OFF_T_MAX);
147+ status = YES;
148+
149+ if (msg_verbose)
150+ msg_info("%s: set virtual mailbox limit size for %s to %ld",
151+ myname, usr_attr.mailbox, OFF_T_MAX);
152+ }
153+ else {
154+ /* Invalid limit size (negative). Use default virtual_mailbox_limit. */
155+ set_file_limit(var_virt_mailbox_limit);
156+ status = NO;
157+ }
158+ }
159+ else {
160+ /* There is no limit in the maps. Use default virtual_mailbox_limit. */
161+ set_file_limit(var_virt_mailbox_limit);
162+ status = NO;
163+ }
164+
165+ return(status);
166+}
167+
168 /* deliver_mailbox_file - deliver to recipient mailbox */
169
170 static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
171@@ -214,62 +279,72 @@ int deliver_mailbox(LOCAL_STATE stat
172 * Look up the mailbox owner rights. Defer in case of trouble.
173 */
174 uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user,
175- IGNORE_EXTENSION);
176- if (uid_res == 0) {
177- msg_warn("recipient %s: not found in %s",
178- state.msg_attr.user, virtual_uid_maps->title);
179- dsb_simple(why, "4.3.5", "mail system configuration error");
180- *statusp = defer_append(BOUNCE_FLAGS(state.request),
181- BOUNCE_ATTR(state.msg_attr));
182- RETURN(YES);
183+ IGNORE_EXTENSION);
184+
185+ if ((uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user, (char **) 0)) == 0) {
186+ if ((uid_res = maps_find(virtual_uid_maps, strchr(state.msg_attr.user, '@'), DICT_FLAG_FIXED)) == 0) {
187+ msg_warn("recipient %s: not found in %s", state.msg_attr.user, virtual_uid_maps->title);
188+ dsb_simple(why, "4.3.5", "mail system configuration error");
189+ *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
190+ RETURN(YES);
191+ }
192 }
193+
194 if ((n = atol(uid_res)) < var_virt_minimum_uid) {
195- msg_warn("recipient %s: bad uid %s in %s",
196- state.msg_attr.user, uid_res, virtual_uid_maps->title);
197- dsb_simple(why, "4.3.5", "mail system configuration error");
198- *statusp = defer_append(BOUNCE_FLAGS(state.request),
199- BOUNCE_ATTR(state.msg_attr));
200- RETURN(YES);
201+ msg_warn("recipient %s: bad uid %s in %s", state.msg_attr.user, uid_res, virtual_uid_maps->title);
202+ dsb_simple(why, "4.3.5", "mail system configuration error");
203+ *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
204+ RETURN(YES);
205 }
206+
207 usr_attr.uid = (uid_t) n;
208
209 /*
210 * Look up the mailbox group rights. Defer in case of trouble.
211 */
212 gid_res = mail_addr_find(virtual_gid_maps, state.msg_attr.user,
213- IGNORE_EXTENSION);
214- if (gid_res == 0) {
215- msg_warn("recipient %s: not found in %s",
216- state.msg_attr.user, virtual_gid_maps->title);
217- dsb_simple(why, "4.3.5", "mail system configuration error");
218- *statusp = defer_append(BOUNCE_FLAGS(state.request),
219- BOUNCE_ATTR(state.msg_attr));
220- RETURN(YES);
221+ IGNORE_EXTENSION);
222+
223+ if ((gid_res = mail_addr_find(virtual_gid_maps, state.msg_attr.user, (char **) 0)) == 0) {
224+ if ((gid_res = maps_find(virtual_gid_maps, strchr(state.msg_attr.user, '@'), DICT_FLAG_FIXED)) == 0) {
225+ msg_warn("recipient %s: not found in %s", state.msg_attr.user, virtual_gid_maps->title);
226+ dsb_simple(why, "4.3.5", "mail system configuration error");
227+ *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
228+ RETURN(YES);
229+ }
230 }
231+
232 if ((n = atol(gid_res)) <= 0) {
233- msg_warn("recipient %s: bad gid %s in %s",
234- state.msg_attr.user, gid_res, virtual_gid_maps->title);
235- dsb_simple(why, "4.3.5", "mail system configuration error");
236- *statusp = defer_append(BOUNCE_FLAGS(state.request),
237- BOUNCE_ATTR(state.msg_attr));
238- RETURN(YES);
239+ msg_warn("recipient %s: bad gid %s in %s", state.msg_attr.user, gid_res, virtual_gid_maps->title);
240+ dsb_simple(why, "4.3.5", "mail system configuration error");
241+ *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
242+ RETURN(YES);
243 }
244+
245 usr_attr.gid = (gid_t) n;
246
247 if (msg_verbose)
248- msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
249- myname, state.level, usr_attr.mailbox,
250- (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
251+ msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
252+ myname, state.level, usr_attr.mailbox,
253+ (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
254
255 /*
256 * Deliver to mailbox or to maildir.
257 */
258 #define LAST_CHAR(s) (s[strlen(s) - 1])
259
260- if (LAST_CHAR(usr_attr.mailbox) == '/')
261- *statusp = deliver_maildir(state, usr_attr);
262- else
263- *statusp = deliver_mailbox_file(state, usr_attr);
264+ if (LAST_CHAR(usr_attr.mailbox) == '/') {
265+ *statusp = deliver_maildir(state, usr_attr);
266+ }
267+ else {
268+ int changed_limit;
269+
270+ changed_limit = change_mailbox_limit(state, usr_attr);
271+ *statusp = deliver_mailbox_file(state, usr_attr);
272+
273+ if (changed_limit)
274+ set_file_limit(var_virt_mailbox_limit);
275+ }
276
277 /*
278 * Cleanup.
279diff -ruNp postfix-2.11.10.orig/src/virtual/maildir.c postfix-2.11.10/src/virtual/maildir.c
280--- postfix-2.11.10.orig/src/virtual/maildir.c 2012-01-25 01:41:08.000000000 +0100
281+++ postfix-2.11.10/src/virtual/maildir.c 2017-07-02 00:13:25.779285410 +0200
282@@ -64,28 +64,420 @@
283 #include <mbox_open.h>
284 #include <dsn_util.h>
285
286+/* Patch library. */
287+
288+#include <sys/types.h> /* opendir(3), stat(2) */
289+#include <sys/stat.h> /* stat(2) */
290+#include <dirent.h> /* opendir(3) */
291+#include <unistd.h> /* stat(2) */
292+#include <stdlib.h> /* atol(3) */
293+#include <string.h> /* strrchr(3) */
294+#include <vstring_vstream.h>
295+#include <dict.h>
296+#include <dict_regexp.h>
297+#include <ctype.h>
298+#include <stdio.h>
299+#include <sys_defs.h>
300+#include <mail_addr_find.h>
301+
302 /* Application-specific. */
303
304 #include "virtual.h"
305
306-/* deliver_maildir - delivery to maildir-style mailbox */
307+/* Maildirsize maximal size. */
308+
309+#define SIZEFILE_MAX 5120
310+
311+/*
312+ * Chris Stratford <chriss@pipex.net>
313+ * Read the maildirsize file to get quota info.
314+ *
315+ * Arguments:
316+ * dirname: the maildir
317+ * countptr: number of messages
318+ *
319+ * Returns the size of all mails as read from maildirsize,
320+ * zero if it couldn't read the file.
321+ */
322+static long read_maildirsize(char *filename, long *sumptr, long *countptr)
323+{
324+ char *myname = "read_maildirsize";
325+ struct stat statbuf;
326+ VSTREAM *sizefile;
327+ char *p;
328+ int len, first;
329+ long sum = 0, count = 0, ret_value = -1;
330+
331+ if (msg_verbose)
332+ msg_info("%s: we will use sizefile = '%s'", myname, filename);
333+
334+ sizefile = vstream_fopen(filename, O_RDONLY, 0);
335+ if (!sizefile) {
336+ if (msg_verbose)
337+ msg_info("%s: cannot open %s: %m (maybe file does not exist)", myname, filename);
338+
339+ return -1;
340+ } else if (stat(filename, &statbuf) < 0 || statbuf.st_size > SIZEFILE_MAX) {
341+ if (sizefile) {
342+ vstream_fclose(sizefile);
343+ unlink(filename);
344+ }
345+
346+ if (msg_verbose)
347+ msg_info("%s: stat() returned < 0 or filesize > SIZEFILE_MAX (filename = %s, filesize = %ld)", myname, filename, statbuf.st_size);
348+
349+ return -1;
350+ }
351+
352+ VSTRING *sizebuf = vstring_alloc(SIZEFILE_MAX);
353+ len = vstream_fread(sizefile, STR(sizebuf), SIZEFILE_MAX);
354+
355+ p = STR(sizebuf);
356+ *(p + len) = '\0';
357+ first = 1;
358+
359+ while (*p) {
360+ long n = 0, c = 0;
361+ char *q = p;
362+
363+ while (*p) {
364+ if (*p++ == '\n') {
365+ p[-1] = 0;
366+ break;
367+ }
368+ }
369+
370+ if (first) {
371+ first = 0;
372+ continue;
373+ }
374+
375+ if (sscanf(q, "%ld %ld", &n, &c) == 2) {
376+ sum += n;
377+ count += c;
378+ /* if (msg_verbose)
379+ msg_info("%s: we read line '%s', totals: sum = %ld, count = %ld", myname, q, sum, count); */
380+ }
381+ else {
382+ vstream_fclose(sizefile);
383+ unlink(filename);
384+ msg_warn("%s: invalid line '%s' found in %s, removing maildirsize file", myname, q, filename);
385+ vstring_free(sizebuf);
386+
387+ return -1;
388+ }
389+ }
390+
391+ *countptr = count;
392+ *sumptr = sum;
393+
394+ if (sum < 0 || count < 0 || (sum == 0 && count != 0) || (sum != 0 && count == 0)) {
395+ if (msg_verbose) {
396+ 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);
397+ }
398+
399+ unlink(filename);
400+ ret_value = -1;
401+ } else {
402+ if (msg_verbose)
403+ msg_info("%s: we will return Maildir size = %ld, count = %ld", myname, *sumptr, *countptr);
404+
405+ ret_value = sum;
406+ }
407+
408+ vstream_fclose(sizefile);
409+ vstring_free(sizebuf);
410+
411+ return ret_value;
412+}
413+
414+/*
415+ * Gives the size of the file according to the Maildir++ extension
416+ * present in the filename (code taken from courier-imap).
417+ *
418+ * Arguments:
419+ * n: filename
420+ *
421+ * Returns the size given in ",S=<size>" in the filename,
422+ * zero if it cannot find ",S=<size>" in the filename.
423+ */
424+static long maildir_parsequota(const char *n)
425+{
426+ const char *o;
427+ int yes = 0;
428+
429+ if ((o = strrchr(n, '/')) == 0)
430+ o = n;
431+
432+ for (; *o; o++) {
433+ if (*o == ':')
434+ break;
435+ }
436+
437+ for (; o >= n; --o) {
438+ if (*o == '/')
439+ break;
440+
441+ if (*o == ',' && o[1] == 'S' && o[2] == '=') {
442+ yes = 1;
443+ o += 3;
444+ break;
445+ }
446+ }
447+
448+ if (yes) {
449+ long s = 0;
450+
451+ while (*o >= '0' && *o <= '9')
452+ s = s*10 + (*o++ - '0');
453+
454+ return s;
455+ }
456+
457+ return 0;
458+}
459+
460+/*
461+ * Computes quota usage for a directory (taken from exim).
462+ *
463+ * This function is called to determine the exact quota usage of a virtual
464+ * maildir box. To achieve maximum possible speed while doing this, it takes
465+ * advantage of the maildirsize file and the Maildir++ extensions to filenames,
466+ * when applicable and configured to be used. In all other cases it simply
467+ * stats all the files as needed to get the size information.
468+ *
469+ * Arguments:
470+ * dirname: the name of the directory
471+ * countptr: where to add the file count (because this function recurses)
472+ *
473+ * Returns the sum of the sizes of all measurable files,
474+ * zero if the directory could not be opened.
475+ */
476+static long check_dir_size(char *dirname, long *countptr)
477+{
478+ char *myname = "check_dir_size";
479+ DIR *dir;
480+ long sum = 0;
481+ struct dirent *ent;
482+ struct stat statbuf;
483+
484+ dir = opendir(dirname);
485+ if (dir == NULL) {
486+ if (make_dirs(dirname, 0700) == 0) { /* Try to create the dirs. */
487+ dir = opendir(dirname); /* Reopen the dir. */
488+ if (dir == NULL) {
489+ msg_warn("%s: cannot reopen directory: %s", myname, dirname);
490+ return 0;
491+ }
492+ }
493+ else {
494+ msg_warn("%s: cannot open directory: %s", myname, dirname);
495+ return 0;
496+ }
497+ }
498+
499+ while ((ent = readdir(dir)) != NULL) {
500+ char *name = ent->d_name;
501+ long tmpsum = 0;
502+ VSTRING *buffer;
503+
504+ /* do not count dot a double-dot dirs */
505+ if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
506+ continue;
507+ /* do not count if this is the trash subdir and if we should NOT count it */
508+ else if (var_virt_trash_count == 0 && strcmp(name, var_virt_trash_name) == 0)
509+ continue;
510+
511+ /*
512+ * Here comes the real logic behind this function.
513+ * Optimized to be the most efficient possible,
514+ * depending on the settings given.
515+ * See above for a more detailed description.
516+ */
517+ if (var_virt_mailbox_limit_inbox) {
518+ if (var_virt_maildir_extended && (tmpsum = maildir_parsequota(name))) {
519+ sum += tmpsum;
520+ (*countptr)++;
521+ }
522+ else {
523+ buffer = vstring_alloc(1024);
524+ vstring_sprintf(buffer, "%s/%s", dirname, name);
525+
526+ if (stat(STR(buffer), &statbuf) < 0) {
527+ vstring_free(buffer);
528+ continue;
529+ }
530+ if ((statbuf.st_mode & S_IFREG) != 0) {
531+ sum += (long) statbuf.st_size;
532+ (*countptr)++;
533+ }
534+
535+ vstring_free(buffer);
536+ }
537+ }
538+ else {
539+ buffer = vstring_alloc(1024);
540+ vstring_sprintf(buffer, "%s/%s", dirname, name);
541+
542+ if (stat(STR(buffer), &statbuf) < 0) {
543+ vstring_free(buffer);
544+ continue;
545+ }
546+ if ((statbuf.st_mode & S_IFREG) != 0) {
547+ if (strcmp(dirname + strlen(dirname) - 3, "new") == 0 || strcmp(dirname + strlen(dirname) - 3, "cur") == 0 || strcmp(dirname + strlen(dirname) - 3, "tmp") == 0) {
548+ sum += (long) statbuf.st_size;
549+ (*countptr)++;
550+ }
551+ }
552+ else if ((statbuf.st_mode & S_IFDIR) != 0) {
553+ sum += check_dir_size(STR(buffer), countptr);
554+ }
555+
556+ vstring_free(buffer);
557+ }
558+ }
559+ closedir(dir);
560
561-int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
562+ if (msg_verbose)
563+ msg_info("%s: full scan done: dir=%s sum=%ld count=%ld", myname, dirname, sum, *countptr);
564+
565+ return sum;
566+}
567+
568+/* Cut all occurrences of pattern from string. */
569+static char *strcut(char *str, const char *pat)
570+{
571+ char *ptr, *loc, *ret;
572+ ret = str;
573+ loc = str;
574+
575+ /* No match, return original string. */
576+ if (!strstr(loc, pat))
577+ return(str);
578+
579+ while (*loc && (ptr = strstr(loc, pat))) {
580+ while (loc < ptr)
581+ *str++ = *loc++;
582+ loc += strlen(pat);
583+ }
584+
585+ while (*loc)
586+ *str++ = *loc++;
587+
588+ *str = 0;
589+
590+ return(ret);
591+}
592+
593+/* Check if maildirfilter file is up-to-date compared to SQL, (re)write it if not. */
594+static long sql2file(char *filename, char *user)
595+{
596+ char *myname = "sql2file";
597+ char *filter_sqlres;
598+ char filter_fileres[128];
599+ long sqlmtime = 0, filemtime = 0, retval = 0;
600+ int filterfile, size_sqlres, i;
601+ struct stat statbuf;
602+
603+ if (*var_virt_maildir_filter_maps != 0) {
604+ filter_sqlres = (char *) mymalloc(16000);
605+ filter_sqlres = (char *) mail_addr_find(virtual_maildir_filter_maps, user, (char **) 0);
606+
607+ if (filter_sqlres) {
608+ strcut(filter_sqlres, "\r");
609+ if (filter_sqlres[0] == '#' && filter_sqlres[1] == ' ' && filter_sqlres[2] == 'M') {
610+ size_sqlres = strlen(filter_sqlres);
611+
612+ for (i = 4; i <= size_sqlres; i++) {
613+ if(filter_sqlres[i] == '/' && filter_sqlres[i+1] == '^') {
614+ filter_sqlres[i-1] = '\n';
615+ }
616+ }
617+
618+ filter_sqlres[(size_sqlres+1)] = '\0';
619+
620+ sqlmtime = atol(filter_sqlres+3);
621+ retval = sqlmtime;
622+
623+ filterfile = open(filename, O_RDONLY, 0);
624+ if (filterfile) {
625+ read(filterfile, (void *) filter_fileres, 127);
626+ close(filterfile);
627+
628+ filemtime = atol(filter_fileres+3);
629+ }
630+
631+ if (msg_verbose)
632+ msg_info("%s: filter data: sql_size=%li sql_mtime=%ld file_mtime=%ld", myname, strlen(filter_sqlres), sqlmtime, filemtime);
633+ }
634+ if (sqlmtime != filemtime && sqlmtime != 0) {
635+ if ((filterfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0640))) {
636+ if (msg_verbose)
637+ msg_info("%s: updating filter file: %s", myname, filename);
638+ write(filterfile, filter_sqlres, strlen(filter_sqlres));
639+ close(filterfile);
640+ }
641+ else {
642+ msg_warn("%s: can't create filter file: %s", myname, filename);
643+ retval = 0;
644+ }
645+ }
646+ }
647+ }
648+ else {
649+ if (stat(filename, &statbuf) == 0)
650+ retval = (long) statbuf.st_mtime;
651+ if (msg_verbose)
652+ msg_info("%s: processing filter file: file_mtime=%ld", myname, retval);
653+ }
654+
655+ return retval;
656+}
657+
658+/* deliver_maildir - delivery to maildir-style mailbox */
659+int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
660 {
661 const char *myname = "deliver_maildir";
662- char *newdir;
663- char *tmpdir;
664- char *curdir;
665- char *tmpfile;
666- char *newfile;
667+ char *newdir;
668+ char *tmpdir;
669+ char *curdir;
670+ char *newfile;
671+ char *tmpfile;
672 DSN_BUF *why = state.msg_attr.why;
673 VSTRING *buf;
674 VSTREAM *dst;
675- int mail_copy_status;
676- int deliver_status;
677- int copy_flags;
678- struct stat st;
679- struct timeval starttime;
680+ int mail_copy_status;
681+ int deliver_status;
682+ int copy_flags;
683+ struct stat st;
684+ struct timeval starttime;
685+
686+ /* Maildir Quota. */
687+ const char *limit_res; /* Limit from map. */
688+ char *sizefilename = (char *) 0; /* Maildirsize file name. */
689+ VSTRING *filequota; /* Quota setting from the maildirsize file. */
690+ VSTREAM *sizefile; /* Maildirsize file handle. */
691+ long n = 0; /* Limit in long integer format. */
692+ long saved_count = 0; /* The total number of files. */
693+ long saved_size = 0; /* The total quota of all files. */
694+ struct stat mail_stat; /* To check the size of the mail to be written. */
695+ struct stat sizefile_stat; /* To check the size of the maildirsize file. */
696+ time_t tm; /* To check the age of the maildirsize file. */
697+
698+ /* Maildir Filters. */
699+ const char *value, *cmd_text; /* Filter values. */
700+ char *filtername;
701+ char *header;
702+ char *bkpnewfile;
703+ char *mdffilename = (char *) 0; /* Maildirfolder file name. */
704+ VSTRING *fltstr;
705+ VSTREAM *tmpfilter;
706+ VSTREAM *mdffile; /* Maildirfolder file handle. */
707+ DICT *FILTERS;
708+ long sqlmtime; /* Latest modification time from sql2file(). */
709+ int cmd_len;
710+ int read_mds = -1; /* read_maildirsize() returned value */
711+ struct stat mdffile_stat; /* To check if the maildirfolder file exists. */
712
713 GETTIMEOFDAY(&starttime);
714
715@@ -94,15 +486,14 @@ int deliver_maildir(LOCAL_STATE stat
716 */
717 state.level++;
718 if (msg_verbose)
719- MSG_LOG_STATE(myname, state);
720+ MSG_LOG_STATE(myname, state);
721
722 /*
723 * Don't deliver trace-only requests.
724 */
725 if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
726- dsb_simple(why, "2.0.0", "delivers to maildir");
727- return (sent(BOUNCE_FLAGS(state.request),
728- SENT_ATTR(state.msg_attr)));
729+ dsb_simple(why, "2.0.0", "delivers to maildir");
730+ return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
731 }
732
733 /*
734@@ -110,18 +501,116 @@ int deliver_maildir(LOCAL_STATE stat
735 * attribute to reflect the final recipient.
736 */
737 if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
738- msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
739+ msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
740 state.msg_attr.delivered = state.msg_attr.rcpt.address;
741 mail_copy_status = MAIL_COPY_STAT_WRITE;
742 buf = vstring_alloc(100);
743
744- copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH
745- | MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT;
746+ copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT;
747
748- newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0);
749- tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0);
750- curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0);
751+ /*
752+ * Concatenate the maildir suffix (if set).
753+ */
754+ if (*var_virt_maildir_suffix == 0) {
755+ newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0);
756+ tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0);
757+ curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0);
758+ }
759+ else {
760+ newdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
761+ tmpdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
762+ curdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
763+ newdir = concatenate(newdir, "new/", (char *) 0);
764+ tmpdir = concatenate(tmpdir, "tmp/", (char *) 0);
765+ curdir = concatenate(curdir, "cur/", (char *) 0);
766+ }
767
768+ /* get the sizefilename, no matter if we use var_virt_maildir_extended */
769+ if (*var_virt_maildir_suffix == 0) {
770+ sizefilename = concatenate(usr_attr.mailbox, "maildirsize", (char *) 0);
771+ } else {
772+ sizefilename = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
773+ sizefilename = concatenate(sizefilename, "maildirsize", (char *) 0);
774+ }
775+
776+ /*
777+ * Look up the virtual maildir limit size for this user.
778+ * Fall back to virtual_mailbox_limit in case lookup failed.
779+ * If virtual maildir limit size is negative, fall back to virtual_mailbox_limit.
780+ * If it's 0, set the mailbox limit to 0, which means unlimited.
781+ * If it's more than 0 (positive int), check if the value is smaller than the maximum message size,
782+ * if it is and the virtual maildir limit can't be overridden, fall back to virtual_mailbox_limit and
783+ * warn the user, else use the value directly as the maildir limit.
784+ */
785+ if (*var_virt_mailbox_limit_maps != 0 && (limit_res = mail_addr_find(virtual_mailbox_limit_maps, state.msg_attr.user, (char **) NULL)) != 0) {
786+ n = atol(limit_res);
787+ if (n > 0) {
788+ if ((n < var_message_limit) && (!var_virt_mailbox_limit_override)) {
789+ n = var_virt_mailbox_limit;
790+
791+ msg_warn("%s: recipient %s - virtual maildir limit is smaller than %s in %s - falling back to %s",
792+ myname, state.msg_attr.user, VAR_MESSAGE_LIMIT, virtual_mailbox_limit_maps->title,
793+ VAR_VIRT_MAILBOX_LIMIT);
794+ }
795+ else {
796+ if (msg_verbose)
797+ msg_info("%s: set virtual maildir limit size for %s to %ld",
798+ myname, usr_attr.mailbox, n);
799+ }
800+ }
801+ else if (n == 0) {
802+ if (msg_verbose)
803+ msg_info("%s: set virtual maildir limit size for %s to %ld",
804+ myname, usr_attr.mailbox, n);
805+ }
806+ else {
807+ if (msg_verbose)
808+ msg_info("%s: quota is negative (%ld), using default virtual_mailbox_limit (%ld)",
809+ myname, n, var_virt_mailbox_limit);
810+ /* Invalid limit size (negative). Use default virtual_mailbox_limit. */
811+ n = var_virt_mailbox_limit;
812+ }
813+ }
814+ else {
815+ if (msg_verbose)
816+ msg_info("%s: no limit found in the maps, using default virtual_mailbox_limit (%ld)",
817+ myname, var_virt_mailbox_limit);
818+ /* There is no limit in the maps. Use default virtual_mailbox_limit. */
819+ n = var_virt_mailbox_limit;
820+ }
821+
822+ /* If there should is a quota on maildir generaly, check it before delivering the mail */
823+ if (n != 0) {
824+ set_eugid(usr_attr.uid, usr_attr.gid);
825+ /* try to read the quota from maildirsize file. Returned values by read_maildirsize:
826+ x < 0 = something failed
827+ x >= 0 = reading successfully finished - sum si returned, so sum size of Maildir was 0 or more */
828+ if (!var_virt_mailbox_limit_inbox && var_virt_maildir_extended && (read_mds = read_maildirsize(sizefilename, &saved_size, &saved_count)) >= 0) {
829+ if (msg_verbose)
830+ msg_info("%s: maildirsize used=%s sum=%ld count=%ld", myname, sizefilename, saved_size, saved_count);
831+ } else {
832+ if (msg_verbose)
833+ msg_info("%s: We will recount the quota (var_virt_mailbox_limit = %ld, var_virt_maildir_extended = %d, read_maildirsize = %d)",
834+ myname, var_virt_mailbox_limit, var_virt_maildir_extended, read_mds);
835+
836+ /* sanity */
837+ saved_size = 0;
838+ saved_count = 0;
839+
840+ if (var_virt_mailbox_limit_inbox) {
841+ /* Check Inbox only (new, cur and tmp dirs). */
842+ saved_size = check_dir_size(newdir, &saved_count);
843+ saved_size += check_dir_size(curdir, &saved_count);
844+ saved_size += check_dir_size(tmpdir, &saved_count);
845+ } else {
846+ /* Check all boxes. */
847+ saved_size = check_dir_size(usr_attr.mailbox, &saved_count);
848+ }
849+
850+ set_eugid(var_owner_uid, var_owner_gid);
851+ }
852+ }
853+
854 /*
855 * Create and write the file as the recipient, so that file quota work.
856 * Create any missing directories on the fly. The file name is chosen
857@@ -175,46 +664,288 @@ int deliver_maildir(LOCAL_STATE stat
858 * [...]
859 */
860 set_eugid(usr_attr.uid, usr_attr.gid);
861- vstring_sprintf(buf, "%lu.P%d.%s",
862- (unsigned long) starttime.tv_sec, var_pid, get_hostname());
863+ vstring_sprintf(buf, "%lu.P%d.%s", (unsigned long) starttime.tv_sec, var_pid, get_hostname());
864 tmpfile = concatenate(tmpdir, STR(buf), (char *) 0);
865 newfile = 0;
866+ bkpnewfile = 0;
867 if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0
868- && (errno != ENOENT
869- || make_dirs(tmpdir, 0700) < 0
870- || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
871- dsb_simple(why, mbox_dsn(errno, "4.2.0"),
872- "create maildir file %s: %m", tmpfile);
873- } else if (fstat(vstream_fileno(dst), &st) < 0) {
874-
875- /*
876- * Coverity 200604: file descriptor leak in code that never executes.
877- * Code replaced by msg_fatal(), as it is not worthwhile to continue
878- * after an impossible error condition.
879- */
880- msg_fatal("fstat %s: %m", tmpfile);
881- } else {
882- vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
883- (unsigned long) starttime.tv_sec,
884- (unsigned long) st.st_dev,
885- (unsigned long) st.st_ino,
886- (unsigned long) starttime.tv_usec,
887- get_hostname());
888- newfile = concatenate(newdir, STR(buf), (char *) 0);
889- if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
890- dst, copy_flags, "\n",
891- why)) == 0) {
892- if (sane_link(tmpfile, newfile) < 0
893- && (errno != ENOENT
894- || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
895- || sane_link(tmpfile, newfile) < 0)) {
896- dsb_simple(why, mbox_dsn(errno, "4.2.0"),
897- "create maildir file %s: %m", newfile);
898- mail_copy_status = MAIL_COPY_STAT_WRITE;
899- }
900- }
901- if (unlink(tmpfile) < 0)
902- msg_warn("remove %s: %m", tmpfile);
903+ && (errno != ENOENT
904+ || make_dirs(tmpdir, 0700) < 0
905+ || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
906+ dsb_simple(why, mbox_dsn(errno, "4.2.0"), "create maildir file %s: %m", tmpfile);
907+ }
908+ else if (fstat(vstream_fileno(dst), &st) < 0) {
909+ /*
910+ * Coverity 200604: file descriptor leak in code that never executes.
911+ * Code replaced by msg_fatal(), as it is not worthwhile to continue
912+ * after an impossible error condition.
913+ */
914+ msg_fatal("fstat %s: %m", tmpfile);
915+ }
916+ else {
917+ vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
918+ (unsigned long) starttime.tv_sec,
919+ (unsigned long) st.st_dev,
920+ (unsigned long) st.st_ino,
921+ (unsigned long) starttime.tv_usec,
922+ get_hostname());
923+ newfile = concatenate(newdir, STR(buf), (char *) 0);
924+ bkpnewfile = concatenate(STR(buf), (char *) 0); /* Will need it later, if we MOVE to other folders. */
925+
926+ if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), dst, copy_flags, "\n", why)) == 0) {
927+ /*
928+ * Add a ",S=<sizeoffile>" to the newly written file according to the
929+ * Maildir++ specifications: http://www.inter7.com/courierimap/README.maildirquota.html
930+ * This needs a stat(2) of the tempfile and modification of the
931+ * name of the file.
932+ */
933+ if (stat(tmpfile, &mail_stat) == 0) {
934+ if (n != 0) {
935+ saved_size += (long) mail_stat.st_size;
936+ saved_count++;
937+ }
938+ if (var_virt_maildir_extended) {
939+ /* Append the size of the file to newfile. */
940+ vstring_sprintf(buf, ",S=%ld", (long) mail_stat.st_size);
941+ newfile = concatenate(newfile, STR(buf), (char *) 0);
942+ bkpnewfile = concatenate(bkpnewfile, STR(buf), (char *) 0);
943+ }
944+ }
945+
946+ /*
947+ * Now we have the maildir size in saved_size, compare it to the max
948+ * quota value and eventually issue a message that we've overdrawn it.
949+ */
950+ if (saved_size > n) {
951+ mail_copy_status = MAIL_COPY_STAT_WRITE;
952+ if (((long) mail_stat.st_size > n) || (var_virt_overquota_bounce))
953+ errno = EFBIG;
954+ else
955+ errno = EDQUOT;
956+ }
957+ else {
958+ /* Maildirfilter code by rk@demiurg.net. */
959+ if (var_virt_maildir_filter) {
960+ if (msg_verbose)
961+ msg_info("%s: loading DICT filters", myname);
962+
963+#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
964+#define MAIL_COPY_STAT_REJECT (1<<3)
965+#define MAIL_COPY_STAT_DISCARD (1<<4)
966+
967+ /* Read filters. */
968+ filtername = concatenate("regexp:", usr_attr.mailbox, "maildirfilter", (char *) 0);
969+ sqlmtime = sql2file(strchr(filtername, '/'), state.msg_attr.user);
970+
971+ /* Check if this filter is already registered as dictionary. */
972+ if (msg_verbose)
973+ msg_info("%s: checking DICT filters for %s", myname, filtername);
974+
975+ if ((FILTERS = dict_handle(filtername))) {
976+ if (msg_verbose)
977+ msg_info("%s: DICT filter found", myname);
978+
979+ /*
980+ * If we have mtime in our DICT structure, check it against sqlmtime
981+ * and reload the filters if they differ.
982+ */
983+ if (FILTERS->mtime > 0 && sqlmtime > 0 && FILTERS->mtime != sqlmtime) {
984+ if (msg_verbose)
985+ msg_info("%s: reloading DICT filters (dict_mtime=%ld != sql_mtime=%ld)",
986+ myname, FILTERS->mtime, sqlmtime);
987+
988+ dict_unregister(filtername);
989+ FILTERS = dict_open(filtername, O_RDONLY, DICT_FLAG_LOCK);
990+ dict_register(filtername, FILTERS);
991+ FILTERS->mtime = sqlmtime;
992+ }
993+ }
994+ else {
995+ if (sqlmtime > 0) {
996+ /* Registering filter as new dictionary. */
997+ if (msg_verbose)
998+ msg_info("%s: loading DICT filters from %s (mtime=%ld)",
999+ myname, filtername, sqlmtime);
1000+
1001+ FILTERS = dict_open(filtername, O_RDONLY, DICT_FLAG_LOCK);
1002+ dict_register(filtername, FILTERS);
1003+ FILTERS->mtime = sqlmtime;
1004+ }
1005+ }
1006+
1007+ if (FILTERS && (tmpfilter = vstream_fopen(tmpfile, O_RDONLY, 0))) {
1008+ fltstr = vstring_alloc(1024);
1009+ header = (char *) malloc(8192); /* !!!INSECURE!!! See 7168-hack below. */
1010+ header[0] = 0;
1011+ vstring_get_nonl_bound(fltstr, tmpfilter, 1023);
1012+ header = concatenate(header, STR(fltstr), (char *) 0);
1013+
1014+ while(!vstream_feof(tmpfilter) && fltstr->vbuf.data[0] && strlen(header) < 7168 ) {
1015+ vstring_get_nonl_bound(fltstr, tmpfilter, 1023);
1016+ /* Glue multiline headers, replacing leading TAB with space. */
1017+ if (msg_verbose)
1018+ msg_info("%s: fltstr value: %s", myname, STR(fltstr));
1019+
1020+ if (fltstr->vbuf.data[0] == ' ' || fltstr->vbuf.data[0] == '\t' ) {
1021+ if (fltstr->vbuf.data[0] == '\t')
1022+ fltstr->vbuf.data[0] = ' ';
1023+ header = concatenate(header, STR(fltstr), (char *) 0);
1024+ }
1025+ else {
1026+ header = concatenate(header, "\n", STR(fltstr), (char *) 0);
1027+ }
1028+ }
1029+
1030+ if (msg_verbose)
1031+ msg_info("%s: checking filter CMD for %s", myname, filtername);
1032+
1033+ /* Check whole header part with regexp maps. */
1034+ if ((value = dict_get(FILTERS, lowercase(header))) != 0) {
1035+ if (msg_verbose)
1036+ msg_info("%s: preparing filter CMD", myname);
1037+
1038+ cmd_text = value + strcspn(value, " \t");
1039+ cmd_len = cmd_text - value;
1040+ while (*cmd_text && ISSPACE(*cmd_text))
1041+ cmd_text++;
1042+
1043+ if (msg_verbose)
1044+ msg_info("%s: executing filter CMD", myname);
1045+
1046+ if (STREQUAL(value, "REJECT", cmd_len)) {
1047+ if (msg_verbose)
1048+ msg_info("%s: executing filter CMD REJECT", myname);
1049+
1050+ mail_copy_status = MAIL_COPY_STAT_REJECT;
1051+ vstring_sprintf(why->reason, "%s", cmd_text);
1052+ dsb_simple(why, "5.0.0", "User filter - REJECT");
1053+ }
1054+
1055+ if (STREQUAL(value, "DISCARD", cmd_len)) {
1056+ if (msg_verbose)
1057+ msg_info("%s: executing filter CMD DISCARD", myname);
1058+
1059+ mail_copy_status = MAIL_COPY_STAT_DISCARD;
1060+ vstring_sprintf(why->reason, "%s", cmd_text);
1061+ dsb_simple(why, "5.0.0", "User filter - DISCARD");
1062+ }
1063+
1064+ if (var_virt_maildir_extended) {
1065+ if (STREQUAL(value, "MOVE", cmd_len)) {
1066+ if (msg_verbose)
1067+ msg_info("%s: executing filter CMD MOVE", myname);
1068+
1069+ strcut((char *) cmd_text, " ");
1070+ strcut((char *) cmd_text, "\t");
1071+ strcut((char *) cmd_text, "/");
1072+ strcut((char *) cmd_text, "..");
1073+
1074+ if (*var_virt_maildir_suffix == 0) {
1075+ newfile = concatenate(usr_attr.mailbox, (char *) 0);
1076+ }
1077+ else {
1078+ newfile = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0);
1079+ }
1080+
1081+ if (cmd_text[0] != '.') {
1082+ newfile = concatenate(newfile, ".", (char *) 0);
1083+ }
1084+ newdir = concatenate(newfile, cmd_text, "/", "new/", (char *) 0);
1085+ tmpdir = concatenate(newfile, cmd_text, "/", "tmp/", (char *) 0);
1086+ curdir = concatenate(newfile, cmd_text, "/", "cur/", (char *) 0);
1087+ mdffilename = concatenate(newfile, cmd_text, "/", "maildirfolder", (char *) 0);
1088+ newfile = concatenate(newfile, cmd_text, "/", "new/", bkpnewfile, (char *) 0);
1089+ }
1090+ }
1091+
1092+ if (STREQUAL(value, "LOG", cmd_len) || STREQUAL(value, "WARN", cmd_len)) {
1093+ msg_warn("%s: header check warning: %s", myname, cmd_text);
1094+ }
1095+
1096+ if (STREQUAL(value, "INFO", cmd_len)) {
1097+ msg_info("%s: header check info: %s", myname, cmd_text);
1098+ }
1099+
1100+ if (msg_verbose)
1101+ msg_info("%s: exiting filter CMD", myname);
1102+ } /* End-Of-Check */
1103+
1104+ myfree(header);
1105+ vstring_free(fltstr);
1106+ vstream_fclose(tmpfilter);
1107+ }
1108+
1109+ myfree(filtername);
1110+ } /* End-Of-Maildirfilter */
1111+
1112+ /* Deliver to curdir. */
1113+ if (mail_copy_status == 0) {
1114+ if (sane_link(tmpfile, newfile) < 0
1115+ && (errno != ENOENT
1116+ || (make_dirs(curdir, 0700), make_dirs(newdir, 0700), make_dirs(tmpdir, 0700)) < 0
1117+ || sane_link(tmpfile, newfile) < 0)) {
1118+ dsb_simple(why, mbox_dsn(errno, "4.2.0"), "create maildir file %s: %m", newfile);
1119+ mail_copy_status = MAIL_COPY_STAT_WRITE;
1120+ }
1121+
1122+ if (var_virt_maildir_extended) {
1123+ time(&tm);
1124+
1125+ /* Check if the quota in the file is the same as the current one, if not, delete the file. */
1126+ sizefile = vstream_fopen(sizefilename, O_RDONLY, 0);
1127+ if (sizefile) {
1128+ filequota = vstring_alloc(128);
1129+ vstring_get_null_bound(filequota, sizefile, 127);
1130+ vstream_fclose(sizefile);
1131+ if (atol(vstring_export(filequota)) != n)
1132+ unlink(sizefilename);
1133+ }
1134+
1135+ /* Open maildirsize file to append this transaction. */
1136+ sizefile = vstream_fopen(sizefilename, O_WRONLY | O_APPEND, 0640);
1137+
1138+ /* If the open fails (maildirsize doesn't exist), or it's too large, or too old, overwrite it. */
1139+ if(!sizefile || (stat(sizefilename, &sizefile_stat) < 0) || (sizefile_stat.st_size > SIZEFILE_MAX) || (sizefile_stat.st_mtime + 15*60 < tm)) {
1140+ /* If the file exists, sizefile has been opened above, so close it first. */
1141+ if (sizefile) {
1142+ vstream_fclose(sizefile);
1143+ sizefile = vstream_fopen(sizefilename, O_WRONLY | O_TRUNC, 0640);
1144+ }
1145+ else {
1146+ sizefile = vstream_fopen(sizefilename, O_WRONLY | O_CREAT, 0640);
1147+ }
1148+
1149+ /* If the creation worked, write to the file, otherwise just give up. */
1150+ if (sizefile) {
1151+ vstream_fprintf(sizefile, "%ldS\n%ld %ld\n", n, saved_size, saved_count);
1152+ vstream_fclose(sizefile);
1153+ }
1154+ }
1155+ else {
1156+ /* We opened maildirsize, so let's just append this transaction and close it. */
1157+ vstream_fprintf(sizefile, "%ld 1\n", (long) mail_stat.st_size);
1158+ vstream_fclose(sizefile);
1159+ }
1160+
1161+ /*
1162+ * 1) mdffilename != 0, so the maildirfilter code went through the MOVE to subfolder rule.
1163+ * 2) stat() failed, maybe the file does not exist? Try to create it.
1164+ */
1165+ if (mdffilename && (stat(mdffilename, &mdffile_stat) < 0)) {
1166+ mdffile = vstream_fopen(mdffilename, O_WRONLY | O_CREAT, 0600);
1167+ if (mdffile) {
1168+ vstream_fclose(mdffile);
1169+ }
1170+ else {
1171+ msg_warn("Cannot create maildirfolder file '%s': %s", mdffilename, strerror(errno));
1172+ }
1173+ }
1174+ }
1175+ }
1176+ }
1177+ }
1178+ if (unlink(tmpfile) < 0)
1179+ msg_warn("remove %s: %m", tmpfile);
1180 }
1181 set_eugid(var_owner_uid, var_owner_gid);
1182
1183@@ -224,31 +955,64 @@ int deliver_maildir(LOCAL_STATE stat
1184 * location possibly under user control.
1185 */
1186 if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
1187- deliver_status = DEL_STAT_DEFER;
1188- } else if (mail_copy_status != 0) {
1189- if (errno == EACCES) {
1190- msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
1191- (long) usr_attr.uid, (long) usr_attr.gid,
1192- STR(why->reason));
1193- msg_warn("perhaps you need to create the maildirs in advance");
1194- }
1195- vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
1196- deliver_status =
1197- (STR(why->status)[0] == '4' ?
1198- defer_append : bounce_append)
1199- (BOUNCE_FLAGS(state.request),
1200- BOUNCE_ATTR(state.msg_attr));
1201- } else {
1202- dsb_simple(why, "2.0.0", "delivered to maildir");
1203- deliver_status = sent(BOUNCE_FLAGS(state.request),
1204- SENT_ATTR(state.msg_attr));
1205+ deliver_status = DEL_STAT_DEFER;
1206+ }
1207+ else if (mail_copy_status != 0) {
1208+ if (errno == EACCES) {
1209+ msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
1210+ (long) usr_attr.uid, (long) usr_attr.gid, STR(why->reason));
1211+ msg_warn("perhaps you need to create the maildirs in advance");
1212+ }
1213+
1214+ /* Support per-recipient bounce messages. */
1215+ const char *limit_message;
1216+ int errnored = errno; /* Seems like mail_addr_find resets errno ... */
1217+
1218+ 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) {
1219+ errno = errnored;
1220+ if (errno == EFBIG) {
1221+ dsb_simple(why, "5.2.2", limit_message, NULL);
1222+ }
1223+ if (errno == EDQUOT) {
1224+ dsb_simple(why, "4.2.2", limit_message, NULL);
1225+ }
1226+ }
1227+ else {
1228+ errno = errnored;
1229+ if (errno == EFBIG) {
1230+ dsb_simple(why, "5.2.2", var_virt_maildir_limit_message, NULL);
1231+ }
1232+ if (errno == EDQUOT) {
1233+ dsb_simple(why, "4.2.2", var_virt_maildir_limit_message, NULL);
1234+ }
1235+ }
1236+
1237+ vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
1238+ deliver_status =
1239+ (STR(why->status)[0] == '4' ? defer_append : bounce_append)
1240+ (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
1241 }
1242+ else {
1243+ dsb_simple(why, "2.0.0", "delivered to maildir");
1244+ deliver_status = sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr));
1245+ }
1246+
1247 vstring_free(buf);
1248+
1249 myfree(newdir);
1250 myfree(tmpdir);
1251 myfree(curdir);
1252+
1253+ if (sizefilename)
1254+ myfree(sizefilename);
1255+ if (mdffilename)
1256+ myfree(mdffilename);
1257+
1258 myfree(tmpfile);
1259 if (newfile)
1260- myfree(newfile);
1261+ myfree(newfile);
1262+ if (bkpnewfile)
1263+ myfree(bkpnewfile);
1264+
1265 return (deliver_status);
1266 }
1267diff -ruNp postfix-2.11.10.orig/src/virtual/virtual.c postfix-2.11.10/src/virtual/virtual.c
1268--- postfix-2.11.10.orig/src/virtual/virtual.c 2011-02-19 01:46:06.000000000 +0100
1269+++ postfix-2.11.10/src/virtual/virtual.c 2017-07-02 00:13:25.779285410 +0200
1270@@ -335,12 +335,30 @@ long var_virt_mailbox_limit;
1271 char *var_mail_spool_dir; /* XXX dependency fix */
1272 bool var_strict_mbox_owner;
1273
1274+char *var_virt_mailbox_limit_maps;
1275+bool var_virt_mailbox_limit_inbox;
1276+bool var_virt_mailbox_limit_override;
1277+bool var_virt_maildir_extended;
1278+bool var_virt_overquota_bounce;
1279+char *var_virt_maildir_limit_message;
1280+char *var_virt_maildir_limit_message_maps;
1281+char *var_virt_maildir_suffix;
1282+bool var_virt_trash_count;
1283+char *var_virt_trash_name;
1284+bool var_virt_maildir_filter;
1285+char *var_virt_maildir_filter_maps;
1286+
1287+
1288 /*
1289 * Mappings.
1290 */
1291 MAPS *virtual_mailbox_maps;
1292 MAPS *virtual_uid_maps;
1293 MAPS *virtual_gid_maps;
1294+MAPS *virtual_mailbox_limit_maps;
1295+MAPS *virtual_maildir_limit_message_maps;
1296+MAPS *virtual_maildir_filter_maps;
1297+
1298
1299 /*
1300 * Bit masks.
1301@@ -450,15 +468,28 @@ static void post_init(char *unused_name,
1302 */
1303 virtual_mailbox_maps =
1304 maps_create(VAR_VIRT_MAILBOX_MAPS, var_virt_mailbox_maps,
1305- DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
1306+ DICT_FLAG_LOCK);
1307
1308 virtual_uid_maps =
1309 maps_create(VAR_VIRT_UID_MAPS, var_virt_uid_maps,
1310- DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
1311+ DICT_FLAG_LOCK);
1312
1313 virtual_gid_maps =
1314 maps_create(VAR_VIRT_GID_MAPS, var_virt_gid_maps,
1315- DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
1316+ DICT_FLAG_LOCK);
1317+
1318+ virtual_mailbox_limit_maps =
1319+ maps_create(VAR_VIRT_MAILBOX_LIMIT_MAPS, var_virt_mailbox_limit_maps,
1320+ DICT_FLAG_LOCK);
1321+
1322+ virtual_maildir_limit_message_maps =
1323+ maps_create(VAR_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS, var_virt_maildir_limit_message_maps,
1324+ DICT_FLAG_LOCK);
1325+
1326+ virtual_maildir_filter_maps =
1327+ maps_create(VAR_VIRT_MAILDIR_FILTER_MAPS, var_virt_maildir_filter_maps,
1328+ DICT_FLAG_LOCK);
1329+
1330
1331 virtual_mbox_lock_mask = mbox_lock_mask(var_virt_mailbox_lock);
1332 }
1333@@ -510,10 +541,22 @@ int main(int argc, char **argv)
1334 VAR_VIRT_GID_MAPS, DEF_VIRT_GID_MAPS, &var_virt_gid_maps, 0, 0,
1335 VAR_VIRT_MAILBOX_BASE, DEF_VIRT_MAILBOX_BASE, &var_virt_mailbox_base, 1, 0,
1336 VAR_VIRT_MAILBOX_LOCK, DEF_VIRT_MAILBOX_LOCK, &var_virt_mailbox_lock, 1, 0,
1337+ VAR_VIRT_MAILBOX_LIMIT_MAPS, DEF_VIRT_MAILBOX_LIMIT_MAPS, &var_virt_mailbox_limit_maps, 0, 0,
1338+ VAR_VIRT_MAILDIR_LIMIT_MESSAGE, DEF_VIRT_MAILDIR_LIMIT_MESSAGE, &var_virt_maildir_limit_message, 1, 0,
1339+ VAR_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS, DEF_VIRT_MAILDIR_LIMIT_MESSAGE_MAPS, &var_virt_maildir_limit_message_maps, 0, 0,
1340+ VAR_VIRT_MAILDIR_SUFFIX, DEF_VIRT_MAILDIR_SUFFIX, &var_virt_maildir_suffix, 0, 0,
1341+ VAR_VIRT_TRASH_NAME, DEF_VIRT_TRASH_NAME, &var_virt_trash_name, 0, 0,
1342+ VAR_VIRT_MAILDIR_FILTER_MAPS, DEF_VIRT_MAILDIR_FILTER_MAPS, &var_virt_maildir_filter_maps, 0, 0,
1343 0,
1344 };
1345 static const CONFIG_BOOL_TABLE bool_table[] = {
1346 VAR_STRICT_MBOX_OWNER, DEF_STRICT_MBOX_OWNER, &var_strict_mbox_owner,
1347+ VAR_VIRT_MAILBOX_LIMIT_INBOX, DEF_VIRT_MAILBOX_LIMIT_INBOX, &var_virt_mailbox_limit_inbox,
1348+ VAR_VIRT_MAILBOX_LIMIT_OVERRIDE, DEF_VIRT_MAILBOX_LIMIT_OVERRIDE, &var_virt_mailbox_limit_override,
1349+ VAR_VIRT_MAILDIR_EXTENDED, DEF_VIRT_MAILDIR_EXTENDED, &var_virt_maildir_extended,
1350+ VAR_VIRT_OVERQUOTA_BOUNCE, DEF_VIRT_OVERQUOTA_BOUNCE, &var_virt_overquota_bounce,
1351+ VAR_VIRT_TRASH_COUNT, DEF_VIRT_TRASH_COUNT, &var_virt_trash_count,
1352+ VAR_VIRT_MAILDIR_FILTER, DEF_VIRT_MAILDIR_FILTER, &var_virt_maildir_filter,
1353 0,
1354 };
1355
1356@@ -530,6 +573,7 @@ int main(int argc, char **argv)
1357 MAIL_SERVER_PRE_INIT, pre_init,
1358 MAIL_SERVER_POST_INIT, post_init,
1359 MAIL_SERVER_PRE_ACCEPT, pre_accept,
1360+ MAIL_SERVER_BOOL_TABLE, bool_table,
1361 MAIL_SERVER_PRIVILEGED,
1362 0);
1363 }
1364diff -ruNp postfix-2.11.10.orig/src/virtual/virtual.h postfix-2.11.10/src/virtual/virtual.h
1365--- postfix-2.11.10.orig/src/virtual/virtual.h 2006-01-08 00:59:47.000000000 +0100
1366+++ postfix-2.11.10/src/virtual/virtual.h 2017-07-02 00:13:25.780285407 +0200
1367@@ -34,6 +34,9 @@
1368 extern MAPS *virtual_mailbox_maps;
1369 extern MAPS *virtual_uid_maps;
1370 extern MAPS *virtual_gid_maps;
1371+extern MAPS *virtual_mailbox_limit_maps;
1372+extern MAPS *virtual_maildir_limit_message_maps;
1373+extern MAPS *virtual_maildir_filter_maps;
1374
1375 /*
1376 * User attributes: these control the privileges for delivery to external
This page took 0.187406 seconds and 4 git commands to generate.