]> git.pld-linux.org Git - packages/postfix.git/blob - postfix-vda.patch
- up to 3.5.0
[packages/postfix.git] / postfix-vda.patch
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
This page took 0.385433 seconds and 3 git commands to generate.