Back to index

courier  0.68.2
maildircache.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2006 Double Precision, Inc.  See COPYING for
00003 ** distribution information.
00004 */
00005 
00006 
00007 #include      "config.h"
00008 #include      "maildircache.h"
00009 #include      "numlib/numlib.h"
00010 #include      <stdio.h>
00011 #include      <string.h>
00012 #include      <stdlib.h>
00013 #include      <ctype.h>
00014 #include      <signal.h>
00015 #include      <pwd.h>
00016 #if    HAVE_UNISTD_H
00017 #include      <unistd.h>
00018 #endif
00019 #include      <sys/types.h>
00020 #if    HAVE_SYS_STAT_H
00021 #include      <sys/stat.h>
00022 #endif
00023 #if    HAVE_SYS_WAIT_H
00024 #include      <sys/wait.h>
00025 #endif
00026 #if    HAVE_FCNTL_H
00027 #include      <fcntl.h>
00028 #endif
00029 #if HAVE_DIRENT_H
00030 #include <dirent.h>
00031 #define NAMLEN(dirent) strlen((dirent)->d_name)
00032 #else
00033 #define dirent direct
00034 #define NAMLEN(dirent) (dirent)->d_namlen
00035 #if HAVE_SYS_NDIR_H
00036 #include <sys/ndir.h>
00037 #endif
00038 #if HAVE_SYS_DIR_H
00039 #include <sys/dir.h>
00040 #endif
00041 #if HAVE_NDIR_H
00042 #include <ndir.h>
00043 #endif
00044 #endif
00045 
00046 #define exit(_a_) _exit(_a_)
00047 
00048 
00049 static const char * const *authvars;
00050 static char **authvals;
00051 static time_t expinterval;
00052 static time_t lastclean=0;
00053 static const char *cachedir;
00054 static const char *cacheowner;
00055 
00056 int maildir_cache_init(time_t n, const char *d, const char *o,
00057                      const char * const *a)
00058 {
00059        unsigned x;
00060 
00061        expinterval=n;
00062        cachedir=d;
00063        cacheowner=o;
00064        authvars=a;
00065 
00066        for (x=0; a[x]; x++)
00067               ;
00068 
00069        if ((authvals=malloc(sizeof(char *)*(x+1))) == NULL)
00070               return (-1);
00071 
00072        for (x=0; a[x]; x++)
00073               authvals[x]=0;
00074        return (0);
00075 }
00076 
00077 static char *create_cache_name(const char *userid, time_t login_time)
00078 {
00079 int    l;
00080 char   buf[NUMBUFSIZE];
00081 const char    *p;
00082 char   *q;
00083 char   *f, *g;
00084 
00085        login_time /= expinterval;
00086        l=1;
00087        for (p=userid; *p; p++)
00088        {
00089               ++l;
00090               if (*p < ' ' || *p == ';' || *p == '\'' || *p == ';')
00091               {
00092                      fprintf(stderr, "CRIT: maildircache: invalid chars in userid: %s\n", p);
00093                      return (NULL);
00094               }
00095               if (*p == '/' || *p == '+' || (int)(unsigned char)*p >= 127)
00096                      l += 2;
00097        }
00098        g=malloc(l);
00099        if (!g)
00100        {
00101               perror("CRIT: maildircache: malloc failed");
00102               return (NULL);
00103        }
00104        q=g;
00105        while (*userid)
00106        {
00107               if (*userid == '/' || *userid == '+'
00108                      || (int)(unsigned char)*userid >= 127)
00109               {
00110               static char xdigit[]="0123456789ABCDEF";
00111 
00112                      *q++ = '+';
00113                      *q++ = xdigit[ (*userid >> 4) & 15 ];
00114                      *q++ = xdigit[ (*userid) & 15 ];
00115               }
00116               else
00117                      *q++ = *userid;
00118 
00119               ++userid;
00120        }
00121        *q=0;
00122 
00123        l=sizeof("//xx/xxxxxxx") + strlen(cachedir);
00124        l += strlen(libmail_str_time_t( login_time, buf)) + strlen(g);
00125        f=malloc(l);
00126        if (!f)
00127        {
00128               free(g);
00129               perror("CRIT: maildircache: malloc failed");
00130               return (NULL);
00131        }
00132        strcat(strcat(strcat(strcpy(f, cachedir), "/"), buf), "/");
00133        strncpy(buf, g, 2);
00134        buf[2]=0;
00135        while (strlen(buf) < 2)     strcat(buf, "+");
00136        strcat(strcat(strcat(f, buf), "/"), g);
00137        free(g);
00138        return (f);
00139 }
00140 
00141 static pid_t childproc= -1;
00142 static int childpipe;
00143 
00144 void maildir_cache_start()
00145 {
00146 int    pipefd[2];
00147 char   buf[2048];
00148 int    i, j;
00149 char   *userid, *login_time, *data;
00150 time_t login_time_n;
00151 char   *f;
00152 FILE   *fp;
00153 
00154        if (pipe(pipefd) < 0)
00155        {
00156               perror("CRIT: maildircache: pipe() failed");
00157               return;
00158        }
00159        while ((childproc=fork()) < 0)
00160        {
00161               sleep(5);
00162        }
00163 
00164        if (childproc)
00165        {
00166               close(pipefd[0]);
00167               childpipe=pipefd[1];
00168               return;
00169        }
00170        close(pipefd[1]);
00171        i=0;
00172 
00173        for (;;)
00174        {
00175               if (i >= sizeof(buf)-1)
00176               {
00177                      close(pipefd[0]);
00178 
00179                      /* Problems */
00180 
00181                      fprintf(stderr, "CRIT: maildircache: Max cache buffer overflow.\n");
00182                      exit(1);
00183               }
00184 
00185               j=read(pipefd[0], buf+i, sizeof(buf)-1-i);
00186               if (j < 0)
00187               {
00188                      perror("CRIT: maildircache: Cache create failure");
00189                      exit(1);
00190               }
00191               if (j == 0)   break;
00192               i += j;
00193        }
00194        close(pipefd[0]);
00195        buf[i]=0;
00196 
00197        {
00198        struct passwd *pwd=getpwnam(cacheowner);
00199 
00200               if (!pwd || setgid(pwd->pw_gid) || setuid(pwd->pw_uid))
00201               {
00202                      fprintf(stderr, "CRIT: maildircache: Cache create failure - cannot change to user %s\n",
00203                             cacheowner);
00204                      exit(1);
00205               }
00206        }
00207 
00208        if (strncmp(buf, "CANCELLED\n", 10) == 0)
00209               exit (0);
00210 
00211        userid=buf;
00212        if ((login_time=strchr(userid, ' ')) == 0)
00213        {
00214               fprintf(stderr, "CRIT: maildircache: Cache create failure - authentication process crashed.\n");
00215               exit(1);
00216        }
00217        *login_time++=0;
00218        if ((data=strchr(login_time, ' ')) == 0)
00219        {
00220               fprintf(stderr, "CRIT: maildircache: Cache create failure - authentication process crashed.\n");
00221               exit(1);
00222        }
00223        *data++=0;
00224 
00225        login_time_n=0;
00226        while (*login_time >= '0' && *login_time <= '9')
00227               login_time_n = login_time_n * 10 + (*login_time++ -'0');
00228 
00229        f=create_cache_name(userid, login_time_n);
00230 
00231        if (!f)
00232               exit(0);
00233 
00234        if ((fp=fopen(f, "w")) == 0)       /* Try creating subdirs */
00235        {
00236               char   *p=f+strlen(cachedir);
00237 
00238               while (p && *p == '/')
00239               {
00240                      *p=0;
00241                      mkdir(f, 0700);
00242                      *p='/';
00243                      p=strchr(p+1, '/');
00244               }
00245 
00246               if ((fp=fopen(f, "w")) == 0)
00247               {
00248                      fprintf(stderr, "CRIT: maildircache: Cache create failure - unable to create file %s.\n", f);
00249                      exit(1);
00250               }
00251        }
00252        free(f);
00253 
00254        if ( fwrite(data, strlen(data), 1, fp) != 1 || fflush(fp)
00255             || ferror(fp))
00256        {
00257               fclose(fp);
00258               unlink(f);    /* Problems */
00259               fprintf(stderr, "CRIT: maildircache: Cache create failure - write error.\n");
00260               exit(1);
00261        }
00262        else   fclose(fp);
00263        exit(0);
00264 }
00265 
00266 static int savebuf(char *p, int l)
00267 {
00268        while (l)
00269        {
00270        int    n=write(childpipe, p, l);
00271 
00272               if (n <= 0)   return (-1);
00273               p += n;
00274               l -= n;
00275        }
00276        return (0);
00277 }
00278 
00279 void maildir_cache_save(const char *a, time_t b, const char *homedir,
00280                     uid_t u, gid_t g)
00281 {
00282 char   buf[2048];
00283 char   buf2[NUMBUFSIZE];
00284 pid_t  p;
00285 int    waitstat;
00286 
00287        strcat(strcpy(buf, a), " ");
00288        strcat(strcat(buf, libmail_str_time_t(b, buf2)), " ");
00289        strcat(strcat(buf, libmail_str_uid_t(u, buf2)), " ");
00290        strcat(strcat(buf, libmail_str_gid_t(g, buf2)), " ");
00291        strncat(buf, homedir, sizeof(buf)-2-strlen(homedir));
00292        strcat(buf, "\n");
00293 
00294        if (savebuf(buf, strlen(buf)) == 0)
00295        {
00296        int    i;
00297 
00298               for (i=0; authvars[i]; i++)
00299               {
00300               const char *p;
00301 
00302                      strcat(strcpy(buf, authvars[i]), "=");
00303                      p=getenv(authvars[i]);
00304                      if (!p || strlen(p)+strlen(buf) >= sizeof(buf)-2 ||
00305                             strchr(p, '\n'))
00306                             continue;
00307                      strcat(strcat(buf, p), "\n");
00308                      if (savebuf(buf, strlen(buf)))     break;
00309               }
00310        }
00311        close(childpipe);
00312        while ((p=wait(&waitstat)) != -1 && p != childproc)
00313               ;
00314        childproc= -1;
00315 }
00316 
00317 void maildir_cache_cancel()
00318 {
00319        if (childproc > 0)
00320        {
00321               if (write(childpipe, "CANCELLED\n", 10) < 0)
00322                      perror("write");
00323 
00324               close(childpipe);
00325        }
00326 }
00327 
00328 int maildir_cache_search(const char *a, time_t b,
00329                       int (*callback_func)(uid_t, gid_t, const char *,
00330                                          void *), void *callback_arg)
00331 {
00332        char *f=create_cache_name(a, b);
00333        FILE *fp;
00334        uid_t  u;
00335        gid_t  g;
00336        char dir[1024];
00337        int    n;
00338        int    c;
00339 
00340        if (!f)
00341               return (-1);
00342        fp=fopen(f, "r");
00343        free(f);
00344        if (!fp)
00345               return (-1);
00346 
00347        u=0;
00348        while ((c=getc(fp)) != ' ')
00349        {
00350               if (c < '0' || c > '9')
00351               {
00352                      fclose(fp);
00353                      return (-1);
00354               }
00355               u=u*10 + (c-'0');
00356        }
00357 
00358        g=0;
00359        while ((c=getc(fp)) != ' ')
00360        {
00361               if (c < '0' || c > '9')
00362               {
00363                      fclose(fp);
00364                      return (-1);
00365               }
00366               g=g*10 + (c-'0');
00367        }
00368 
00369        for (n=0; (c=getc(fp)) != EOF; n++)
00370        {
00371               if (c == '\n')       break;
00372               if (n >= sizeof(dir)-1)
00373               {
00374                      fclose(fp);
00375                      fprintf(stderr, "CRIT: maildircache: Cache record overflow.\n");
00376                      return (-1);
00377               }
00378               dir[n]=(char)c;
00379        }
00380        dir[n]=0;
00381 
00382        if ((n=(*callback_func)(u, g, dir, callback_arg)) != 0)
00383        {
00384               fclose(fp);
00385               return (n);
00386        }
00387 
00388        if (c != EOF)
00389        {
00390               while (fgets(dir, sizeof(dir), fp))
00391               {
00392               char   *q;
00393 
00394                      if ( (q=strchr(dir, '\n')) == 0)
00395                      {
00396                             fclose(fp);
00397                             fprintf(stderr, "CRIT: maildircache: Cache record overflow.\n");
00398                             return (-1);
00399                      }
00400                      *q=0;
00401 
00402                      for (n=0; authvars[n]; n++)
00403                      {
00404                             int l=strlen(authvars[n]);
00405 
00406                             if (strncmp(dir, authvars[n], l) == 0 &&
00407                                 dir[l] == '=')
00408                             {
00409                                    char *s=strdup(dir);
00410 
00411                                    if (!s)
00412                                    {
00413                                           fclose(fp);
00414                                           perror("CRIT: maildircache: malloc failed");
00415                                           return (-1);
00416                                    }
00417 
00418                                    putenv(s);
00419 
00420                                    if (authvals[n])
00421                                           free(authvals[n]);
00422                                    authvals[n]=s;
00423                                    break;
00424                             }
00425                      }
00426               }
00427               fclose(fp);
00428        }
00429        return (0);
00430 }
00431 
00432 struct purge_list {
00433        struct purge_list *next;
00434        char *n;
00435 } ;
00436 
00437 static void add_purge_list(struct purge_list **p, const char *a)
00438 {
00439        char *c=malloc(strlen(a) + 1);
00440        struct purge_list *pp;
00441 
00442        if (!c)
00443               return;
00444 
00445        pp=malloc(sizeof(struct purge_list));
00446        if (!pp)
00447        {
00448               free(c);
00449               return;
00450        }
00451 
00452        pp->next=*p;
00453 
00454        *p=pp;
00455        pp->n=c;
00456        strcpy(c, a);
00457 }
00458 
00459 static void rmrf(const char *);
00460 
00461 void maildir_cache_purge()
00462 {
00463        time_t now;
00464        pid_t p;
00465        int waitstat;
00466        struct passwd *pw;
00467        struct purge_list *pl;
00468        DIR *dirp;
00469        struct dirent *de;
00470        struct sigaction sa, oldsa;
00471 
00472        time(&now);
00473 
00474        if (lastclean && lastclean >= now - expinterval)
00475               return;
00476 
00477        lastclean=now;
00478 
00479        memset(&sa, 0, sizeof(sa));
00480 
00481        sa.sa_handler=SIG_DFL;
00482        if (sigaction(SIGCHLD, &sa, &oldsa) < 0)
00483        {
00484               perror("sigaction");
00485               return;
00486        }
00487 
00488        p=fork();
00489 
00490        if (p < 0)
00491               return;
00492 
00493        if (p)
00494        {
00495               pid_t p2;
00496 
00497               while ((p2=wait(&waitstat)) >= 0 && p2 != p)
00498                      ;
00499 
00500               sigaction(SIGCHLD, &oldsa, NULL);
00501               return;
00502        }
00503 
00504        p=fork();
00505 
00506        if (p)
00507               exit(0);
00508 
00509        pw=getpwnam(cacheowner);
00510 
00511        if (!pw)
00512        {
00513               fprintf(stderr, "CRIT: maildircache: no such user %s - cannot purge login cache dir\n",
00514                      cacheowner);
00515               exit(0);
00516        }
00517 
00518        if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0)
00519        {
00520               fprintf(stderr, "CRIT: maildircache: cannot change to uid/gid for %s - cannot purge login cache dir\n",
00521                      cacheowner);
00522               exit(0);
00523        }
00524 
00525        if (chdir(cachedir))
00526        {
00527               fprintf(stderr, "CRIT: maildircache: cannot change dir to %s\n", cachedir);
00528               exit(0);
00529        }
00530 
00531        pl=NULL;
00532 
00533        dirp=opendir(".");
00534 
00535        now /= expinterval;
00536        --now;
00537 
00538        while (dirp && (de=readdir(dirp)) != NULL)
00539        {
00540               if (!isdigit((int)(unsigned char)de->d_name[0]))
00541                      continue;
00542 
00543               if (atol(de->d_name) >= now)
00544                      continue;
00545 
00546               add_purge_list(&pl, de->d_name);
00547        }
00548        if (dirp)
00549               closedir(dirp);
00550 
00551        while (pl)
00552        {
00553               struct purge_list *p=pl;
00554 
00555               pl=pl->next;
00556 
00557               rmrf(p->n);
00558               free(p->n);
00559               free(p);
00560        }
00561        exit(0);
00562 }
00563 
00564 static void rmrf(const char *d)
00565 {
00566        DIR *dirp;
00567        struct dirent *de;
00568        struct purge_list *pl=NULL, *p;
00569 
00570        if (chdir(d))
00571               return;
00572 
00573        dirp=opendir(".");
00574 
00575        while (dirp && (de=readdir(dirp)) != NULL)
00576        {
00577               if (strcmp(de->d_name, ".") == 0 ||
00578                   strcmp(de->d_name, "..") == 0)
00579                      continue;
00580 
00581               add_purge_list(&pl, de->d_name);
00582        }
00583        if (dirp)
00584               closedir(dirp);
00585 
00586        while (pl)
00587        {
00588               p=pl;
00589 
00590               pl=pl->next;
00591 
00592               if (unlink(p->n))
00593                      rmrf(p->n);
00594               free(p->n);
00595               free(p);
00596        }
00597 
00598        if (chdir("..") < 0 || rmdir(d) < 0)
00599        {
00600               fprintf(stderr, "CRIT: maildircache: cannot chdir to .. while purging login cache\n");
00601               exit(1);
00602        }
00603 }