I've made one change to the patch below, which is the same one I posted yesterday.
This patch makes public the following two functions in maildirquota.c: int readdomainquota(const char *dir, long *sizep, int *cntp); int readuserquota(const char* dir, long *sizep, int *cntp); These two functions read the current Maildir++ quota usage for a domain/user given the domain or user directory. It *adds* to the current values pointed to by sizep & cntp the size/msgcount used by the specified domain/user. They return 0 on success, -1 on failure. errno would be set to EAGAIN if the contents of the directory changed during the calculation. In short, the attached patch enforces the quota limits specified in the qmailadmin-limits file/table in the vdelivermail program. I figured it might be better to expose the above two functions as well, therefore the updated patch. Thanks, Brian ------------- Begin Forwarded Message ------------- 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 ------------- End Forwarded Message -------------
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 *); + int readdomainquota(const char *dir, long *sizep, int *cntp); + 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 (readdomainquota(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); + } + + int readdomainquota(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; + } + + 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();