summaryrefslogtreecommitdiffstats
path: root/maildirquota.c
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2013-02-04 02:48:34 +0100
committermanuel <manuel@mausz.at>2013-02-04 02:48:34 +0100
commit5d47a7101a2394c7f7bb288585464fa293a2ad33 (patch)
tree0c371687c8791b2278c5b8049bbd64a7de1d0863 /maildirquota.c
parent86d5a6ec30ceea44e9459394d2e6403642cdd158 (diff)
downloadqmail-5d47a7101a2394c7f7bb288585464fa293a2ad33.tar.gz
qmail-5d47a7101a2394c7f7bb288585464fa293a2ad33.tar.bz2
qmail-5d47a7101a2394c7f7bb288585464fa293a2ad33.zip
[PATCH] maildir++ for qmail-local and qmail-pop3d
Patch to make qmail-local and qmail-pop3d compatible with the maildir++ quota system that is used by vpopmail and courier-imap qmail-maildir++
Diffstat (limited to 'maildirquota.c')
-rw-r--r--maildirquota.c685
1 files changed, 685 insertions, 0 deletions
diff --git a/maildirquota.c b/maildirquota.c
new file mode 100644
index 0000000..e9ba47b
--- /dev/null
+++ b/maildirquota.c
@@ -0,0 +1,685 @@
1/*
2** Copyright 1998 - 2002 Double Precision, Inc.
3** See COPYING for distribution information.
4*/
5
6#if HAVE_CONFIG_H
7#include "config.h"
8#endif
9
10#include <sys/types.h>
11/* #if HAVE_DIRENT_H */
12#include <dirent.h>
13#define NAMLEN(dirent) strlen((dirent)->d_name)
14/* #else
15#define dirent direct
16#define NAMLEN(dirent) (dirent)->d_namlen
17#if HAVE_SYS_NDIR_H
18#include <sys/ndir.h>
19#endif
20#if HAVE_SYS_DIR_H
21#include <sys/dir.h>
22#endif
23#if HAVE_NDIR_H
24#include <ndir.h>
25#endif
26#endif */
27#include <sys/types.h>
28/* #if HAVE_SYS_STAT_H */
29#include <sys/stat.h>
30/* #endif */
31#include <sys/uio.h>
32
33#include "maildirquota.h"
34#include "maildirmisc.h"
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <errno.h>
39/* #if HAVE_FCNTL_H */
40#include <fcntl.h>
41/* #endif */
42#if HAVE_UNISTD_H
43#include <unistd.h>
44#endif
45#include <time.h>
46#include "numlib.h"
47
48static const char rcsid[]="$Id: maildirquota.c,v 1.9 2002/05/01 04:05:33 mrsam Exp $";
49
50/* Read the maildirsize file */
51
52int maildirsize_read(const char *filename, /* The filename */
53 int *fdptr, /* Keep the file descriptor open */
54 off_t *sizeptr, /* Grand total of maildir size */
55 unsigned *cntptr, /* Grand total of message count */
56 unsigned *nlines, /* # of lines in maildirsize */
57 struct stat *statptr) /* The stats on maildirsize */
58{
59char buf[5120];
60int f;
61char *p;
62unsigned l;
63int n;
64int first;
65
66 if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0)
67 return (-1);
68 p=buf;
69 l=sizeof(buf);
70
71 while (l)
72 {
73 n=read(f, p, l);
74 if (n < 0)
75 {
76 close(f);
77 return (-1);
78 }
79 if (n == 0) break;
80 p += n;
81 l -= n;
82 }
83 if (l == 0 || fstat(f, statptr)) /* maildir too big */
84 {
85 close(f);
86 return (-1);
87 }
88
89 *sizeptr=0;
90 *cntptr=0;
91 *nlines=0;
92 *p=0;
93 p=buf;
94 first=1;
95 while (*p)
96 {
97 long n=0;
98 int c=0;
99 char *q=p;
100
101 while (*p)
102 if (*p++ == '\n')
103 {
104 p[-1]=0;
105 break;
106 }
107
108 if (first)
109 {
110 first=0;
111 continue;
112 }
113 sscanf(q, "%ld %d", &n, &c);
114 *sizeptr += n;
115 *cntptr += c;
116 ++ *nlines;
117 }
118 *fdptr=f;
119 return (0);
120}
121
122static char *makenewmaildirsizename(const char *, int *);
123static int countcurnew(const char *, time_t *, off_t *, unsigned *);
124static int countsubdir(const char *, const char *,
125 time_t *, off_t *, unsigned *);
126static int statcurnew(const char *, time_t *);
127static int statsubdir(const char *, const char *, time_t *);
128
129#define MDQUOTA_SIZE 'S' /* Total size of all messages in maildir */
130#define MDQUOTA_BLOCKS 'B' /* Total # of blocks for all messages in
131 maildir -- NOT IMPLEMENTED */
132#define MDQUOTA_COUNT 'C' /* Total number of messages in maildir */
133
134static int qcalc(off_t s, unsigned n, const char *quota, int *percentage)
135{
136off_t i;
137int spercentage=0;
138int npercentage=0;
139
140 errno=ENOSPC;
141 while (quota && *quota)
142 {
143 int x=1;
144
145 if (*quota < '0' || *quota > '9')
146 {
147 ++quota;
148 continue;
149 }
150 i=0;
151 while (*quota >= '0' && *quota <= '9')
152 i=i*10 + (*quota++ - '0');
153 switch (*quota) {
154 default:
155 if (i < s)
156 {
157 *percentage=100;
158 return (-1);
159 }
160
161 /*
162 ** For huge quotas, over 20mb,
163 ** divide numerator & denominator by 1024 to prevent
164 ** an overflow when multiplying by 100
165 */
166
167 x=1;
168 if (i > 20000000) x=1024;
169
170 spercentage = i ? (s/x) * 100 / (i/x):100;
171 break;
172 case 'C':
173
174 if (i < n)
175 {
176 *percentage=100;
177 return (-1);
178 }
179
180 /* Ditto */
181
182 x=1;
183 if (i > 20000000) x=1024;
184
185 npercentage = i ? ((off_t)n/x) * 100 / (i/x):100;
186 break;
187 }
188 }
189 *percentage = spercentage > npercentage ? spercentage:npercentage;
190 return (0);
191}
192
193static int doaddquota(const char *, int, const char *, long, int, int);
194
195static int docheckquota(const char *dir,
196 int *maildirsize_fdptr,
197 const char *quota_type,
198 long xtra_size,
199 int xtra_cnt, int *percentage);
200
201
202int maildir_checkquota(const char *dir,
203 int *maildirsize_fdptr,
204 const char *quota_type,
205 long xtra_size,
206 int xtra_cnt)
207{
208int dummy;
209
210 return (docheckquota(dir, maildirsize_fdptr, quota_type,
211 xtra_size, xtra_cnt, &dummy));
212}
213
214int maildir_readquota(const char *dir, const char *quota_type)
215{
216int percentage=0;
217int fd=-1;
218
219 (void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage);
220 if (fd >= 0)
221 close(fd);
222 return (percentage);
223}
224
225static int docheckquota(const char *dir,
226 int *maildirsize_fdptr,
227 const char *quota_type,
228 long xtra_size,
229 int xtra_cnt,
230 int *percentage)
231{
232char *checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
233char *newmaildirsizename;
234struct stat stat_buf;
235int maildirsize_fd;
236off_t maildirsize_size;
237unsigned maildirsize_cnt;
238unsigned maildirsize_nlines;
239int n;
240time_t tm;
241time_t maxtime;
242DIR *dirp;
243struct dirent *de;
244
245 if (checkfolder == 0) return (-1);
246 *maildirsize_fdptr= -1;
247 strcat(strcpy(checkfolder, dir), "/maildirfolder");
248 if (stat(checkfolder, &stat_buf) == 0) /* Go to parent */
249 {
250 strcat(strcpy(checkfolder, dir), "/..");
251 n=docheckquota(checkfolder, maildirsize_fdptr,
252 quota_type, xtra_size, xtra_cnt, percentage);
253 free(checkfolder);
254 return (n);
255 }
256 if (!quota_type || !*quota_type) return (0);
257
258 strcat(strcpy(checkfolder, dir), "/maildirsize");
259 time(&tm);
260 if (maildirsize_read(checkfolder, &maildirsize_fd,
261 &maildirsize_size, &maildirsize_cnt,
262 &maildirsize_nlines, &stat_buf) == 0)
263 {
264 n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
265 quota_type, percentage);
266
267 if (n == 0)
268 {
269 free(checkfolder);
270 *maildirsize_fdptr=maildirsize_fd;
271 return (0);
272 }
273 close(maildirsize_fd);
274
275 if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60)
276 return (n);
277 }
278
279 maxtime=0;
280 maildirsize_size=0;
281 maildirsize_cnt=0;
282
283 if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt))
284 {
285 free(checkfolder);
286 return (-1);
287 }
288
289 dirp=opendir(dir);
290 while (dirp && (de=readdir(dirp)) != 0)
291 {
292 if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size,
293 &maildirsize_cnt))
294 {
295 free(checkfolder);
296 closedir(dirp);
297 return (-1);
298 }
299 }
300 if (dirp)
301 {
302#if CLOSEDIR_VOID
303 closedir(dirp);
304#else
305 if (closedir(dirp))
306 {
307 free(checkfolder);
308 return (-1);
309 }
310#endif
311 }
312
313 newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
314 if (!newmaildirsizename)
315 {
316 free(checkfolder);
317 return (-1);
318 }
319
320 *maildirsize_fdptr=maildirsize_fd;
321
322 if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
323 maildirsize_cnt, 1))
324 {
325 free(newmaildirsizename);
326 unlink(newmaildirsizename);
327 close(maildirsize_fd);
328 *maildirsize_fdptr= -1;
329 free(checkfolder);
330 return (-1);
331 }
332
333 strcat(strcpy(checkfolder, dir), "/maildirsize");
334
335 if (rename(newmaildirsizename, checkfolder))
336 {
337 free(checkfolder);
338 unlink(newmaildirsizename);
339 close(maildirsize_fd);
340 *maildirsize_fdptr= -1;
341 }
342 free(checkfolder);
343 free(newmaildirsizename);
344
345 tm=0;
346
347 if (statcurnew(dir, &tm))
348 {
349 close(maildirsize_fd);
350 *maildirsize_fdptr= -1;
351 return (-1);
352 }
353
354 dirp=opendir(dir);
355 while (dirp && (de=readdir(dirp)) != 0)
356 {
357 if (statsubdir(dir, de->d_name, &tm))
358 {
359 close(maildirsize_fd);
360 *maildirsize_fdptr= -1;
361 closedir(dirp);
362 return (-1);
363 }
364 }
365 if (dirp)
366 {
367#if CLOSEDIR_VOID
368 closedir(dirp);
369#else
370 if (closedir(dirp))
371 {
372 close(maildirsize_fd);
373 *maildirsize_fdptr= -1;
374 return (-1);
375 }
376#endif
377 }
378
379 if (tm != maxtime) /* Race condition, someone changed something */
380 {
381 errno=EAGAIN;
382 return (-1);
383 }
384
385 return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
386 quota_type, percentage));
387}
388
389int maildir_addquota(const char *dir, int maildirsize_fd,
390 const char *quota_type, long maildirsize_size, int maildirsize_cnt)
391{
392 if (!quota_type || !*quota_type) return (0);
393 return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
394 maildirsize_cnt, 0));
395}
396
397static int doaddquota(const char *dir, int maildirsize_fd,
398 const char *quota_type, long maildirsize_size, int maildirsize_cnt,
399 int isnew)
400{
401union {
402 char buf[100];
403 struct stat stat_buf;
404 } u; /* Scrooge */
405char *newname2=0;
406char *newmaildirsizename=0;
407struct iovec iov[3];
408int niov;
409struct iovec *p;
410int n;
411
412 niov=0;
413 if ( maildirsize_fd < 0)
414 {
415 newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
416 if (!newname2) return (-1);
417 strcat(strcpy(newname2, dir), "/maildirfolder");
418 if (stat(newname2, &u.stat_buf) == 0)
419 {
420 strcat(strcpy(newname2, dir), "/..");
421 n=doaddquota(newname2, maildirsize_fd, quota_type,
422 maildirsize_size, maildirsize_cnt,
423 isnew);
424 free(newname2);
425 return (n);
426 }
427
428 strcat(strcpy(newname2, dir), "/maildirsize");
429
430 if ((maildirsize_fd=maildir_safeopen(newname2,
431 O_RDWR|O_APPEND, 0644)) < 0)
432 {
433 newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
434 if (!newmaildirsizename)
435 {
436 free(newname2);
437 return (-1);
438 }
439
440 maildirsize_fd=maildir_safeopen(newmaildirsizename,
441 O_CREAT|O_RDWR|O_APPEND, 0644);
442
443 if (maildirsize_fd < 0)
444 {
445 free(newname2);
446 return (-1);
447 }
448 isnew=1;
449 }
450 }
451
452 if (isnew)
453 {
454 iov[0].iov_base=(caddr_t)quota_type;
455 iov[0].iov_len=strlen(quota_type);
456 iov[1].iov_base=(caddr_t)"\n";
457 iov[1].iov_len=1;
458 niov=2;
459 }
460
461
462 sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt);
463 iov[niov].iov_base=(caddr_t)u.buf;
464 iov[niov].iov_len=strlen(u.buf);
465
466 p=iov;
467 ++niov;
468 n=0;
469 while (niov)
470 {
471 if (n)
472 {
473 if (n < p->iov_len)
474 {
475 p->iov_base=
476 (caddr_t)((char *)p->iov_base + n);
477 p->iov_len -= n;
478 }
479 else
480 {
481 n -= p->iov_len;
482 ++p;
483 --niov;
484 continue;
485 }
486 }
487
488 n=writev( maildirsize_fd, p, niov);
489
490 if (n <= 0)
491 {
492 if (newname2)
493 {
494 close(maildirsize_fd);
495 free(newname2);
496 }
497 return (-1);
498 }
499 }
500 if (newname2)
501 {
502 close(maildirsize_fd);
503
504 if (newmaildirsizename)
505 {
506 rename(newmaildirsizename, newname2);
507 free(newmaildirsizename);
508 }
509 free(newname2);
510 }
511 return (0);
512}
513
514/* New maildirsize is built in the tmp subdirectory */
515
516static char *makenewmaildirsizename(const char *dir, int *fd)
517{
518char hostname[256];
519struct stat stat_buf;
520time_t t;
521char *p;
522
523 hostname[0]=0;
524 hostname[sizeof(hostname)-1]=0;
525 gethostname(hostname, sizeof(hostname)-1);
526 p=(char *)malloc(strlen(dir)+strlen(hostname)+130);
527 if (!p) return (0);
528
529 for (;;)
530 {
531 char tbuf[NUMBUFSIZE];
532 char pbuf[NUMBUFSIZE];
533
534 time(&t);
535 strcat(strcpy(p, dir), "/tmp/");
536 sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s",
537 str_time_t(t, tbuf),
538 str_pid_t(getpid(), pbuf), hostname);
539
540 if (stat( (const char *)p, &stat_buf) < 0 &&
541 (*fd=maildir_safeopen(p,
542 O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0)
543 break;
544 sleep(3);
545 }
546 return (p);
547}
548
549static int statcurnew(const char *dir, time_t *maxtimestamp)
550{
551char *p=(char *)malloc(strlen(dir)+5);
552struct stat stat_buf;
553
554 if (!p) return (-1);
555 strcat(strcpy(p, dir), "/cur");
556 if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
557 *maxtimestamp=stat_buf.st_mtime;
558 strcat(strcpy(p, dir), "/new");
559 if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
560 *maxtimestamp=stat_buf.st_mtime;
561 free(p);
562 return (0);
563}
564
565static int statsubdir(const char *dir, const char *subdir, time_t *maxtime)
566{
567char *p;
568int n;
569
570 if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
571 strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
572 return (0);
573
574 p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
575 if (!p) return (-1);
576 strcat(strcat(strcpy(p, dir), "/"), subdir);
577 n=statcurnew(p, maxtime);
578 free(p);
579 return (n);
580}
581
582static int docount(const char *, time_t *, off_t *, unsigned *);
583
584static int countcurnew(const char *dir, time_t *maxtime,
585 off_t *sizep, unsigned *cntp)
586{
587char *p=(char *)malloc(strlen(dir)+5);
588int n;
589
590 if (!p) return (-1);
591 strcat(strcpy(p, dir), "/new");
592 n=docount(p, maxtime, sizep, cntp);
593 if (n == 0)
594 {
595 strcat(strcpy(p, dir), "/cur");
596 n=docount(p, maxtime, sizep, cntp);
597 }
598 free(p);
599 return (n);
600}
601
602static int countsubdir(const char *dir, const char *subdir, time_t *maxtime,
603 off_t *sizep, unsigned *cntp)
604{
605char *p;
606int n;
607
608 if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
609 strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
610 return (0);
611
612 p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
613 if (!p) return (2);
614 strcat(strcat(strcpy(p, dir), "/"), subdir);
615 n=countcurnew(p, maxtime, sizep, cntp);
616 free(p);
617 return (n);
618}
619
620static int docount(const char *dir, time_t *dirstamp,
621 off_t *sizep, unsigned *cntp)
622{
623struct stat stat_buf;
624char *p;
625DIR *dirp;
626struct dirent *de;
627unsigned long s;
628
629 if (stat(dir, &stat_buf)) return (0); /* Ignore */
630 if (stat_buf.st_mtime > *dirstamp) *dirstamp=stat_buf.st_mtime;
631 if ((dirp=opendir(dir)) == 0) return (0);
632 while ((de=readdir(dirp)) != 0)
633 {
634 const char *n=de->d_name;
635
636 if (*n == '.') continue;
637
638 /* PATCH - do not count msgs marked as deleted */
639
640 for ( ; *n; n++)
641 {
642 if (n[0] != ':' || n[1] != '2' ||
643 n[2] != ',') continue;
644 n += 3;
645 while (*n >= 'A' && *n <= 'Z')
646 {
647 if (*n == 'T') break;
648 ++n;
649 }
650 break;
651 }
652 if (*n == 'T') continue;
653 n=de->d_name;
654
655
656 if (maildir_parsequota(n, &s) == 0)
657 stat_buf.st_size=s;
658 else
659 {
660 p=(char *)malloc(strlen(dir)+strlen(n)+2);
661 if (!p)
662 {
663 closedir(dirp);
664 return (-1);
665 }
666 strcat(strcat(strcpy(p, dir), "/"), n);
667 if (stat(p, &stat_buf))
668 {
669 free(p);
670 continue;
671 }
672 free(p);
673 }
674 *sizep += stat_buf.st_size;
675 ++*cntp;
676 }
677
678#if CLOSEDIR_VOID
679 closedir(dirp);
680#else
681 if (closedir(dirp))
682 return (-1);
683#endif
684 return (0);
685}