summaryrefslogtreecommitdiffstats
path: root/maildir/maildirquota.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /maildir/maildirquota.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'maildir/maildirquota.c')
-rw-r--r--maildir/maildirquota.c1157
1 files changed, 1157 insertions, 0 deletions
diff --git a/maildir/maildirquota.c b/maildir/maildirquota.c
new file mode 100644
index 0000000..3f3e0bd
--- /dev/null
+++ b/maildir/maildirquota.c
@@ -0,0 +1,1157 @@
+/*
+** Copyright 1998 - 2010 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#if HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#if HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif
+#include <sys/types.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "maildirquota.h"
+#include "maildirmisc.h"
+#include "maildircreate.h"
+#include "quotawarnmsg.h"
+#include "../rfc822/rfc822.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <ctype.h>
+#include <numlib/numlib.h>
+
+
+static void parsequotastr(const char *, struct maildirquota *);
+
+#define DIG(c) ( (c) >= '0' && (c) <= '9')
+
+/* Read the maildirsize file */
+
+static int maildir_openquotafile_init(struct maildirsize *info,
+ const char *maildir,
+ const char *newquota);
+
+static int do_maildir_openquotafile(struct maildirsize *info,
+ const char *filename,
+ const char *newquota);
+
+int maildir_openquotafile(struct maildirsize *info, const char *maildir)
+{
+ return (maildir_openquotafile_init(info, maildir, NULL));
+}
+
+static int maildir_openquotafile_init(struct maildirsize *info,
+ const char *maildir,
+ const char *newquota)
+{
+ int rc;
+
+ char *buf=(char *)malloc(strlen(maildir)+sizeof("/maildirfolder"));
+
+ memset(info, 0, sizeof(*info));
+
+ info->fd= -1;
+
+ if (!buf)
+ return (-1);
+
+ strcat(strcpy(buf, maildir), "/maildirfolder");
+ if (stat(buf, &info->statbuf) == 0) /* Go to parent */
+ {
+ strcat(strcpy(buf, maildir), "/..");
+
+ rc=maildir_openquotafile_init(info, buf, newquota);
+ free(buf);
+ return rc;
+ }
+
+ info->maildir=strdup(maildir);
+
+ if (!info->maildir)
+ {
+ free(buf);
+ return (-1);
+ }
+
+ strcat(strcpy(info->maildirsizefile=buf, maildir), "/maildirsize");
+
+ rc=do_maildir_openquotafile(info, buf, newquota);
+
+ if (rc == 0)
+ return (0);
+
+ free(buf);
+ free(info->maildir);
+ return (rc);
+}
+
+static int do_maildir_openquotafile(struct maildirsize *info,
+ const char *filename,
+ const char *newquota)
+{
+ char buf[5120];
+ char *p;
+ unsigned l;
+ int n;
+ int first;
+
+ /*
+ ** When setting a new quota, we don't care about the existing
+ ** maildirsize.
+ */
+
+ if ((info->fd=(newquota ? open("/dev/null", O_RDWR):
+ maildir_safeopen(filename,
+ O_RDWR|O_APPEND, 0))) < 0)
+ return (0); /* No quota */
+
+ if (newquota)
+ {
+ parsequotastr(newquota, &info->quota);
+
+ if (info->quota.nbytes == 0 &&
+ info->quota.nmessages == 0)
+ {
+ close(info->fd);
+ info->fd= -1;
+ errno=EINVAL;
+ return (-1);
+ }
+ info->recalculation_needed=1;
+ return (0);
+ }
+
+ p=buf;
+ l=sizeof(buf);
+
+ while (l)
+ {
+ n=read(info->fd, p, l);
+ if (n < 0)
+ {
+ close(info->fd);
+ info->fd= -1;
+ return (-1);
+ }
+ if (n == 0) break;
+ p += n;
+ l -= n;
+ }
+
+ if (fstat(info->fd, &info->statbuf)) /* maildir too big */
+ {
+ close(info->fd);
+ info->fd= -1;
+ return (-1);
+ }
+
+ if (l == 0) /*
+ ** maildirsize overflowed, still need to read its
+ ** quota
+ */
+ {
+ p[-1]=0;
+ p=strchr(buf, '\n');
+ if (p)
+ *p=0;
+ parsequotastr(buf, &info->quota);
+ info->recalculation_needed=1;
+ return (0);
+ }
+
+
+ info->size.nbytes=0;
+ info->size.nmessages=0;
+ info->nlines=0;
+ *p=0;
+ p=buf;
+ first=1;
+ while (*p)
+ {
+ int64_t n=0;
+ int c=0;
+ char *q=p;
+ int neg;
+
+ while (*p)
+ if (*p++ == '\n')
+ {
+ p[-1]=0;
+ break;
+ }
+
+ if (first)
+ {
+ parsequotastr(q, &info->quota);
+ first=0;
+ continue;
+ }
+
+ while (*q && isspace((int)(unsigned char)*q))
+ ++q;
+
+ neg=0;
+ if (*q == '-')
+ {
+ neg=1;
+ ++q;
+ }
+
+ if (DIG(*q))
+ {
+ while (DIG(*q))
+ {
+ n=n*10 + (*q++ - '0');
+ }
+
+ if (neg)
+ n= -n;
+
+ neg=0;
+ while (*q && isspace((int)(unsigned char)*q))
+ ++q;
+
+ if (*q == '-')
+ {
+ neg=1;
+ ++q;
+ }
+ if (DIG(*q))
+ {
+ while (DIG(*q))
+ {
+ c=c*10 + (*q++ - '0');
+ }
+
+ if (neg)
+ c= -c;
+
+ info->size.nbytes += n;
+ info->size.nmessages += c;
+ }
+ }
+
+ ++ info->nlines;
+ }
+ if (info->size.nbytes < 0 ||
+ info->size.nmessages < 0)
+ info->recalculation_needed=1;
+ return (0);
+}
+
+static void parsequotastr(const char *quota, struct maildirquota *q)
+{
+ int64_t i;
+ const char *quota_start = quota;
+
+ q->nbytes=0;
+ q->nmessages=0;
+
+ while (quota && *quota)
+ {
+ if (!DIG(*quota))
+ {
+ ++quota;
+ continue;
+ }
+ i=0;
+ while (DIG(*quota))
+ i=i*10 + (*quota++ - '0');
+ switch (*quota) {
+ case MDQUOTA_SIZE:
+ q->nbytes=i;
+ break;
+ case MDQUOTA_COUNT:
+ q->nmessages=i;
+ break;
+ default:
+ fprintf(stderr, "WARN: quota string '%s' not parseable\n",
+ quota_start);
+ break;
+ }
+ }
+}
+
+
+void maildir_closequotafile(struct maildirsize *info)
+{
+ if (info->maildir)
+ free (info->maildir);
+ info->maildir=NULL;
+
+ if (info->maildirsizefile)
+ free (info->maildirsizefile);
+ info->maildirsizefile=NULL;
+
+ if (info->fd >= 0)
+ close(info->fd);
+ info->fd= -1;
+}
+
+/**
+ ** Check if size > quota, and calculate by how much
+ */
+
+static int checkOneQuota(int64_t size, int64_t quota, int *percentage);
+
+static int checkQuota(struct maildirquota *size,
+ struct maildirquota *quota, int *percentage)
+{
+ int b_quota;
+ int n_quota;
+
+ if (checkOneQuota(size->nbytes, quota->nbytes, &b_quota) ||
+ checkOneQuota(size->nmessages, quota->nmessages,
+ &n_quota))
+ {
+ if (percentage)
+ *percentage= 100;
+ errno=ENOSPC;
+ return -1;
+ }
+
+ if (b_quota < n_quota)
+ b_quota=n_quota;
+
+ if (percentage)
+ *percentage=b_quota;
+ return (0);
+}
+
+static int checkOneQuota(int64_t size, int64_t quota, int *percentage)
+{
+ int x=1;
+
+ if (quota == 0) /* No quota */
+ {
+ *percentage=0;
+ return (0);
+ }
+
+ if (size > quota)
+ {
+ *percentage=100;
+ return (-1);
+ }
+
+ if (quota > 20000000)
+ x=1024;
+
+ *percentage= quota > 0 ? (size/x) * 100 / (quota/x):0;
+ return 0;
+}
+
+static char *makenewmaildirsizename(const char *, int *);
+static int countcurnew(const char *, time_t *, int64_t *, unsigned *);
+static int countsubdir(const char *, const char *,
+ time_t *, int64_t *, unsigned *);
+static int statcurnew(const char *, time_t *);
+static int statsubdir(const char *, const char *, time_t *);
+
+static int doaddquota(struct maildirsize *, int, int64_t, int, int);
+
+static int docheckquota(struct maildirsize *info,
+ int64_t xtra_size,
+ int xtra_cnt, int *percentage);
+
+int maildir_checkquota(struct maildirsize *info,
+ int64_t xtra_size,
+ int xtra_cnt)
+{
+ int dummy;
+
+ return (docheckquota(info, xtra_size, xtra_cnt, &dummy));
+}
+
+int maildir_readquota(struct maildirsize *info)
+{
+ int percentage=0;
+
+ (void)docheckquota(info, 0, 0, &percentage);
+ return (percentage);
+}
+
+static int docheckquota(struct maildirsize *info,
+ int64_t xtra_size,
+ int xtra_cnt,
+ int *percentage)
+{
+ char *newmaildirsizename;
+ int maildirsize_fd;
+ int64_t maildirsize_size;
+ unsigned maildirsize_cnt;
+
+ time_t tm;
+ time_t maxtime;
+ DIR *dirp;
+ struct dirent *de;
+
+ struct maildirquota new_quota;
+
+ *percentage=0;
+
+ if (info->fd < 0) /* No quota */
+ return (0);
+
+ new_quota=info->size;
+
+ new_quota.nbytes += xtra_size;
+ new_quota.nmessages += xtra_cnt;
+
+ if (!info->recalculation_needed &&
+ checkQuota(&new_quota, &info->quota, percentage) == 0)
+ return (0); /* New size is under quota */
+
+ /*
+ ** Overquota, see if it's time to recalculate the quota anyway
+ */
+
+ time(&tm);
+ if (!info->recalculation_needed &&
+ info->nlines == 1 && tm < info->statbuf.st_mtime + 15*60)
+ return (-1);
+
+
+ maxtime=0;
+ maildirsize_size=0;
+ maildirsize_cnt=0;
+
+ if (countcurnew(info->maildir,
+ &maxtime, &maildirsize_size, &maildirsize_cnt))
+ {
+ errno=EIO;
+ return (-1);
+ }
+
+ dirp=opendir(info->maildir);
+ while (dirp && (de=readdir(dirp)) != 0)
+ {
+ if (countsubdir(info->maildir, de->d_name,
+ &maxtime, &maildirsize_size,
+ &maildirsize_cnt))
+ {
+ errno=EIO;
+ closedir(dirp);
+ return (-1);
+ }
+ }
+ if (dirp)
+ {
+#if CLOSEDIR_VOID
+ closedir(dirp);
+#else
+ if (closedir(dirp))
+ {
+ errno=EIO;
+ return (-1);
+ }
+#endif
+ }
+
+ newmaildirsizename=makenewmaildirsizename(info->maildir,
+ &maildirsize_fd);
+ if (!newmaildirsizename)
+ {
+ errno=EIO;
+ return (-1);
+ }
+
+ if (doaddquota(info, maildirsize_fd, maildirsize_size,
+ maildirsize_cnt, 1))
+ {
+ unlink(newmaildirsizename);
+ free(newmaildirsizename);
+ close(maildirsize_fd);
+ errno=EIO;
+ return (-1);
+ }
+
+ if (rename(newmaildirsizename, info->maildirsizefile))
+ {
+ unlink(newmaildirsizename);
+ close(maildirsize_fd);
+ errno=EIO;
+ return (-1);
+ }
+
+ info->recalculation_needed=0;
+ info->size.nbytes=maildirsize_size;
+ info->size.nmessages=maildirsize_cnt;
+ info->nlines=1;
+ close(info->fd);
+ info->fd=maildirsize_fd;
+
+ tm=0;
+
+ if (statcurnew(info->maildir, &tm))
+ {
+ errno=EIO;
+ return (-1);
+ }
+
+ dirp=opendir(info->maildir);
+ while (dirp && (de=readdir(dirp)) != 0)
+ {
+ if (statsubdir(info->maildir, de->d_name, &tm))
+ {
+ errno=EIO;
+ closedir(dirp);
+ return (-1);
+ }
+ }
+ if (dirp)
+ {
+#if CLOSEDIR_VOID
+ closedir(dirp);
+#else
+ if (closedir(dirp))
+ {
+ errno=EIO;
+ return (-1);
+ }
+#endif
+ }
+
+ if (tm != maxtime) /* Race condition, someone changed something */
+ {
+ info->recalculation_needed=1;
+ info->nlines=0;
+ errno=EAGAIN;
+ return (-1);
+ }
+
+ *percentage=0;
+
+ new_quota=info->size;
+
+ new_quota.nbytes += xtra_size;
+ new_quota.nmessages += xtra_cnt;
+
+ return checkQuota(&new_quota, &info->quota, percentage);
+}
+
+int maildir_addquota(struct maildirsize *info,
+ int64_t maildirsize_size, int maildirsize_cnt)
+{
+ if (info->fd < 0)
+ return (0);
+
+ return (doaddquota(info, info->fd, maildirsize_size,
+ maildirsize_cnt, 0));
+}
+
+static int doaddquota(struct maildirsize *info, int maildirsize_fd,
+ int64_t maildirsize_size, int maildirsize_cnt,
+ int isnew)
+{
+ char n[NUMBUFSIZE];
+ char buf[NUMBUFSIZE * 4 + 32 ];
+ char *p;
+ int cnt;
+
+ buf[0]=0;
+
+ if (isnew)
+ {
+ if (info->quota.nbytes > 0)
+ {
+ char b[2];
+
+ b[0]=MDQUOTA_SIZE;
+ b[1]=0;
+
+ strcat(strcat(buf, libmail_str_int64_t(info->quota.nbytes, n)),
+ b);
+ }
+
+ if (info->quota.nmessages > 0)
+ {
+ char b[2];
+
+ b[0]=MDQUOTA_COUNT;
+ b[1]=0;
+
+ if (buf[0] != 0)
+ strcat(buf, ",");
+
+ strcat(strcat(buf,
+ libmail_str_size_t(info->quota.nmessages, n)),
+ b);
+ }
+ strcat(buf, "\n");
+ }
+
+ sprintf(buf + strlen(buf),
+ "%12s ", libmail_str_int64_t(maildirsize_size, n));
+
+ sprintf(buf + strlen(buf),
+ "%12s\n", libmail_str_int64_t(maildirsize_cnt, n));
+
+ p=buf;
+ cnt=strlen(buf);
+
+ while (cnt > 0)
+ {
+ int c=write( maildirsize_fd, p, cnt);
+
+ if (c < 0)
+ return (-1);
+
+ cnt -= c;
+ p += c;
+ }
+
+ return (0);
+}
+
+
+/* New maildirsize is built in the tmp subdirectory */
+
+static char *makenewmaildirsizename(const char *dir, int *fd)
+{
+char hostname[256];
+struct stat stat_buf;
+time_t t;
+char *p;
+
+ hostname[0]=0;
+ hostname[sizeof(hostname)-1]=0;
+ gethostname(hostname, sizeof(hostname)-1);
+ p=(char *)malloc(strlen(dir)+strlen(hostname)+130);
+ if (!p) return (0);
+
+ for (;;)
+ {
+ char tbuf[NUMBUFSIZE];
+ char pbuf[NUMBUFSIZE];
+
+ time(&t);
+ strcat(strcpy(p, dir), "/tmp/");
+ sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s",
+ libmail_str_time_t(t, tbuf),
+ libmail_str_pid_t(getpid(), pbuf), hostname);
+
+ if (stat( (const char *)p, &stat_buf) < 0 &&
+ (*fd=maildir_safeopen(p,
+ O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0)
+ break;
+ sleep(3);
+ }
+ return (p);
+}
+
+static int statcurnew(const char *dir, time_t *maxtimestamp)
+{
+char *p=(char *)malloc(strlen(dir)+5);
+struct stat stat_buf;
+
+ if (!p) return (-1);
+ strcat(strcpy(p, dir), "/cur");
+ if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
+ *maxtimestamp=stat_buf.st_mtime;
+ strcat(strcpy(p, dir), "/new");
+ if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
+ *maxtimestamp=stat_buf.st_mtime;
+ free(p);
+ return (0);
+}
+
+static int statsubdir(const char *dir, const char *subdir, time_t *maxtime)
+{
+char *p;
+int n;
+
+ if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
+ strcmp(subdir, "..") == 0
+#ifndef TRASHQUOTA
+ || strcmp(subdir, "." TRASH) == 0
+#endif
+ )
+ return (0);
+
+ p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
+ if (!p) return (-1);
+ strcat(strcat(strcpy(p, dir), "/"), subdir);
+ n=statcurnew(p, maxtime);
+ free(p);
+ return (n);
+}
+
+static int docount(const char *, time_t *, int64_t *, unsigned *);
+
+static int countcurnew(const char *dir, time_t *maxtime,
+ int64_t *sizep, unsigned *cntp)
+{
+char *p=(char *)malloc(strlen(dir)+5);
+int n;
+
+ if (!p) return (-1);
+ strcat(strcpy(p, dir), "/new");
+ n=docount(p, maxtime, sizep, cntp);
+ if (n == 0)
+ {
+ strcat(strcpy(p, dir), "/cur");
+ n=docount(p, maxtime, sizep, cntp);
+ }
+ free(p);
+ return (n);
+}
+
+static int countsubdir(const char *dir, const char *subdir, time_t *maxtime,
+ int64_t *sizep, unsigned *cntp)
+{
+char *p;
+int n;
+
+ if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
+ strcmp(subdir, "..") == 0 ||
+ ! maildirquota_countfolder(subdir))
+ return (0);
+
+ p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
+ if (!p) return (2);
+ strcat(strcat(strcpy(p, dir), "/"), subdir);
+ n=countcurnew(p, maxtime, sizep, cntp);
+ free(p);
+ return (n);
+}
+
+static int docount(const char *dir, time_t *dirstamp,
+ int64_t *sizep, unsigned *cntp)
+{
+struct stat stat_buf;
+char *p;
+DIR *dirp;
+struct dirent *de;
+unsigned long s;
+
+ if (stat(dir, &stat_buf)) return (0); /* Ignore */
+ if (stat_buf.st_mtime > *dirstamp) *dirstamp=stat_buf.st_mtime;
+ if ((dirp=opendir(dir)) == 0) return (0);
+ while ((de=readdir(dirp)) != 0)
+ {
+ const char *n=de->d_name;
+
+ if (*n == '.') continue;
+
+ if (!maildirquota_countfile(n))
+ continue;
+
+ if (maildir_parsequota(n, &s) == 0)
+ stat_buf.st_size=s;
+ else
+ {
+ p=(char *)malloc(strlen(dir)+strlen(n)+2);
+ if (!p)
+ {
+ closedir(dirp);
+ return (-1);
+ }
+ strcat(strcat(strcpy(p, dir), "/"), n);
+ if (stat(p, &stat_buf))
+ {
+ free(p);
+ continue;
+ }
+ free(p);
+ }
+ *sizep += stat_buf.st_size;
+ ++*cntp;
+ }
+
+#if CLOSEDIR_VOID
+ closedir(dirp);
+#else
+ if (closedir(dirp))
+ return (-1);
+#endif
+ return (0);
+}
+
+int maildirquota_countfolder(const char *folder)
+{
+#ifdef TRASHQUOTA
+
+#else
+
+ if (strcmp(folder, "." TRASH) == 0 ||
+ strcmp(folder, "." TRASH "/") == 0)
+ return (0);
+
+ for ( ; *folder; folder++)
+ if (*folder == '/' &&
+ (strcmp(folder+1, "." TRASH) == 0 ||
+ strcmp(folder+1, "." TRASH "/") == 0))
+ return (0);
+#endif
+ return (1);
+}
+
+int maildirquota_countfile(const char *n)
+{
+#ifdef TRASHQUOTA
+
+#else
+ const char *nn=strrchr(n, '/');
+
+ if (nn != NULL)
+ n=nn+1;
+
+ /* do not count msgs marked as deleted */
+
+ for ( ; *n; n++)
+ {
+ if (n[0] != MDIRSEP[0] || n[1] != '2' ||
+ n[2] != ',') continue;
+ n += 3;
+ while (*n >= 'A' && *n <= 'Z')
+ {
+ if (*n == 'T') return (0);
+ ++n;
+ }
+ break;
+ }
+#endif
+ return (1);
+}
+
+/*
+** Prepare to add something to the maildir
+*/
+
+int maildir_quota_add_start(const char *maildir,
+ struct maildirsize *info,
+ int64_t msgsize, int nmsgs,
+ const char *newquota)
+{
+ struct maildirquota mq;
+ int i;
+
+ if ( maildir_openquotafile(info, maildir))
+ info->fd= -1;
+
+ if (newquota != NULL)
+ {
+ parsequotastr(newquota, &mq);
+
+ if ((mq.nbytes > 0 || mq.nmessages > 0) &&
+ (info->fd < 0 || info->quota.nbytes != mq.nbytes ||
+ info->quota.nmessages != mq.nmessages))
+ {
+ if (info->fd < 0)
+ {
+ maildir_quota_set(maildir, newquota);
+ if (maildir_openquotafile(info, maildir))
+ info->fd= -1;
+ }
+ else
+ {
+ info->quota=mq;
+ info->recalculation_needed=1;
+ }
+ }
+ }
+ if (info->fd < 0)
+ return (0); /* No quota set on this maildir */
+
+ for (i=0; i<5; i++)
+ {
+ int rc;
+
+ rc=maildir_checkquota(info, msgsize, nmsgs);
+ if (rc == 0)
+ return (0);
+
+ if (errno != EAGAIN)
+ {
+ maildir_closequotafile(info);
+ return (-1);
+ }
+ }
+ maildir_closequotafile(info);
+
+ /* Cannot recover from a race condition, just punt */
+
+ return (0);
+}
+
+void maildir_quota_add_end(struct maildirsize *info,
+ int64_t msgsize, int nmsgs)
+{
+ maildir_addquota(info, msgsize, nmsgs);
+ maildir_closequotafile(info);
+}
+
+void maildir_quota_deleted(const char *maildir,
+ int64_t nbytes, /* Must be negative */
+ int nmsgs) /* Must be negative */
+{
+ struct maildirsize info;
+
+ if ( maildir_openquotafile(&info, maildir))
+ return;
+
+ maildir_checkquota(&info, nbytes, nmsgs); /* Cleanup */
+ maildir_addquota(&info, nbytes, nmsgs);
+ maildir_closequotafile(&info);
+}
+
+void maildir_quota_recalculate(const char *maildir)
+{
+ struct maildirsize info;
+
+ if (maildir_openquotafile(&info, maildir))
+ return;
+ info.recalculation_needed=1;
+
+ maildir_readquota(&info);
+ maildir_closequotafile(&info);
+}
+
+int maildir_quota_delundel_start(const char *maildir,
+ struct maildirsize *info,
+ int64_t msgsize, int nmsgs)
+{
+#if TRASHQUOTA
+ return (0);
+#else
+ if (nmsgs < 0)
+ {
+ maildir_quota_deleted(maildir, msgsize, nmsgs);
+ return (0); /* Always allowed */
+ }
+
+ return maildir_quota_add_start(maildir, info, msgsize, nmsgs, NULL);
+#endif
+}
+
+void maildir_quota_delundel_end(struct maildirsize *info,
+ int64_t msgsize, int nmsgs)
+{
+#if TRASHQUOTA
+ return;
+#else
+ if (nmsgs < 0)
+ return;
+
+ maildir_quota_add_end(info, msgsize, nmsgs);
+#endif
+}
+
+
+void maildir_quota_set(const char *dir, const char *quota)
+{
+ struct maildirsize info;
+
+ if (maildir_openquotafile_init(&info, dir, quota) == 0)
+ {
+ maildir_checkquota(&info, 0, 0);
+ maildir_closequotafile(&info);
+ }
+}
+
+
+static void do_deliver_warning(const char *msgfile, const char *dir)
+{
+ int fdin, fd, ret;
+ FILE *fpout;
+ time_t t;
+ size_t l, msg_len;
+ char *qname = 0;
+ struct stat sb;
+ char hostname[256];
+ char buf[4096];
+ size_t n;
+ struct maildirsize info;
+ struct maildir_tmpcreate_info createInfo;
+
+ fdin=-1;
+
+ if (msgfile && *msgfile)
+ fdin=open(msgfile, O_RDONLY);
+
+ if (fdin < 0)
+ {
+ msgfile=QUOTAWARNMSG;
+ fdin=open(msgfile, O_RDONLY);
+ }
+
+ if (fdin < 0)
+ return;
+
+ l = strlen(dir)+sizeof("/quotawarn");
+
+ /* Send only one warning every 24 hours */
+ if ((qname = malloc(l)) == 0)
+ {
+ close(fdin);
+ return;
+ }
+
+ strcat(strcpy(qname, dir), "/quotawarn");
+ time(&t);
+ ret = stat(qname, &sb);
+ if ((ret == -1 && errno != ENOENT) ||
+ (ret == 0 && (sb.st_mtime + 86400) > t))
+ {
+ free(qname);
+ close(fdin);
+ return;
+ }
+
+ fd = open(qname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ free(qname);
+ if (fd == -1)
+ {
+ close(fdin);
+ return;
+ }
+ if (write(fd, buf, 1) < 0)
+ perror(msgfile);
+
+ close(fd);
+
+ strcpy(buf, "Date: ");
+ rfc822_mkdate_buf(t, buf+strlen(buf));
+ strcat(buf, "\n");
+
+ hostname[0]=0;
+ hostname[sizeof(hostname)-1]=0;
+ gethostname(hostname, sizeof(hostname)-1);
+ sprintf(buf+strlen(buf), "Message-Id: <%lu.overquota@%-1.256s>\n",
+ (unsigned long)t, hostname);
+
+ if (fstat(fdin, &sb) < 0) {
+ close(fdin);
+ return;
+ }
+ msg_len=strlen(buf)+sb.st_size;
+
+
+
+ maildir_tmpcreate_init(&createInfo);
+ createInfo.maildir=dir;
+ createInfo.uniq="warn";
+ createInfo.msgsize=msg_len;
+ createInfo.doordie=1;
+
+ if ((fpout=maildir_tmpcreate_fp(&createInfo)) == NULL)
+ {
+ close(fdin);
+ return;
+ }
+
+ fprintf(fpout, "%s", buf);
+
+ while ((n=read(fdin, buf, sizeof(buf))) > 0)
+ {
+ if (fwrite(buf, n, 1, fpout) != 1)
+ {
+ perror(createInfo.tmpname);
+ break;
+ }
+ }
+ close(fdin);
+
+ if (fflush(fpout) || ferror(fpout))
+ {
+ fclose(fpout);
+ unlink(createInfo.tmpname);
+ maildir_tmpcreate_free(&createInfo);
+ return;
+ }
+
+ if (fseek(fpout, 0L, SEEK_SET) >= 0)
+ {
+ /* Make sure the quota message's size itself is factored into
+ ** the quota. Deliver the message regardless of whether the
+ ** user is over quota.
+ */
+ if (maildirquota_countfolder(dir))
+ {
+ maildir_quota_add_start(dir, &info, msg_len, 1, NULL);
+ maildir_quota_add_end(&info, msg_len, 1);
+ }
+ }
+
+ fclose(fpout);
+ if (maildir_movetmpnew(createInfo.tmpname,
+ createInfo.newname))
+ {
+ unlink(createInfo.tmpname);
+ maildir_tmpcreate_free(&createInfo);
+ return;
+ }
+ maildir_tmpcreate_free(&createInfo);
+}
+
+void maildir_deliver_quota_warning(const char *dir, const int percent,
+ const char *msgquotafile)
+{
+ size_t l;
+ char *p;
+ struct stat sb;
+
+ /* If we delivered to a folder, dump the warning message into INBOX */
+
+ l = strlen(dir)+sizeof("/maildirfolder");
+ if ((p = malloc(l)) == 0)
+ return;
+
+ strcat(strcpy(p, dir), "/maildirfolder");
+
+ /* If delivering to a folder, find quotawarn in its parent directory */
+
+ if (stat(p, &sb) == 0)
+ {
+ strcat(strcpy(p, dir), "/..");
+ maildir_deliver_quota_warning(p, percent, msgquotafile);
+ free(p);
+ return;
+ }
+ free(p);
+
+ if (percent >= 0)
+ {
+ struct maildirsize info;
+
+ if (maildir_openquotafile(&info, dir) == 0)
+ {
+ if (maildir_readquota(&info) >= percent)
+ {
+ maildir_closequotafile(&info);
+ do_deliver_warning(msgquotafile, dir);
+ return;
+ }
+ maildir_closequotafile(&info);
+ }
+ }
+}