Back to index

courier  0.68.2
maildirquota.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2010 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if HAVE_CONFIG_H
00007 #include "config.h"
00008 #endif
00009 
00010 #include <sys/types.h>
00011 #if HAVE_DIRENT_H
00012 #include <dirent.h>
00013 #define NAMLEN(dirent) strlen((dirent)->d_name)
00014 #else
00015 #define dirent direct
00016 #define NAMLEN(dirent) (dirent)->d_namlen
00017 #if HAVE_SYS_NDIR_H
00018 #include <sys/ndir.h>
00019 #endif
00020 #if HAVE_SYS_DIR_H
00021 #include <sys/dir.h>
00022 #endif
00023 #if HAVE_NDIR_H
00024 #include <ndir.h>
00025 #endif
00026 #endif
00027 #include      <sys/types.h>
00028 #if    HAVE_SYS_STAT_H
00029 #include      <sys/stat.h>
00030 #endif
00031 
00032 #include      "maildirquota.h"
00033 #include      "maildirmisc.h"
00034 #include      "maildircreate.h"
00035 #include      "quotawarnmsg.h"
00036 #include      "../rfc822/rfc822.h"
00037 #include      <stdio.h>
00038 #include      <stdlib.h>
00039 #include      <string.h>
00040 #include      <errno.h>
00041 #if    HAVE_FCNTL_H
00042 #include      <fcntl.h>
00043 #endif
00044 #if    HAVE_UNISTD_H
00045 #include      <unistd.h>
00046 #endif
00047 #include      <time.h>
00048 #include      <ctype.h>
00049 #include      <numlib/numlib.h>
00050 
00051 
00052 static void parsequotastr(const char *, struct maildirquota *);
00053 
00054 #define DIG(c) ( (c) >= '0' && (c) <= '9')
00055 
00056 /* Read the maildirsize file */
00057 
00058 static int maildir_openquotafile_init(struct maildirsize *info,
00059                                   const char *maildir,
00060                                   const char *newquota);
00061 
00062 static int do_maildir_openquotafile(struct maildirsize *info,
00063                                 const char *filename,
00064                                 const char *newquota);
00065 
00066 int maildir_openquotafile(struct maildirsize *info, const char *maildir)
00067 {
00068        return (maildir_openquotafile_init(info, maildir, NULL));
00069 }
00070 
00071 static int maildir_openquotafile_init(struct maildirsize *info,
00072                                   const char *maildir,
00073                                   const char *newquota)
00074 {
00075        int rc;
00076 
00077        char   *buf=(char *)malloc(strlen(maildir)+sizeof("/maildirfolder"));
00078 
00079        memset(info, 0, sizeof(*info));
00080 
00081        info->fd= -1;
00082 
00083        if (!buf)
00084               return (-1);
00085 
00086        strcat(strcpy(buf, maildir), "/maildirfolder");
00087        if (stat(buf, &info->statbuf) == 0)       /* Go to parent */
00088        {
00089               strcat(strcpy(buf, maildir), "/..");
00090 
00091               rc=maildir_openquotafile_init(info, buf, newquota);
00092               free(buf);
00093               return rc;
00094        }
00095 
00096        info->maildir=strdup(maildir);
00097 
00098        if (!info->maildir)
00099        {
00100               free(buf);
00101               return (-1);
00102        }
00103 
00104        strcat(strcpy(info->maildirsizefile=buf, maildir), "/maildirsize");
00105 
00106        rc=do_maildir_openquotafile(info, buf, newquota);
00107 
00108        if (rc == 0)
00109               return (0);
00110 
00111        free(buf);
00112        free(info->maildir);
00113        return (rc);
00114 }
00115 
00116 static int do_maildir_openquotafile(struct maildirsize *info,
00117                                 const char *filename,
00118                                 const char *newquota)
00119 {
00120        char buf[5120];
00121        char *p;
00122        unsigned l;
00123        int n;
00124        int first;
00125 
00126        /*
00127        ** When setting a new quota, we don't care about the existing
00128        ** maildirsize.
00129        */
00130 
00131        if ((info->fd=(newquota ? open("/dev/null", O_RDWR):
00132                      maildir_safeopen(filename,
00133                                    O_RDWR|O_APPEND, 0))) < 0)
00134               return (0);   /* No quota */
00135 
00136        if (newquota)
00137        {
00138               parsequotastr(newquota, &info->quota);
00139 
00140               if (info->quota.nbytes == 0 &&
00141                   info->quota.nmessages == 0)
00142               {
00143                      close(info->fd);
00144                      info->fd= -1;
00145                      errno=EINVAL;
00146                      return (-1);
00147               }
00148               info->recalculation_needed=1;
00149               return (0);
00150        }
00151 
00152        p=buf;
00153        l=sizeof(buf);
00154 
00155        while (l)
00156        {
00157               n=read(info->fd, p, l);
00158               if (n < 0)
00159               {
00160                      close(info->fd);
00161                      info->fd= -1;
00162                      return (-1);
00163               }
00164               if (n == 0)   break;
00165               p += n;
00166               l -= n;
00167        }
00168 
00169        if (fstat(info->fd, &info->statbuf))      /* maildir too big */
00170        {
00171               close(info->fd);
00172               info->fd= -1;
00173               return (-1);
00174        }
00175 
00176        if (l == 0)   /*
00177                      ** maildirsize overflowed, still need to read its
00178                      ** quota
00179                      */
00180        {
00181               p[-1]=0;
00182               p=strchr(buf, '\n');
00183               if (p)
00184                      *p=0;
00185               parsequotastr(buf, &info->quota);
00186               info->recalculation_needed=1;
00187               return (0);
00188        }
00189 
00190 
00191        info->size.nbytes=0;
00192        info->size.nmessages=0;
00193        info->nlines=0;
00194        *p=0;
00195        p=buf;
00196        first=1;
00197        while (*p)
00198        {
00199               int64_t n=0;
00200               int c=0;
00201               char   *q=p;
00202               int    neg;
00203 
00204               while (*p)
00205                      if (*p++ == '\n')
00206                      {
00207                             p[-1]=0;
00208                             break;
00209                      }
00210 
00211               if (first)
00212               {
00213                      parsequotastr(q, &info->quota);
00214                      first=0;
00215                      continue;
00216               }
00217 
00218               while (*q && isspace((int)(unsigned char)*q))
00219                      ++q;
00220 
00221               neg=0;
00222               if (*q == '-')
00223               {
00224                      neg=1;
00225                      ++q;
00226               }
00227 
00228               if (DIG(*q))
00229               {
00230                      while (DIG(*q))
00231                      {
00232                             n=n*10 + (*q++ - '0');
00233                      }
00234 
00235                      if (neg)
00236                             n= -n;
00237 
00238                      neg=0;
00239                      while (*q && isspace((int)(unsigned char)*q))
00240                             ++q;
00241 
00242                      if (*q == '-')
00243                      {
00244                             neg=1;
00245                             ++q;
00246                      }
00247                      if (DIG(*q))
00248                      {
00249                             while (DIG(*q))
00250                             {
00251                                    c=c*10 + (*q++ - '0');
00252                             }
00253 
00254                             if (neg)
00255                                    c= -c;
00256 
00257                             info->size.nbytes += n;
00258                             info->size.nmessages += c;
00259                      }
00260               }
00261 
00262               ++ info->nlines;
00263        }
00264        if (info->size.nbytes < 0 ||
00265            info->size.nmessages < 0)
00266               info->recalculation_needed=1;
00267        return (0);
00268 }
00269 
00270 static void parsequotastr(const char *quota, struct maildirquota *q)
00271 {
00272        int64_t i;
00273        const char *quota_start = quota;
00274 
00275        q->nbytes=0;
00276        q->nmessages=0;
00277 
00278        while (quota && *quota)
00279        {
00280               if (!DIG(*quota))
00281               {
00282                      ++quota;
00283                      continue;
00284               }
00285               i=0;
00286               while (DIG(*quota))
00287                      i=i*10 + (*quota++ - '0');
00288               switch (*quota)      {
00289               case MDQUOTA_SIZE:
00290                      q->nbytes=i;
00291                      break;
00292               case MDQUOTA_COUNT:
00293                      q->nmessages=i;
00294                      break;
00295               default:
00296                      fprintf(stderr, "WARN: quota string '%s' not parseable\n",
00297                             quota_start);
00298                      break;
00299               }
00300        }
00301 }
00302 
00303 
00304 void maildir_closequotafile(struct maildirsize *info)
00305 {
00306        if (info->maildir)
00307               free (info->maildir);
00308        info->maildir=NULL;
00309 
00310        if (info->maildirsizefile)
00311               free (info->maildirsizefile);
00312        info->maildirsizefile=NULL;
00313 
00314        if (info->fd >= 0)
00315               close(info->fd);
00316        info->fd= -1;
00317 }
00318 
00323 static int checkOneQuota(int64_t size, int64_t quota, int *percentage);
00324 
00325 static int checkQuota(struct maildirquota *size,
00326                     struct maildirquota *quota, int *percentage)
00327 {
00328        int b_quota;
00329        int n_quota;
00330 
00331        if (checkOneQuota(size->nbytes, quota->nbytes, &b_quota) ||
00332            checkOneQuota(size->nmessages, quota->nmessages,
00333                        &n_quota))
00334        {
00335               if (percentage)
00336                      *percentage= 100;
00337               errno=ENOSPC;
00338               return -1;
00339        }
00340 
00341        if (b_quota < n_quota)
00342               b_quota=n_quota;
00343 
00344        if (percentage)
00345               *percentage=b_quota;
00346        return (0);
00347 }
00348 
00349 static int checkOneQuota(int64_t size, int64_t quota, int *percentage)
00350 {
00351        int x=1;
00352 
00353        if (quota == 0) /* No quota */
00354        {
00355               *percentage=0;
00356               return (0);
00357        }
00358 
00359        if (size > quota)
00360        {
00361               *percentage=100;
00362               return (-1);
00363        }
00364 
00365        if (quota > 20000000)
00366               x=1024;
00367 
00368        *percentage= quota > 0 ? (size/x) * 100 / (quota/x):0;
00369        return 0;
00370 }
00371 
00372 static char *makenewmaildirsizename(const char *, int *);
00373 static int countcurnew(const char *, time_t *, int64_t *, unsigned *);
00374 static int countsubdir(const char *, const char *,
00375               time_t *, int64_t *, unsigned *);
00376 static int statcurnew(const char *, time_t *);
00377 static int statsubdir(const char *, const char *, time_t *);
00378 
00379 static int    doaddquota(struct maildirsize *, int, int64_t, int, int);
00380 
00381 static int docheckquota(struct maildirsize *info,
00382                      int64_t xtra_size,
00383                      int xtra_cnt, int *percentage);
00384 
00385 int maildir_checkquota(struct maildirsize *info,
00386                      int64_t xtra_size,
00387                      int xtra_cnt)
00388 {
00389        int    dummy;
00390 
00391        return (docheckquota(info, xtra_size, xtra_cnt, &dummy));
00392 }
00393 
00394 int maildir_readquota(struct maildirsize *info)
00395 {
00396        int    percentage=0;
00397 
00398        (void)docheckquota(info, 0, 0, &percentage);
00399        return (percentage);
00400 }
00401 
00402 static int docheckquota(struct maildirsize *info,
00403                      int64_t xtra_size,
00404                      int xtra_cnt,
00405                      int *percentage)
00406 {
00407        char   *newmaildirsizename;
00408        int    maildirsize_fd;
00409        int64_t       maildirsize_size;
00410        unsigned maildirsize_cnt;
00411 
00412        time_t tm;
00413        time_t maxtime;
00414        DIR    *dirp;
00415        struct dirent *de;
00416 
00417        struct maildirquota new_quota;
00418 
00419        *percentage=0;
00420 
00421        if (info->fd < 0)    /* No quota */
00422               return (0);
00423 
00424        new_quota=info->size;
00425 
00426        new_quota.nbytes += xtra_size;
00427        new_quota.nmessages += xtra_cnt;
00428 
00429        if (!info->recalculation_needed &&
00430            checkQuota(&new_quota, &info->quota, percentage) == 0)
00431               return (0);   /* New size is under quota */
00432 
00433        /*
00434        ** Overquota, see if it's time to recalculate the quota anyway
00435        */
00436 
00437        time(&tm);
00438        if (!info->recalculation_needed &&
00439            info->nlines == 1 && tm < info->statbuf.st_mtime + 15*60)
00440               return (-1);
00441 
00442 
00443        maxtime=0;
00444        maildirsize_size=0;
00445        maildirsize_cnt=0;
00446 
00447        if (countcurnew(info->maildir,
00448                      &maxtime, &maildirsize_size, &maildirsize_cnt))
00449        {
00450               errno=EIO;
00451               return (-1);
00452        }
00453 
00454        dirp=opendir(info->maildir);
00455        while (dirp && (de=readdir(dirp)) != 0)
00456        {
00457               if (countsubdir(info->maildir, de->d_name,
00458                             &maxtime, &maildirsize_size,
00459                      &maildirsize_cnt))
00460               {
00461                      errno=EIO;
00462                      closedir(dirp);
00463                      return (-1);
00464               }
00465        }
00466        if (dirp)
00467        {
00468 #if    CLOSEDIR_VOID
00469               closedir(dirp);
00470 #else
00471               if (closedir(dirp))
00472               {
00473                      errno=EIO;
00474                      return (-1);
00475               }
00476 #endif
00477        }
00478 
00479        newmaildirsizename=makenewmaildirsizename(info->maildir,
00480                                             &maildirsize_fd);
00481        if (!newmaildirsizename)
00482        {
00483               errno=EIO;
00484               return (-1);
00485        }
00486 
00487        if (doaddquota(info, maildirsize_fd, maildirsize_size,
00488               maildirsize_cnt, 1))
00489        {
00490               unlink(newmaildirsizename);
00491               free(newmaildirsizename);
00492               close(maildirsize_fd);
00493               errno=EIO;
00494               return (-1);
00495        }
00496 
00497        if (rename(newmaildirsizename, info->maildirsizefile))
00498        {
00499               unlink(newmaildirsizename);
00500               close(maildirsize_fd);
00501               errno=EIO;
00502               return (-1);
00503        }
00504 
00505        info->recalculation_needed=0;
00506        info->size.nbytes=maildirsize_size;
00507        info->size.nmessages=maildirsize_cnt;
00508        info->nlines=1;
00509        close(info->fd);
00510        info->fd=maildirsize_fd;
00511 
00512        tm=0;
00513 
00514        if (statcurnew(info->maildir, &tm))
00515        {
00516               errno=EIO;
00517               return (-1);
00518        }
00519 
00520        dirp=opendir(info->maildir);
00521        while (dirp && (de=readdir(dirp)) != 0)
00522        {
00523               if (statsubdir(info->maildir, de->d_name, &tm))
00524               {
00525                      errno=EIO;
00526                      closedir(dirp);
00527                      return (-1);
00528               }
00529        }
00530        if (dirp)
00531        {
00532 #if    CLOSEDIR_VOID
00533               closedir(dirp);
00534 #else
00535               if (closedir(dirp))
00536               {
00537                      errno=EIO;
00538                      return (-1);
00539               }
00540 #endif
00541        }
00542 
00543        if (tm != maxtime)   /* Race condition, someone changed something */
00544        {
00545               info->recalculation_needed=1;
00546               info->nlines=0;
00547               errno=EAGAIN;
00548               return (-1);
00549        }
00550 
00551        *percentage=0;
00552 
00553        new_quota=info->size;
00554 
00555        new_quota.nbytes += xtra_size;
00556        new_quota.nmessages += xtra_cnt;
00557 
00558        return checkQuota(&new_quota, &info->quota, percentage);
00559 }
00560 
00561 int    maildir_addquota(struct maildirsize *info,
00562                       int64_t maildirsize_size, int maildirsize_cnt)
00563 {
00564        if (info->fd < 0)
00565               return (0);
00566 
00567        return (doaddquota(info, info->fd, maildirsize_size,
00568                         maildirsize_cnt, 0));
00569 }
00570 
00571 static int doaddquota(struct maildirsize *info, int maildirsize_fd,
00572                     int64_t maildirsize_size, int maildirsize_cnt,
00573                     int isnew)
00574 {
00575        char   n[NUMBUFSIZE];
00576        char   buf[NUMBUFSIZE * 4 + 32 ];
00577        char *p;
00578        int cnt;
00579 
00580        buf[0]=0;
00581 
00582        if (isnew)
00583        {
00584               if (info->quota.nbytes > 0)
00585               {
00586                      char b[2];
00587 
00588                      b[0]=MDQUOTA_SIZE;
00589                      b[1]=0;
00590 
00591                      strcat(strcat(buf, libmail_str_int64_t(info->quota.nbytes, n)),
00592                             b);
00593               }
00594 
00595               if (info->quota.nmessages > 0)
00596               {
00597                      char b[2];
00598 
00599                      b[0]=MDQUOTA_COUNT;
00600                      b[1]=0;
00601 
00602                      if (buf[0] != 0)
00603                             strcat(buf, ",");
00604 
00605                      strcat(strcat(buf,
00606                                   libmail_str_size_t(info->quota.nmessages, n)),
00607                             b);
00608               }
00609               strcat(buf, "\n");
00610        }
00611 
00612        sprintf(buf + strlen(buf),
00613               "%12s ", libmail_str_int64_t(maildirsize_size, n));
00614 
00615        sprintf(buf + strlen(buf),
00616               "%12s\n", libmail_str_int64_t(maildirsize_cnt, n));
00617 
00618        p=buf;
00619        cnt=strlen(buf);
00620 
00621        while (cnt > 0)
00622        {
00623               int c=write( maildirsize_fd, p, cnt);
00624 
00625               if (c < 0)
00626                      return (-1);
00627 
00628               cnt -= c;
00629               p += c;
00630        }
00631 
00632        return (0);
00633 }
00634 
00635 
00636 /* New maildirsize is built in the tmp subdirectory */
00637 
00638 static char *makenewmaildirsizename(const char *dir, int *fd)
00639 {
00640 char   hostname[256];
00641 struct stat stat_buf;
00642 time_t t;
00643 char   *p;
00644 
00645        hostname[0]=0;
00646        hostname[sizeof(hostname)-1]=0;
00647        gethostname(hostname, sizeof(hostname)-1);
00648        p=(char *)malloc(strlen(dir)+strlen(hostname)+130);
00649        if (!p)       return (0);
00650 
00651        for (;;)
00652        {
00653        char   tbuf[NUMBUFSIZE];
00654        char   pbuf[NUMBUFSIZE];
00655 
00656               time(&t);
00657               strcat(strcpy(p, dir), "/tmp/");
00658               sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s",
00659                      libmail_str_time_t(t, tbuf),
00660                      libmail_str_pid_t(getpid(), pbuf), hostname);
00661 
00662               if (stat( (const char *)p, &stat_buf) < 0 &&
00663                      (*fd=maildir_safeopen(p,
00664                             O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0)
00665                      break;
00666               sleep(3);
00667        }
00668        return (p);
00669 }
00670 
00671 static int statcurnew(const char *dir, time_t *maxtimestamp)
00672 {
00673 char   *p=(char *)malloc(strlen(dir)+5);
00674 struct stat   stat_buf;
00675 
00676        if (!p)       return (-1);
00677        strcat(strcpy(p, dir), "/cur");
00678        if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
00679               *maxtimestamp=stat_buf.st_mtime;
00680        strcat(strcpy(p, dir), "/new");
00681        if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
00682               *maxtimestamp=stat_buf.st_mtime;
00683        free(p);
00684        return (0);
00685 }
00686 
00687 static int statsubdir(const char *dir, const char *subdir, time_t *maxtime)
00688 {
00689 char   *p;
00690 int    n;
00691 
00692        if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
00693               strcmp(subdir, "..") == 0
00694 #ifndef TRASHQUOTA
00695                             || strcmp(subdir, "." TRASH) == 0
00696 #endif
00697               )
00698               return (0);
00699 
00700        p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
00701        if (!p)       return (-1);
00702        strcat(strcat(strcpy(p, dir), "/"), subdir);
00703        n=statcurnew(p, maxtime);
00704        free(p);
00705        return (n);
00706 }
00707 
00708 static int docount(const char *, time_t *, int64_t *, unsigned *);
00709 
00710 static int countcurnew(const char *dir, time_t *maxtime,
00711        int64_t *sizep, unsigned *cntp)
00712 {
00713 char   *p=(char *)malloc(strlen(dir)+5);
00714 int    n;
00715 
00716        if (!p)       return (-1);
00717        strcat(strcpy(p, dir), "/new");
00718        n=docount(p, maxtime, sizep, cntp);
00719        if (n == 0)
00720        {
00721               strcat(strcpy(p, dir), "/cur");
00722               n=docount(p, maxtime, sizep, cntp);
00723        }
00724        free(p);
00725        return (n);
00726 }
00727 
00728 static int countsubdir(const char *dir, const char *subdir, time_t *maxtime,
00729        int64_t *sizep, unsigned *cntp)
00730 {
00731 char   *p;
00732 int    n;
00733 
00734        if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
00735             strcmp(subdir, "..") == 0 ||
00736             ! maildirquota_countfolder(subdir))
00737               return (0);
00738 
00739        p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
00740        if (!p)       return (2);
00741        strcat(strcat(strcpy(p, dir), "/"), subdir);
00742        n=countcurnew(p, maxtime, sizep, cntp);
00743        free(p);
00744        return (n);
00745 }
00746 
00747 static int docount(const char *dir, time_t *dirstamp,
00748        int64_t *sizep, unsigned *cntp)
00749 {
00750 struct stat   stat_buf;
00751 char   *p;
00752 DIR    *dirp;
00753 struct dirent *de;
00754 unsigned long s;
00755 
00756        if (stat(dir, &stat_buf))   return (0);   /* Ignore */
00757        if (stat_buf.st_mtime > *dirstamp) *dirstamp=stat_buf.st_mtime;
00758        if ((dirp=opendir(dir)) == 0)      return (0);
00759        while ((de=readdir(dirp)) != 0)
00760        {
00761        const char *n=de->d_name;
00762 
00763               if (*n == '.')       continue;
00764 
00765               if (!maildirquota_countfile(n))
00766                      continue;
00767 
00768               if (maildir_parsequota(n, &s) == 0)
00769                      stat_buf.st_size=s;
00770               else
00771               {
00772                      p=(char *)malloc(strlen(dir)+strlen(n)+2);
00773                      if (!p)
00774                      {
00775                             closedir(dirp);
00776                             return (-1);
00777                      }
00778                      strcat(strcat(strcpy(p, dir), "/"), n);
00779                      if (stat(p, &stat_buf))
00780                      {
00781                             free(p);
00782                             continue;
00783                      }
00784                      free(p);
00785               }
00786               *sizep += stat_buf.st_size;
00787               ++*cntp;
00788        }
00789 
00790 #if    CLOSEDIR_VOID
00791        closedir(dirp);
00792 #else
00793        if (closedir(dirp))
00794               return (-1);
00795 #endif
00796        return (0);
00797 }
00798 
00799 int maildirquota_countfolder(const char *folder)
00800 {
00801 #ifdef TRASHQUOTA
00802 
00803 #else
00804 
00805        if (strcmp(folder, "." TRASH) == 0 ||
00806            strcmp(folder, "." TRASH "/") == 0)
00807               return (0);
00808 
00809        for ( ; *folder; folder++)
00810               if (*folder == '/' &&
00811                   (strcmp(folder+1, "." TRASH) == 0 ||
00812                    strcmp(folder+1, "." TRASH "/") == 0))
00813                      return (0);
00814 #endif
00815        return (1);
00816 }
00817 
00818 int maildirquota_countfile(const char *n)
00819 {
00820 #ifdef TRASHQUOTA
00821 
00822 #else
00823        const char *nn=strrchr(n, '/');
00824 
00825        if (nn != NULL)
00826               n=nn+1;
00827 
00828        /* do not count msgs marked as deleted */
00829 
00830        for ( ; *n; n++)
00831        {
00832               if (n[0] != MDIRSEP[0] || n[1] != '2' ||
00833                   n[2] != ',')     continue;
00834               n += 3;
00835               while (*n >= 'A' && *n <= 'Z')
00836               {
00837                      if (*n == 'T')       return (0);
00838                      ++n;
00839               }
00840               break;
00841        }
00842 #endif
00843        return (1);
00844 }
00845 
00846 /*
00847 ** Prepare to add something to the maildir
00848 */
00849 
00850 int maildir_quota_add_start(const char *maildir,
00851                          struct maildirsize *info,
00852                          int64_t msgsize, int nmsgs,
00853                          const char *newquota)
00854 {
00855        struct maildirquota mq;
00856        int i;
00857 
00858        if ( maildir_openquotafile(info, maildir))
00859               info->fd= -1;
00860 
00861        if (newquota != NULL)
00862        {
00863               parsequotastr(newquota, &mq);
00864 
00865               if ((mq.nbytes > 0 || mq.nmessages > 0) &&
00866                   (info->fd < 0 || info->quota.nbytes != mq.nbytes ||
00867                    info->quota.nmessages != mq.nmessages))
00868               {
00869                      if (info->fd < 0)
00870                      {
00871                             maildir_quota_set(maildir, newquota);
00872                             if (maildir_openquotafile(info, maildir))
00873                                    info->fd= -1;
00874                      }
00875                      else
00876                      {
00877                             info->quota=mq;
00878                             info->recalculation_needed=1;
00879                      }
00880               }
00881        }
00882        if (info->fd < 0)
00883               return (0);   /* No quota set on this maildir */
00884 
00885        for (i=0; i<5; i++)
00886        {
00887               int rc;
00888 
00889               rc=maildir_checkquota(info, msgsize, nmsgs);
00890               if (rc == 0)
00891                      return (0);
00892 
00893               if (errno != EAGAIN)
00894               {
00895                      maildir_closequotafile(info);
00896                      return (-1);
00897               }
00898        }
00899        maildir_closequotafile(info);
00900 
00901        /* Cannot recover from a race condition, just punt */
00902 
00903        return (0);
00904 }
00905 
00906 void maildir_quota_add_end(struct maildirsize *info,
00907                         int64_t msgsize, int nmsgs)
00908 {
00909        maildir_addquota(info, msgsize, nmsgs);
00910        maildir_closequotafile(info);
00911 }
00912 
00913 void maildir_quota_deleted(const char *maildir,
00914                         int64_t nbytes,   /* Must be negative */
00915                         int nmsgs) /* Must be negative */
00916 {
00917        struct maildirsize info;
00918 
00919        if ( maildir_openquotafile(&info, maildir))
00920               return;
00921 
00922        maildir_checkquota(&info, nbytes, nmsgs); /* Cleanup */
00923        maildir_addquota(&info, nbytes, nmsgs);
00924        maildir_closequotafile(&info);
00925 }
00926 
00927 void maildir_quota_recalculate(const char *maildir)
00928 {
00929        struct maildirsize info;
00930 
00931        if (maildir_openquotafile(&info, maildir))
00932               return;
00933        info.recalculation_needed=1;
00934 
00935        maildir_readquota(&info);
00936        maildir_closequotafile(&info);
00937 }
00938 
00939 int maildir_quota_delundel_start(const char *maildir,
00940                              struct maildirsize *info,
00941                              int64_t msgsize, int nmsgs)
00942 {
00943 #if TRASHQUOTA
00944        return (0);
00945 #else
00946        if (nmsgs < 0)
00947        {
00948               maildir_quota_deleted(maildir, msgsize, nmsgs);
00949               return (0);   /* Always allowed */
00950        }
00951 
00952        return maildir_quota_add_start(maildir, info, msgsize, nmsgs, NULL);
00953 #endif
00954 }
00955 
00956 void maildir_quota_delundel_end(struct maildirsize *info,
00957                             int64_t msgsize, int nmsgs)
00958 {
00959 #if TRASHQUOTA
00960        return;
00961 #else
00962        if (nmsgs < 0)
00963               return;
00964 
00965        maildir_quota_add_end(info, msgsize, nmsgs);
00966 #endif
00967 }
00968 
00969 
00970 void maildir_quota_set(const char *dir, const char *quota)
00971 {
00972        struct maildirsize info;
00973 
00974        if (maildir_openquotafile_init(&info, dir, quota) == 0)
00975        {
00976               maildir_checkquota(&info, 0, 0);
00977               maildir_closequotafile(&info);
00978        }
00979 }
00980 
00981 
00982 static void do_deliver_warning(const char *msgfile, const char *dir)
00983 {
00984        int    fdin, fd, ret;
00985        FILE *fpout;
00986        time_t t;
00987        size_t l, msg_len;
00988        char   *qname = 0;
00989        struct stat   sb;
00990        char   hostname[256];
00991        char   buf[4096];
00992        size_t n;
00993        struct maildirsize info;
00994        struct maildir_tmpcreate_info createInfo;
00995 
00996        fdin=-1;
00997 
00998        if (msgfile && *msgfile)
00999               fdin=open(msgfile, O_RDONLY);
01000 
01001        if (fdin < 0)
01002        {
01003               msgfile=QUOTAWARNMSG;
01004               fdin=open(msgfile, O_RDONLY);
01005        }
01006 
01007        if (fdin < 0)
01008               return;
01009 
01010        l = strlen(dir)+sizeof("/quotawarn");
01011 
01012        /* Send only one warning every 24 hours */
01013        if ((qname = malloc(l)) == 0)
01014        {
01015               close(fdin);
01016               return;
01017        }
01018 
01019        strcat(strcpy(qname, dir), "/quotawarn");
01020        time(&t);
01021        ret = stat(qname, &sb);
01022        if ((ret == -1 && errno != ENOENT) ||
01023            (ret == 0 && (sb.st_mtime + 86400) > t))
01024        {
01025               free(qname);
01026               close(fdin);
01027               return;
01028        }
01029 
01030        fd = open(qname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
01031        free(qname);
01032        if (fd == -1)
01033        {
01034               close(fdin);
01035               return;
01036        }
01037        if (write(fd, buf, 1) < 0)
01038               perror(msgfile);
01039 
01040        close(fd);
01041 
01042        strcpy(buf, "Date: ");
01043        rfc822_mkdate_buf(t, buf+strlen(buf));
01044        strcat(buf, "\n");
01045 
01046        hostname[0]=0;
01047        hostname[sizeof(hostname)-1]=0;
01048        gethostname(hostname, sizeof(hostname)-1);
01049        sprintf(buf+strlen(buf), "Message-Id: <%lu.overquota@%-1.256s>\n",
01050               (unsigned long)t, hostname);
01051 
01052        if (fstat(fdin, &sb) < 0) {
01053               close(fdin);
01054               return;
01055        }
01056        msg_len=strlen(buf)+sb.st_size;
01057 
01058 
01059 
01060        maildir_tmpcreate_init(&createInfo);
01061        createInfo.maildir=dir;
01062        createInfo.uniq="warn";
01063        createInfo.msgsize=msg_len;
01064        createInfo.doordie=1;
01065 
01066        if ((fpout=maildir_tmpcreate_fp(&createInfo)) == NULL)
01067        {
01068               close(fdin);
01069               return;
01070        }
01071 
01072        fprintf(fpout, "%s", buf);
01073 
01074        while ((n=read(fdin, buf, sizeof(buf))) > 0)
01075        {
01076               if (fwrite(buf, n, 1, fpout) != 1)
01077               {
01078                      perror(createInfo.tmpname);
01079                      break;
01080               }
01081        }
01082        close(fdin);
01083 
01084        if (fflush(fpout) || ferror(fpout))
01085        {
01086               fclose(fpout);
01087               unlink(createInfo.tmpname);
01088               maildir_tmpcreate_free(&createInfo);
01089               return;
01090        }
01091 
01092        if (fseek(fpout, 0L, SEEK_SET) >= 0)
01093        {
01094               /* Make sure the quota message's size itself is factored into
01095               ** the quota. Deliver the message regardless of whether the
01096               ** user is over quota.
01097               */
01098               if (maildirquota_countfolder(dir))
01099               {
01100                      maildir_quota_add_start(dir, &info, msg_len, 1, NULL);
01101                      maildir_quota_add_end(&info, msg_len, 1);
01102               }
01103        }
01104 
01105        fclose(fpout);
01106        if (maildir_movetmpnew(createInfo.tmpname,
01107                             createInfo.newname))
01108        {
01109               unlink(createInfo.tmpname);
01110               maildir_tmpcreate_free(&createInfo);
01111               return;
01112        }
01113        maildir_tmpcreate_free(&createInfo);
01114 }
01115 
01116 void maildir_deliver_quota_warning(const char *dir, const int percent,
01117                                const char *msgquotafile)
01118 {
01119        size_t l;
01120        char *p;
01121        struct stat   sb;
01122 
01123        /* If we delivered to a folder, dump the warning message into INBOX */
01124 
01125        l = strlen(dir)+sizeof("/maildirfolder");
01126        if ((p = malloc(l)) == 0)
01127               return;
01128 
01129        strcat(strcpy(p, dir), "/maildirfolder");
01130 
01131        /* If delivering to a folder, find quotawarn in its parent directory */
01132 
01133        if (stat(p, &sb) == 0)
01134        {
01135               strcat(strcpy(p, dir), "/..");
01136               maildir_deliver_quota_warning(p, percent, msgquotafile);
01137               free(p);
01138               return;
01139        }
01140        free(p);
01141 
01142        if (percent >= 0)
01143        {
01144               struct maildirsize info;
01145 
01146               if (maildir_openquotafile(&info, dir) == 0)
01147               {
01148                      if (maildir_readquota(&info) >= percent)
01149                      {
01150                             maildir_closequotafile(&info);
01151                             do_deliver_warning(msgquotafile, dir);
01152                             return;
01153                      }
01154                      maildir_closequotafile(&info);
01155               }
01156        }
01157 }