]>
Commit | Line | Data |
---|---|---|
17cc8889 AO |
1 | diff -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 | + | |
15 | diff -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; | |
73 | diff -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 | |
89 | diff -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. | |
279 | diff -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 | } | |
1267 | diff -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 | } | |
1364 | diff -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 |