From 5d47a7101a2394c7f7bb288585464fa293a2ad33 Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 4 Feb 2013 02:48:34 +0100 Subject: [PATCH] maildir++ for qmail-local and qmail-pop3d Patch to make qmail-local and qmail-pop3d compatible with the maildir++ quota system that is used by vpopmail and courier-imap qmail-maildir++ --- Makefile | 45 +++- TARGETS | 8 + maildirflags.c | 23 ++ maildirgetquota.c | 50 ++++ maildirgetquota.h | 30 +++ maildirmisc.h | 145 +++++++++++ maildiropen.c | 133 ++++++++++ maildirparsequota.c | 44 ++++ maildirquota.c | 685 ++++++++++++++++++++++++++++++++++++++++++++++++++++ maildirquota.h | 45 ++++ numlib.h | 45 ++++ overmaildirquota.c | 43 ++++ qmail-local.c | 13 +- qmail-pop3d.c | 75 ++++-- strpidt.c | 26 ++ strtimet.c | 26 ++ 16 files changed, 1413 insertions(+), 23 deletions(-) create mode 100644 maildirflags.c create mode 100644 maildirgetquota.c create mode 100644 maildirgetquota.h create mode 100644 maildirmisc.h create mode 100644 maildiropen.c create mode 100644 maildirparsequota.c create mode 100644 maildirquota.c create mode 100644 maildirquota.h create mode 100644 numlib.h create mode 100644 overmaildirquota.c create mode 100644 strpidt.c create mode 100644 strtimet.c diff --git a/Makefile b/Makefile index 1000b58..5f676dd 100644 --- a/Makefile +++ b/Makefile @@ -894,6 +894,38 @@ datetime.h gen_alloc.h stralloc.h gen_alloc.h str.h exit.h hfield.h \ readwrite.h open.h headerbody.h maildir.h strerr.h ./compile maildirwatch.c +maildirgetquota.o: \ +compile maildirgetquota.c maildirgetquota.h maildirmisc.h + ./compile maildirgetquota.c + +maildirflags.o: \ +compile maildirflags.c + ./compile maildirflags.c + +maildiropen.o: \ +compile maildiropen.c maildirmisc.h + ./compile maildiropen.c + +maildirparsequota.o: \ +compile maildirparsequota.c + ./compile maildirparsequota.c + +maildirquota.o: \ +compile maildirquota.c maildirquota.h maildirmisc.h numlib.h + ./compile maildirquota.c + +overmaildirquota.o: \ +compile overmaildirquota.c + ./compile overmaildirquota.c + +strtimet.o: \ +compile strtimet.c + ./compile strtimet.c + +strpidt.o: \ +compile strpidt.c + ./compile strpidt.c + mailsubj: \ warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split cat warn-auto.sh mailsubj.sh \ @@ -1178,12 +1210,15 @@ qmail-local: \ load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ -fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib +fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib maildirquota.o \ +maildirgetquota.o maildiropen.o maildirparsequota.o overmaildirquota.o \ +strtimet.o strpidt.o ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \ lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \ substdio.a error.a str.a fs.a datetime.a auto_qmail.o \ - auto_patrn.o `cat socket.lib` + auto_patrn.o `cat socket.lib` maildirquota.o maildirgetquota.o \ + maildiropen.o maildirparsequota.o overmaildirquota.o strtimet.o strpidt.o qmail-local.0: \ qmail-local.8 @@ -1273,11 +1308,13 @@ readwrite.h open.h error.h case.h auto_qmail.h qmail-pop3d: \ load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \ maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \ -stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib +stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib maildirquota.o \ +maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o ./load qmail-pop3d commands.o case.a timeoutread.o \ timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \ open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \ - fs.a `cat socket.lib` + fs.a `cat socket.lib` maildirquota.o maildirgetquota.o \ + maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o qmail-pop3d.0: \ qmail-pop3d.8 diff --git a/TARGETS b/TARGETS index 63c8d4c..949bc46 100644 --- a/TARGETS +++ b/TARGETS @@ -16,6 +16,14 @@ myctime.o slurpclose.o make-makelib makelib +maildirflags.o +maildirparsequota.o +maildiropen.o +maildirgetquota.o +maildirquota.o +overmaildirquota.o +strtimet.o +strpidt.o case_diffb.o case_diffs.o case_lowerb.o diff --git a/maildirflags.c b/maildirflags.c new file mode 100644 index 0000000..8ac460c --- /dev/null +++ b/maildirflags.c @@ -0,0 +1,23 @@ +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include +#include + +static const char rcsid[]="$Id: maildirflags.c,v 1.1 2000/10/07 01:10:19 mrsam Exp $"; + +int maildir_hasflag(const char *filename, char flag) +{ + const char *p=strrchr(filename, '/'); + + if (p) + filename=p+1; + + p=strrchr(p, ':'); + if (p && strncmp(p, ":2,", 3) == 0 && + strchr(p+3, flag)) + return (1); + return (0); +} diff --git a/maildirgetquota.c b/maildirgetquota.c new file mode 100644 index 0000000..1c541f8 --- /dev/null +++ b/maildirgetquota.c @@ -0,0 +1,50 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include "maildirgetquota.h" +#include "maildirmisc.h" +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +int maildir_getquota(const char *dir, char buf[QUOTABUFSIZE]) +{ +char *p; +struct stat stat_buf; +int n; +int l; + + p=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); + if (!p) return (-1); + + strcat(strcpy(p, dir), "/maildirfolder"); + if (stat(p, &stat_buf) == 0) + { + strcat(strcpy(p, dir), "/.."); + n=maildir_getquota(p, buf); + free(p); + return (n); + } + + strcat(strcpy(p, dir), "/maildirsize"); + n=maildir_safeopen(p, O_RDONLY, 0); + free(p); + if (n < 0) return (n); + if ((l=read(n, buf, QUOTABUFSIZE-1)) < 0) + { + close(n); + return (-1); + } + close(n); + for (n=0; n +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static const char maildirgetquota_h_rcsid[]="$Id: maildirgetquota.h,v 1.5 1999/12/06 13:21:05 mrsam Exp $"; + +#define QUOTABUFSIZE 256 + +int maildir_getquota(const char *, char [QUOTABUFSIZE]); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/maildirmisc.h b/maildirmisc.h new file mode 100644 index 0000000..86743bc --- /dev/null +++ b/maildirmisc.h @@ -0,0 +1,145 @@ +#ifndef maildirmisc_h +#define maildirmisc_h + +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +static const char maildirmisc_h_rcsid[]="$Id: maildirmisc.h,v 1.8 2000/12/25 17:33:06 mrsam Exp $"; + +/* +** +** Miscellaneous maildir-related code +** +*/ + +/* Some special folders */ + +#define INBOX "INBOX" +#define DRAFTS "Drafts" +#define SENT "Sent" +#define TRASH "Trash" + +#define SHAREDSUBDIR "shared-folders" + +char *maildir_folderdir(const char *, /* maildir */ + const char *); /* folder name */ + /* Returns the directory corresponding to foldername (foldername is + ** checked to make sure that it's a valid name, else we set errno + ** to EINVAL, and return (0). + */ + +char *maildir_filename(const char *, /* maildir */ + const char *, /* folder */ + const char *); /* filename */ + /* + ** Builds the filename to this message, suitable for opening. + ** If the file doesn't appear to be there, search the maildir to + ** see if someone changed the flags, and return the current filename. + */ + +int maildir_safeopen(const char *, /* filename */ + int, /* mode */ + int); /* perm */ + +/* +** Same arguments as open(). When we're accessing a shared maildir, +** prevent someone from playing cute and dumping a bunch of symlinks +** in there. This function will open the indicate file only if the +** last component is not a symlink. +** This is implemented by opening the file with O_NONBLOCK (to prevent +** a DOS attack of someone pointing the symlink to a pipe, causing +** the open to hang), clearing O_NONBLOCK, then stat-int the file +** descriptor, lstating the filename, and making sure that dev/ino +** match. +*/ + +int maildir_semisafeopen(const char *, /* filename */ + int, /* mode */ + int); /* perm */ + +/* +** Same thing, except that we allow ONE level of soft link indirection, +** because we're reading from our own maildir, which points to the +** message in the sharable maildir. +*/ + +int maildir_mkdir(const char *); /* directory */ +/* +** Create maildir including all subdirectories in the path (like mkdir -p) +*/ + +void maildir_purgetmp(const char *); /* maildir */ + /* purges old stuff out of tmp */ + +void maildir_purge(const char *, /* directory */ + unsigned); /* time_t to purge */ + +void maildir_getnew(const char *, /* maildir */ + const char *); /* folder */ + /* move messages from new to cur */ + +int maildir_deletefolder(const char *, /* maildir */ + const char *); /* folder */ + /* deletes a folder */ + +int maildir_mddelete(const char *); /* delete a maildir folder by path */ + +void maildir_list_sharable(const char *, /* maildir */ + void (*)(const char *, void *), /* callback function */ + void *); /* 2nd arg to callback func */ + /* list sharable folders */ + +int maildir_shared_subscribe(const char *, /* maildir */ + const char *); /* folder */ + /* subscribe to a shared folder */ + +void maildir_list_shared(const char *, /* maildir */ + void (*)(const char *, void *), /* callback function */ + void *); /* 2nd arg to the callback func */ + /* list subscribed folders */ + +int maildir_shared_unsubscribe(const char *, /* maildir */ + const char *); /* folder */ + /* unsubscribe from a shared folder */ + +char *maildir_shareddir(const char *, /* maildir */ + const char *); /* folder */ + /* + ** Validate and return a path to a shared folder. folderdir must be + ** a name of a valid shared folder. + */ + +void maildir_shared_sync(const char *); /* maildir */ + /* "sync" the shared folder */ + +int maildir_sharedisro(const char *); /* maildir */ + /* maildir is a shared read-only folder */ + +int maildir_unlinksharedmsg(const char *); /* filename */ + /* Remove a message from a shared folder */ + +/* Internal function that reads a symlink */ + +char *maildir_getlink(const char *); + + /* Determine whether the maildir filename has a certain flag */ + +int maildir_hasflag(const char *filename, char); + +#define MAILDIR_DELETED(f) maildir_hasflag((f), 'T') + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/maildiropen.c b/maildiropen.c new file mode 100644 index 0000000..bcaadd4 --- /dev/null +++ b/maildiropen.c @@ -0,0 +1,133 @@ +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include + +#include "maildirmisc.h" + +static const char rcsid[]="$Id: maildiropen.c,v 1.7 2000/12/10 04:43:44 mrsam Exp $"; + +char *maildir_getlink(const char *filename) +{ +#if HAVE_READLINK +size_t bufsiz; +char *buf; + + bufsiz=0; + buf=0; + + for (;;) + { + int n; + + if (buf) free(buf); + bufsiz += 256; + if ((buf=malloc(bufsiz)) == 0) + { + perror("malloc"); + return (0); + } + if ((n=readlink(filename, buf, bufsiz)) < 0) + { + free(buf); + return (0); + } + if (n < bufsiz) + { + buf[n]=0; + break; + } + } + return (buf); +#else + return (0); +#endif +} + +int maildir_semisafeopen(const char *path, int mode, int perm) +{ + +#if HAVE_READLINK + +char *l=maildir_getlink(path); + + if (l) + { + int f; + + if (*l != '/') + { + char *q=malloc(strlen(path)+strlen(l)+2); + char *s; + + if (!q) + { + free(l); + return (-1); + } + + strcpy(q, path); + if ((s=strchr(q, '/')) != 0) + s[1]=0; + else *q=0; + strcat(q, l); + free(l); + l=q; + } + + f=maildir_safeopen(l, mode, perm); + + free(l); + return (f); + } +#endif + + return (maildir_safeopen(path, mode, perm)); +} + +int maildir_safeopen(const char *path, int mode, int perm) +{ +struct stat stat1, stat2; + +int fd=open(path, mode +#ifdef O_NONBLOCK + | O_NONBLOCK +#else + | O_NDELAY +#endif + , perm); + + if (fd < 0) return (fd); + if (fcntl(fd, F_SETFL, (mode & O_APPEND)) || fstat(fd, &stat1) + || lstat(path, &stat2)) + { + close(fd); + return (-1); + } + + if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino) + { + close(fd); + errno=ENOENT; + return (-1); + } + + return (fd); +} diff --git a/maildirparsequota.c b/maildirparsequota.c new file mode 100644 index 0000000..35e7d99 --- /dev/null +++ b/maildirparsequota.c @@ -0,0 +1,44 @@ +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "maildirquota.h" +#include +#include + +static const char rcsid[]="$Id: maildirparsequota.c,v 1.2 1999/12/06 13:21:05 mrsam Exp $"; + +int maildir_parsequota(const char *n, unsigned long *s) +{ +const char *o; +int yes; + + if ((o=strrchr(n, '/')) == 0) o=n; + + for (; *o; o++) + if (*o == ':') break; + yes=0; + for ( ; o >= n; --o) + { + if (*o == '/') break; + + if (*o == ',' && o[1] == 'S' && o[2] == '=') + { + yes=1; + o += 3; + break; + } + } + if (yes) + { + *s=0; + while (*o >= '0' && *o <= '9') + *s= *s*10 + (*o++ - '0'); + return (0); + } + return (-1); +} diff --git a/maildirquota.c b/maildirquota.c new file mode 100644 index 0000000..e9ba47b --- /dev/null +++ b/maildirquota.c @@ -0,0 +1,685 @@ +/* +** Copyright 1998 - 2002 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +/* #if HAVE_DIRENT_H */ +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +/* #else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#if HAVE_SYS_NDIR_H +#include +#endif +#if HAVE_SYS_DIR_H +#include +#endif +#if HAVE_NDIR_H +#include +#endif +#endif */ +#include +/* #if HAVE_SYS_STAT_H */ +#include +/* #endif */ +#include + +#include "maildirquota.h" +#include "maildirmisc.h" +#include +#include +#include +#include +/* #if HAVE_FCNTL_H */ +#include +/* #endif */ +#if HAVE_UNISTD_H +#include +#endif +#include +#include "numlib.h" + +static const char rcsid[]="$Id: maildirquota.c,v 1.9 2002/05/01 04:05:33 mrsam Exp $"; + +/* Read the maildirsize file */ + +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=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 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, int *percentage) +{ +off_t i; +int spercentage=0; +int npercentage=0; + + errno=ENOSPC; + while (quota && *quota) + { + int x=1; + + 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) + { + *percentage=100; + return (-1); + } + + /* + ** For huge quotas, over 20mb, + ** divide numerator & denominator by 1024 to prevent + ** an overflow when multiplying by 100 + */ + + x=1; + if (i > 20000000) x=1024; + + spercentage = i ? (s/x) * 100 / (i/x):100; + break; + case 'C': + + if (i < n) + { + *percentage=100; + return (-1); + } + + /* Ditto */ + + x=1; + if (i > 20000000) x=1024; + + npercentage = i ? ((off_t)n/x) * 100 / (i/x):100; + break; + } + } + *percentage = spercentage > npercentage ? spercentage:npercentage; + return (0); +} + +static int doaddquota(const char *, int, const char *, long, int, int); + +static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, int *percentage); + + +int maildir_checkquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt) +{ +int dummy; + + return (docheckquota(dir, maildirsize_fdptr, quota_type, + xtra_size, xtra_cnt, &dummy)); +} + +int maildir_readquota(const char *dir, const char *quota_type) +{ +int percentage=0; +int fd=-1; + + (void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage); + if (fd >= 0) + close(fd); + return (percentage); +} + +static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, + int *percentage) +{ +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=docheckquota(checkfolder, maildirsize_fdptr, + quota_type, xtra_size, xtra_cnt, percentage); + 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, percentage); + + 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, percentage)); +} + +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=maildir_safeopen(newname2, + O_RDWR|O_APPEND, 0644)) < 0) + { + newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); + if (!newmaildirsizename) + { + free(newname2); + return (-1); + } + + maildirsize_fd=maildir_safeopen(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", + str_time_t(t, tbuf), + 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 || 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); +} diff --git a/maildirquota.h b/maildirquota.h new file mode 100644 index 0000000..96bbea7 --- /dev/null +++ b/maildirquota.h @@ -0,0 +1,45 @@ +#ifndef maildirquota_h +#define maildirquota_h + +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static const char maildirquota_h_rcsid[]="$Id: maildirquota.h,v 1.2 2000/09/04 17:10:28 mrsam Exp $"; + +int maildir_checkquota(const char *, /* Pointer to directory */ + int *, /* Initialized to -1, or opened descriptor for maildirsize */ + const char *, /* The quota */ + long, /* Extra bytes planning to add/remove from maildir */ + int); /* Extra messages planning to add/remove from maildir */ + +int maildir_addquota(const char *, /* Pointer to the maildir */ + int, /* Must be the int pointed to by 2nd arg to checkquota */ + const char *, /* The quota */ + long, /* +/- bytes */ + int); /* +/- files */ + +int maildir_readquota(const char *, /* Directory */ + const char *); /* Quota, from getquota */ + +int maildir_parsequota(const char *, unsigned long *); + /* Attempt to parse file size encoded in filename. Returns 0 if + ** parsed, non-zero if we didn't parse. */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/numlib.h b/numlib.h new file mode 100644 index 0000000..d36a791 --- /dev/null +++ b/numlib.h @@ -0,0 +1,45 @@ +#ifndef numlib_h +#define numlib_h + +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +static const char numlib_h_rcsid[]="$Id: numlib.h,v 1.3 2001/08/12 15:46:40 mrsam Exp $"; + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define NUMBUFSIZE 60 + +/* Convert various system types to decimal */ + +char *str_time_t(time_t, char *); +char *str_off_t(off_t, char *); +char *str_pid_t(pid_t, char *); +char *str_ino_t(ino_t, char *); +char *str_uid_t(uid_t, char *); +char *str_gid_t(gid_t, char *); +char *str_size_t(size_t, char *); + +char *str_sizekb(unsigned long, char *); /* X Kb or X Mb */ + +/* Convert selected system types to hex */ + +char *strh_time_t(time_t, char *); +char *strh_pid_t(pid_t, char *); +char *strh_ino_t(ino_t, char *); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/overmaildirquota.c b/overmaildirquota.c new file mode 100644 index 0000000..5e1877c --- /dev/null +++ b/overmaildirquota.c @@ -0,0 +1,43 @@ +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "maildirquota.h" +#include +#include +#include +#include + +static const char rcsid[]="$Id: overquota.c,v 1.0 2002/06/09 16:21:05 mr \ +sam Exp $"; + + +int user_over_maildirquota( const char *dir, const char *q) +{ +struct stat stat_buf; +int quotafd; +int ret_value; + + if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) && + stat_buf.st_size > 0 && *q) + { + if (maildir_checkquota(dir, "afd, q, stat_buf.st_size, 1) + && errno != EAGAIN) + { + if (quotafd >= 0) close(quotafd); + ret_value = 1; + } else { + maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1); + if (quotafd >= 0) close(quotafd); + ret_value = 0; + } + } else { + ret_value = 0; + } + + return(ret_value); +} diff --git a/qmail-local.c b/qmail-local.c index 6fec288..1bafb34 100644 --- a/qmail-local.c +++ b/qmail-local.c @@ -66,6 +66,7 @@ stralloc foo = {0}; char buf[1024]; char outbuf[1024]; +#define QUOTABUFSIZE 256 /* child process */ @@ -86,9 +87,15 @@ char *dir; int fd; substdio ss; substdio ssout; + char quotabuf[QUOTABUFSIZE]; sig_alarmcatch(sigalrm); if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); } + if (maildir_getquota(dir, quotabuf) == 0) { + if (user_over_maildirquota(dir,quotabuf)==1) { + _exit(1); + } + } pid = getpid(); host[0] = 0; gethostname(host,sizeof(host)); @@ -99,7 +106,10 @@ char *dir; s += fmt_str(s,"tmp/"); s += fmt_ulong(s,time); *s++ = '.'; s += fmt_ulong(s,pid); *s++ = '.'; - s += fmt_strn(s,host,sizeof(host)); *s++ = 0; + s += fmt_strn(s,host,sizeof(host)); + s += fmt_strn(s,",S=",sizeof(",S=")); + if (fstat(0,&st) == -1) if (errno == error_noent) break; + s += fmt_ulong(s,st.st_size); *s++ = 0; if (stat(fntmptph,&st) == -1) if (errno == error_noent) break; /* really should never get to this point */ if (loop == 2) _exit(1); @@ -159,6 +169,7 @@ char *fn; switch(wait_exitcode(wstat)) { case 0: break; + case 1: strerr_die1x(1, "User over quota. (#5.1.1)"); case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)"); case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)"); case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)"); diff --git a/qmail-pop3d.c b/qmail-pop3d.c index 51976c2..d2a6f87 100644 --- a/qmail-pop3d.c +++ b/qmail-pop3d.c @@ -16,6 +16,11 @@ #include "readwrite.h" #include "timeoutread.h" #include "timeoutwrite.h" +#include +#include "maildirquota.h" +#include "maildirmisc.h" + +#define QUOTABUFSIZE 256 void die() { _exit(0); } @@ -45,19 +50,15 @@ void put(buf,len) char *buf; int len; { substdio_put(&ssout,buf,len); } -void puts(s) char *s; -{ - substdio_puts(&ssout,s); -} void flush() { substdio_flush(&ssout); } void err(s) char *s; { - puts("-ERR "); - puts(s); - puts("\r\n"); + substdio_puts(&ssout,"-ERR "); + substdio_puts(&ssout,s); + substdio_puts(&ssout,"\r\n"); flush(); } @@ -73,7 +74,7 @@ void err_toobig() { err("not that many messages"); } void err_nosuch() { err("unable to open that message"); } void err_nounlink() { err("unable to unlink all deleted messages"); } -void okay() { puts("+OK \r\n"); flush(); } +void okay() { substdio_puts(&ssout,"+OK \r\n"); flush(); } void printfn(fn) char *fn; { @@ -153,11 +154,11 @@ void pop3_stat() total = 0; for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; - puts("+OK "); + substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,numm)); - puts(" "); + substdio_puts(&ssout," "); put(strnum,fmt_ulong(strnum,total)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); flush(); } @@ -171,18 +172,41 @@ void pop3_rset() void pop3_last() { - puts("+OK "); + substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,last)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); flush(); } void pop3_quit() { int i; + char quotabuf[QUOTABUFSIZE]; + int has_quota=maildir_getquota(".", quotabuf); + + long deleted_bytes=0; + long deleted_messages=0; + for (i = 0;i < numm;++i) if (m[i].flagdeleted) { - if (unlink(m[i].fn) == -1) err_nounlink(); + unsigned long un=0; + const char *filename=m[i].fn; + if (has_quota == 0 && !MAILDIR_DELETED(filename)) { + if (maildir_parsequota(filename, &un)) { + struct stat stat_buf; + + if (stat(filename, &stat_buf) == 0) + un=stat_buf.st_size; + } + } + if (unlink(m[i].fn) == -1) { + err_nounlink(); + un=0; + } + if (un) { + deleted_bytes -= un; + deleted_messages -= 1; + } } else if (str_start(m[i].fn,"new/")) { @@ -192,6 +216,21 @@ void pop3_quit() if (!stralloc_0(&line)) die_nomem(); rename(m[i].fn,line.s); /* if it fails, bummer */ } + + if (deleted_messages < 0) { + int quotafd; + + if (maildir_checkquota(".", "afd, quotabuf, deleted_bytes, + deleted_messages) && errno != EAGAIN && + deleted_bytes >= 0) + { + if (quotafd >= 0) close (quotafd); + } else { + maildir_addquota(".", quotafd, quotabuf, + deleted_bytes, deleted_messages); + if (quotafd >= 0) close(quotafd); + } + } okay(); die(); } @@ -222,10 +261,10 @@ int i; int flaguidl; { put(strnum,fmt_uint(strnum,i + 1)); - puts(" "); + substdio_puts(&ssout," "); if (flaguidl) printfn(m[i].fn); else put(strnum,fmt_ulong(strnum,m[i].size)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); } void dolisting(arg,flaguidl) char *arg; int flaguidl; @@ -234,7 +273,7 @@ void dolisting(arg,flaguidl) char *arg; int flaguidl; if (*arg) { i = msgno(arg); if (i == -1) return; - puts("+OK "); + substdio_puts(&ssout,"+OK "); list(i,flaguidl); } else { @@ -242,7 +281,7 @@ void dolisting(arg,flaguidl) char *arg; int flaguidl; for (i = 0;i < numm;++i) if (!m[i].flagdeleted) list(i,flaguidl); - puts(".\r\n"); + substdio_puts(&ssout,".\r\n"); } flush(); } diff --git a/strpidt.c b/strpidt.c new file mode 100644 index 0000000..7bec6b3 --- /dev/null +++ b/strpidt.c @@ -0,0 +1,26 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "numlib.h" +#include + +static const char rcsid[]="$Id: strpidt.c,v 1.3 2000/05/27 04:59:26 mrsam Exp $"; + +char *str_pid_t(pid_t t, char *arg) +{ +char buf[NUMBUFSIZE]; +char *p=buf+sizeof(buf)-1; + + *p=0; + do + { + *--p= '0' + (t % 10); + t=t / 10; + } while(t); + return (strcpy(arg, p)); +} diff --git a/strtimet.c b/strtimet.c new file mode 100644 index 0000000..c174872 --- /dev/null +++ b/strtimet.c @@ -0,0 +1,26 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "numlib.h" +#include + +static const char rcsid[]="$Id: strtimet.c,v 1.3 2000/05/27 04:59:26 mrsam Exp $"; + +char *str_time_t(time_t t, char *arg) +{ +char buf[NUMBUFSIZE]; +char *p=buf+sizeof(buf)-1; + + *p=0; + do + { + *--p= '0' + (t % 10); + t=t / 10; + } while(t); + return (strcpy(arg, p)); +} -- cgit v1.2.3