diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 | 
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 | 
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /maildir/maildirquota.c | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-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.c | 1157 | 
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); +		} +	} +} | 
