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 /maildrop/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 'maildrop/maildirquota.c')
| -rw-r--r-- | maildrop/maildirquota.c | 613 | 
1 files changed, 613 insertions, 0 deletions
| diff --git a/maildrop/maildirquota.c b/maildrop/maildirquota.c new file mode 100644 index 0000000..07e99e9 --- /dev/null +++ b/maildrop/maildirquota.c @@ -0,0 +1,613 @@ +#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	<sys/uio.h> + +#include	"maildirquota.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	<numlib/numlib.h> + + +/* Read the maildirsize file */ + +static int maildirsize_read(const char *filename,	/* The filename */ +	int *fdptr,	/* Keep the file descriptor open */ +	off_t *sizeptr,	/* Grand total of maildir size */ +	unsigned *cntptr, /* Grand total of message count */ +	unsigned *nlines, /* # of lines in maildirsize */ +	struct stat *statptr)	/* The stats on maildirsize */ +{ +char buf[5120]; +int f; +char *p; +unsigned l; +int n; +int first; + +	if ((f=open(filename, O_RDWR|O_APPEND)) < 0)	return (-1); +	p=buf; +	l=sizeof(buf); + +	while (l) +	{ +		n=read(f, p, l); +		if (n < 0) +		{ +			close(f); +			return (-1); +		} +		if (n == 0)	break; +		p += n; +		l -= n; +	} +	if (l == 0 || fstat(f, statptr))	/* maildir too big */ +	{ +		close(f); +		return (-1); +	} + +	*sizeptr=0; +	*cntptr=0; +	*nlines=0; +	*p=0; +	p=buf; +	first=1; +	while (*p) +	{ +	long n=0; +	int c=0; +	char	*q=p; + +		while (*p) +			if (*p++ == '\n') +			{ +				p[-1]=0; +				break; +			} + +		if (first) +		{ +			first=0; +			continue; +		} +		sscanf(q, "%ld %d", &n, &c); +		*sizeptr += n; +		*cntptr += c; +		++ *nlines; +	} +	*fdptr=f; +	return (0); +} + +static char *makenewmaildirsizename(const char *, int *); +static int countcurnew(const char *, time_t *, off_t *, unsigned *); +static int countsubdir(const char *, const char *, +		time_t *, off_t *, unsigned *); +static int statcurnew(const char *, time_t *); +static int statsubdir(const char *, const char *, time_t *); + +#define	MDQUOTA_SIZE	'S'	/* Total size of all messages in maildir */ +#define	MDQUOTA_BLOCKS	'B'	/* Total # of blocks for all messages in +				maildir -- NOT IMPLEMENTED */ +#define	MDQUOTA_COUNT	'C'	/* Total number of messages in maildir */ + +static int qcalc(off_t s, unsigned n, const char *quota) +{ +unsigned long i; + +	errno=ENOSPC; +	while (quota && *quota) +	{ +		if (*quota < '0' || *quota > '9') +		{ +			++quota; +			continue; +		} +		i=0; +		while (*quota >= '0' && *quota <= '9') +			i=i*10 + (*quota++ - '0'); +		switch (*quota)	{ +		default: +			if (i < s)	return (-1); +			break; +		case 'C': +			if (i < n)	return (-1); +			break; +		} +	} +	return (0); +} + +static int	doaddquota(const char *, int, const char *, long, int, int); + +int maildir_checkquota(const char *dir, +	int *maildirsize_fdptr, +	const char *quota_type, +	long xtra_size, +	int xtra_cnt) +{ +char	*checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); +char	*newmaildirsizename; +struct stat stat_buf; +int	maildirsize_fd; +off_t	maildirsize_size; +unsigned maildirsize_cnt; +unsigned maildirsize_nlines; +int	n; +time_t	tm; +time_t	maxtime; +DIR	*dirp; +struct dirent *de; + +	if (checkfolder == 0)	return (-1); +	*maildirsize_fdptr= -1; +	strcat(strcpy(checkfolder, dir), "/maildirfolder"); +	if (stat(checkfolder, &stat_buf) == 0)	/* Go to parent */ +	{ +		strcat(strcpy(checkfolder, dir), "/.."); +		n=maildir_checkquota(checkfolder, maildirsize_fdptr, +			quota_type, xtra_size, xtra_cnt); +		free(checkfolder); +		return (n); +	} +	if (!quota_type || !*quota_type)	return (0); + +	strcat(strcpy(checkfolder, dir), "/maildirsize"); +	time(&tm); +	if (maildirsize_read(checkfolder, &maildirsize_fd, +		&maildirsize_size, &maildirsize_cnt, +		&maildirsize_nlines, &stat_buf) == 0) +	{ +		n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, +			quota_type); + +		if (n == 0) +		{ +			free(checkfolder); +			*maildirsize_fdptr=maildirsize_fd; +			return (0); +		} +		close(maildirsize_fd); + +		if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60) +			return (n); +	} + +	maxtime=0; +	maildirsize_size=0; +	maildirsize_cnt=0; + +	if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt)) +	{ +		free(checkfolder); +		return (-1); +	} + +	dirp=opendir(dir); +	while (dirp && (de=readdir(dirp)) != 0) +	{ +		if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size, +			&maildirsize_cnt)) +		{ +			free(checkfolder); +			closedir(dirp); +			return (-1); +		} +	} +	if (dirp) +	{ +#if	CLOSEDIR_VOID +		closedir(dirp); +#else +		if (closedir(dirp)) +		{ +			free(checkfolder); +			return (-1); +		} +#endif +	} + +	newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); +	if (!newmaildirsizename) +	{ +		free(checkfolder); +		return (-1); +	} + +	*maildirsize_fdptr=maildirsize_fd; + +	if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, +		maildirsize_cnt, 1)) +	{ +		free(newmaildirsizename); +		unlink(newmaildirsizename); +		close(maildirsize_fd); +		*maildirsize_fdptr= -1; +		free(checkfolder); +		return (-1); +	} + +	strcat(strcpy(checkfolder, dir), "/maildirsize"); + +	if (rename(newmaildirsizename, checkfolder)) +	{ +		free(checkfolder); +		unlink(newmaildirsizename); +		close(maildirsize_fd); +		*maildirsize_fdptr= -1; +	} +	free(checkfolder); +	free(newmaildirsizename); + +	tm=0; + +	if (statcurnew(dir, &tm)) +	{ +		close(maildirsize_fd); +		*maildirsize_fdptr= -1; +		return (-1); +	} + +	dirp=opendir(dir); +	while (dirp && (de=readdir(dirp)) != 0) +	{ +		if (statsubdir(dir, de->d_name, &tm)) +		{ +			close(maildirsize_fd); +			*maildirsize_fdptr= -1; +			closedir(dirp); +			return (-1); +		} +	} +	if (dirp) +	{ +#if	CLOSEDIR_VOID +		closedir(dirp); +#else +		if (closedir(dirp)) +		{ +			close(maildirsize_fd); +			*maildirsize_fdptr= -1; +			return (-1); +		} +#endif +	} + +	if (tm != maxtime)	/* Race condition, someone changed something */ +	{ +		errno=EAGAIN; +		return (-1); +	} + +	return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, +		quota_type)); +} + +int	maildir_addquota(const char *dir, int maildirsize_fd, +	const char *quota_type, long maildirsize_size, int maildirsize_cnt) +{ +	if (!quota_type || !*quota_type)	return (0); +	return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, +			maildirsize_cnt, 0)); +} + +static int doaddquota(const char *dir, int maildirsize_fd, +	const char *quota_type, long maildirsize_size, int maildirsize_cnt, +	int isnew) +{ +union	{ +	char	buf[100]; +	struct stat stat_buf; +	} u;				/* Scrooge */ +char	*newname2=0; +char	*newmaildirsizename=0; +struct	iovec	iov[3]; +int	niov; +struct	iovec	*p; +int	n; + +	niov=0; +	if ( maildirsize_fd < 0) +	{ +		newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); +		if (!newname2)	return (-1); +		strcat(strcpy(newname2, dir), "/maildirfolder"); +		if (stat(newname2, &u.stat_buf) == 0) +		{ +			strcat(strcpy(newname2, dir), "/.."); +			n=doaddquota(newname2, maildirsize_fd, quota_type, +					maildirsize_size, maildirsize_cnt, +					isnew); +			free(newname2); +			return (n); +		} + +		strcat(strcpy(newname2, dir), "/maildirsize"); + +		if ((maildirsize_fd=open(newname2, O_RDWR|O_APPEND, 0644)) < 0) +		{ +			newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); +			if (!newmaildirsizename) +			{ +				free(newname2); +				return (-1); +			} + +			maildirsize_fd=open(newmaildirsizename, +				O_CREAT|O_RDWR|O_APPEND, 0644); + +			if (maildirsize_fd < 0) +			{ +				free(newname2); +				return (-1); +			} +			isnew=1; +		} +	} + +	if (isnew) +	{ +		iov[0].iov_base=(caddr_t)quota_type; +		iov[0].iov_len=strlen(quota_type); +		iov[1].iov_base=(caddr_t)"\n"; +		iov[1].iov_len=1; +		niov=2; +	} + + +	sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt); +	iov[niov].iov_base=(caddr_t)u.buf; +	iov[niov].iov_len=strlen(u.buf); + +	p=iov; +	++niov; +	n=0; +	while (niov) +	{ +		if (n) +		{ +			if (n < p->iov_len) +			{ +				p->iov_base= +					(caddr_t)((char *)p->iov_base + n); +				p->iov_len -= n; +			} +			else +			{ +				n -= p->iov_len; +				++p; +				--niov; +				continue; +			} +		} + +		n=writev( maildirsize_fd, p, niov); + +		if (n <= 0) +		{ +			if (newname2) +			{ +				close(maildirsize_fd); +				free(newname2); +			} +			return (-1); +		} +	} +	if (newname2) +	{ +		close(maildirsize_fd); + +		if (newmaildirsizename) +		{ +			rename(newmaildirsizename, newname2); +			free(newmaildirsizename); +		} +		free(newname2); +	} +	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 && +			errno == ENOENT && +			(*fd=open(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 || strcmp(subdir, ".Trash") == 0) +		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 *, off_t *, unsigned *); + +static int countcurnew(const char *dir, time_t *maxtime, +	off_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, +	off_t *sizep, unsigned *cntp) +{ +char	*p; +int	n; + +	if ( *subdir != '.' || strcmp(subdir, ".") == 0 || +		strcmp(subdir, "..") == 0 || strcmp(subdir, ".Trash") == 0) +		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, +	off_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; + +		/* PATCH - do not count msgs marked as deleted */ + +		for ( ; *n; n++) +		{ +			if (n[0] != ':' || n[1] != '2' || +				n[2] != ',')	continue; +			n += 3; +			while (*n >= 'A' && *n <= 'Z') +			{ +				if (*n == 'T')	break; +				++n; +			} +			break; +		} +		if (*n == 'T')	continue; +		n=de->d_name; + + +		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); +} | 
