Since there's been alot of hype about domain quotas, I've put my changes in the attached patch file. This will patch vpopmail-5.3.16 (maildirquota.c and vdelivermail.c). There's a new file vqmaillocal.c that apparently doesn't use Maildir++ quotas, so I didn't touch that.
The CPU usage is negligible (I saw 0-20 ms). With this patch, another function is added to maildirquota.c called domain_over_maildirquota() that extracts the domain info from the given end-user Maildir and returns 1 if the domain is already at/over quota, or the new message would exceed the domains quota. Note that this patch *only* enforces Maildir++ quotas (both size and count), and not system quotas. The diskquota parameter of the vlimits structure is in MB (NOT BYTES), and the maxmsgcount parameter specifies the max messages for the whole domain. If either value is less than or equal to 0, it is treated as unlimited. Ken/Bill - I've been running this for about a month now with the published vlimits API and it seems to work well with little CPU overhead. All deliveries still seem to be under 1 second. I've stripped out the system quotas implementation and made it simply Maildir++ quota compliant. Thanks, Brian
Common subdirectories: vpopmail-5.3.16/attic and vpopmail-5.3.16.new/attic Common subdirectories: vpopmail-5.3.16/cdb and vpopmail-5.3.16.new/cdb Common subdirectories: vpopmail-5.3.16/contrib and vpopmail-5.3.16.new/contrib Common subdirectories: vpopmail-5.3.16/convert and vpopmail-5.3.16.new/convert Common subdirectories: vpopmail-5.3.16/doc and vpopmail-5.3.16.new/doc Common subdirectories: vpopmail-5.3.16/ldap and vpopmail-5.3.16.new/ldap diff -c vpopmail-5.3.16/maildirquota.c vpopmail-5.3.16.new/maildirquota.c *** vpopmail-5.3.16/maildirquota.c Wed Oct 23 15:53:36 2002 --- vpopmail-5.3.16.new/maildirquota.c Wed Feb 19 17:55:36 2003 *************** *** 29,34 **** --- 29,35 ---- #include <errno.h> #include <time.h> #include <sys/uio.h> + #include "vlimits.h" #include "maildirquota.h" #include "config.h" *************** *** 46,51 **** --- 47,54 ---- long xtra_size, int xtra_cnt, int *percentage); static int docount(const char *, time_t *, off_t *, unsigned *); + static int domreadquota(const char *dir, long *sizep, int *cntp); + static int readuserquota(const char* dir, long *sizep, int *cntp); int deliver_quota_warning(const char *dir); #define NUMBUFSIZE 60 *************** *** 55,60 **** --- 58,253 ---- #define MDQUOTA_COUNT 'C' /* Total number of messages in maildir */ + /* bk: add domain limits functionality */ + int domain_over_maildirquota(const char *userdir) + { + struct stat stat_buf; + int ret_value = 0; + char *domdir=(char *)malloc(strlen(userdir)+1); + char *p; + char domain[256]; + unsigned long size = 0; + unsigned long maxsize = 0; + int cnt = 0; + int maxcnt = 0; + struct vlimits limits; + + if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) && + stat_buf.st_size > 0) + { + + /* locate the domain directory */ + strcpy(domdir, userdir); + if ((p = strstr(domdir, "/Maildir/")) != NULL) + { + while (*(--p) != '/') + ; + *(p+1) = '\0'; + } + + /* locate the domainname */ + while (*(--p) != '/') + ; + strncpy(domain, ++p, sizeof(domain)); + if ((p = strchr(domain, '/')) != NULL) + *p = '\0'; + + /* get the domain quota */ + if (vget_limits(domain, &limits)) + { + free(domdir); + return 0; + } + + /* convert from MB to bytes */ + maxsize = limits.diskquota * 1024 * 1024; + maxcnt = limits.maxmsgcount; + + /* get the domain usage */ + if (domreadquota(domdir, &size, &cnt)) + { + free(domdir); + return -1; + } + + /* check if either quota (size/count) would be exceeded */ + if (maxsize > 0 && (size + stat_buf.st_size) > maxsize) + { + ret_value = 1; + } + else if (maxcnt > 0 && cnt >= maxcnt) + { + ret_value = 1; + } + } + + free(domdir); + + return(ret_value); + } + + static int domreadquota(const char *dir, long *sizep, int *cntp) + { + int tries; + char checkdir[256]; + DIR *dirp; + struct dirent *de; + + + if (dir == NULL || sizep == NULL || cntp == NULL) + return -1; + + *sizep = 0; + *cntp = 0; + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + strncpy(checkdir, dir, sizeof(checkdir)); + strncat(checkdir, de->d_name, sizeof(checkdir)); + strncat(checkdir, "/Maildir/", sizeof(checkdir)); + tries = 5; + while (tries-- && readuserquota(checkdir, sizep, cntp)) + { + if (errno != EAGAIN) + return -1; + sleep(1); + } + if (tries <= 0) + return -1; + } + if (dirp) + { + #if CLOSEDIR_VOID + closedir(dirp); + #else + if (closedir(dirp)) + { + return (-1); + } + #endif + } + + return 0; + } + + static int readuserquota(const char* dir, long *sizep, int *cntp) + { + time_t tm; + time_t maxtime; + DIR *dirp; + struct dirent *de; + + maxtime=0; + + if (countcurnew(dir, &maxtime, sizep, cntp)) + { + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (countsubdir(dir, de->d_name, &maxtime, sizep, cntp)) + { + closedir(dirp); + return (-1); + } + } + if (dirp) + { + #if CLOSEDIR_VOID + closedir(dirp); + #else + if (closedir(dirp)) + { + return (-1); + } + #endif + } + + /* make sure nothing changed while calculating this... */ + tm=0; + + if (statcurnew(dir, &tm)) + { + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (statsubdir(dir, de->d_name, &tm)) + { + closedir(dirp); + return (-1); + } + } + if (dirp) + { + #if CLOSEDIR_VOID + closedir(dirp); + #else + if (closedir(dirp)) + { + return (-1); + } + #endif + } + + if (tm != maxtime) /* Race condition, someone changed something */ + { + errno=EAGAIN; + return (-1); + } + errno=0; + + return 0; + } + int user_over_maildirquota( const char *dir, const char *q) { struct stat stat_buf; Common subdirectories: vpopmail-5.3.16/oracle and vpopmail-5.3.16.new/oracle diff -c vpopmail-5.3.16/vdelivermail.c vpopmail-5.3.16.new/vdelivermail.c *** vpopmail-5.3.16/vdelivermail.c Wed Oct 23 16:42:36 2002 --- vpopmail-5.3.16.new/vdelivermail.c Wed Feb 19 15:56:58 2003 *************** *** 93,102 **** /* functions in maildirquota.c */ int deliver_quota_warning(const char *dir, const char *q); int user_over_maildirquota(char *address, char *quota); void add_warningsize_to_quota( const char *dir, const char *q); char *format_maildirquota(const char *q); - static char local_file[156]; static char local_file_new[156]; --- 93,102 ---- /* functions in maildirquota.c */ int deliver_quota_warning(const char *dir, const char *q); int user_over_maildirquota(char *address, char *quota); + int domain_over_maildirquota(char *userdir); void add_warningsize_to_quota( const char *dir, const char *q); char *format_maildirquota(const char *q); static char local_file[156]; static char local_file_new[156]; *************** *** 494,499 **** --- 494,522 ---- } } + /* bk: check domain quota */ + if (domain_over_maildirquota(address)==1) + { + /* check for over quota message in domain */ + sprintf(tmp_file, "%s/.over-quota.msg",TheDomainDir); + if ( (fs=fopen(tmp_file, "r")) == NULL ) { + /* if no domain over quota then check in vpopmail dir */ + sprintf(tmp_file, "%s/domains/.over-quota.msg",VPOPMAILDIR); + fs=fopen(tmp_file, "r"); + } + + if ( fs == NULL ) { + printf("domain is over quota\n"); + } else { + while( fgets( tmp_file, 256, fs ) != NULL ) { + fputs(tmp_file, stdout); + } + fclose(fs); + } + deliver_quota_warning(address, format_maildirquota(quota)); + return(-1); + } + /* Format the email file name */ gethostname(hostname,sizeof(hostname)); pid=getpid();