Back to index

courier  0.68.2
pop3dserver.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2011 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 **
00005 */
00006 
00007 #if    HAVE_CONFIG_H
00008 #include      "config.h"
00009 #endif
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 #if TIME_WITH_SYS_TIME
00032 #include <sys/time.h>
00033 #include <time.h>
00034 #else
00035 #if HAVE_SYS_TIME_H
00036 #include <sys/time.h>
00037 #else
00038 #include <time.h>
00039 #endif
00040 #endif
00041 #include      <stdio.h>
00042 #include      <stdlib.h>
00043 #include      <string.h>
00044 #include      <ctype.h>
00045 #if    HAVE_UNISTD_H
00046 #include      <unistd.h>
00047 #endif
00048 #include      <signal.h>
00049 #include      <errno.h>
00050 #include      "numlib/numlib.h"
00051 #include      "maildir/config.h"
00052 #include      "maildir/maildirmisc.h"
00053 #include      "maildir/maildirquota.h"
00054 #include      "maildir/maildirgetquota.h"
00055 #include      "maildir/maildircreate.h"
00056 #include      "maildir/loginexec.h"
00057 #include      "courierauth.h"
00058 
00059 #define POP3DLIST "courierpop3dsizelist"
00060 
00061 extern void pop3dcapa();
00062 static void acctout(const char *disc);
00063 
00064 static const char *authaddr, *remoteip, *remoteport;
00065 
00066 struct msglist {
00067        struct msglist *next;
00068        char *filename;
00069        int isdeleted;
00070        int isnew;
00071        off_t size;
00072 
00073        struct {
00074               unsigned long uidv;
00075               unsigned long n;
00076        } uid;
00077        } ;
00078 
00079 static struct msglist *msglist_l;
00080 static struct msglist **msglist_a;
00081 static unsigned msglist_cnt;
00082 
00083 static struct stat enomem_stat;
00084 static int enomem_1msg;
00085 
00086 /*
00087 ** When a disk error occurs while saving an updated courierpop3dsizelist
00088 ** file, proceed to recover as follows:
00089 **
00090 ** If there's at least one existing message that's found in the old
00091 ** courierpop3dsizelist, then ignore all new messages that were not found
00092 ** in the old courierpop3dsizelist, and a new uid/uidv was assigned to them.
00093 ** Therefore, the client doesn't see any new messages.  Hopefully, after
00094 ** at least one existing message gets deleted, there'll be room to create
00095 ** a new courierpop3dsizelist file next time, and record new messages.
00096 **
00097 ** If none of the messages in the maildir could be found in the
00098 ** courierpop3dsizelist, take the first message in the maildir only, and
00099 ** use a UIDL that's derived from the message's dev_t/ino_t.  The client
00100 ** will see that message only.  After deleting it, hopefully a new
00101 ** courierpop3dsizelist file could be written out next time.
00102 */
00103 
00104 
00105 static unsigned long top_count=0;
00106 static unsigned long retr_count=0;
00107 
00108 static unsigned long bytes_sent_count=0;
00109 static unsigned long bytes_received_count=0;
00110 
00111 static unsigned long uidv=0;
00112 static int convert_v0=0;
00113 
00114 static time_t start_time;
00115 
00116 /*
00117 ** The RFC is pretty strict in stating that octet size must count the CR
00118 ** in the CRLF endofline.
00119 */
00120 
00121 static void calcsize(struct msglist *m)
00122 {
00123 FILE   *f=fopen(m->filename, "r");
00124 int    c, lastc;
00125 
00126        m->size=0;
00127        if (f == 0)
00128        {
00129               perror("calcsize fopen");
00130               return;
00131        }
00132        lastc='\n';
00133        while ((c=getc(f)) >= 0)
00134        {
00135               if (c == '\n')       ++m->size;
00136               ++m->size;
00137               lastc=c;
00138        }
00139        if (lastc != '\n')   m->size += 2;
00140 
00141        if (ferror(f))
00142        {
00143               perror("calcsize ferror");
00144               return;
00145        }
00146        fclose(f);
00147 }
00148 
00149 static FILE *
00150 openpop3dlist()
00151 {
00152        int tries;
00153        FILE *fp;
00154 
00155        tries = 0;
00156        do {
00157               fp = fopen(POP3DLIST, "r");
00158               if (fp != NULL)
00159                      return (fp);
00160               if (errno != ESTALE) {
00161                      if (errno != ENOENT)
00162                             perror("failed to open " POP3DLIST " file");
00163                      return (NULL);
00164               }
00165               ++tries;
00166        } while (tries < 3); /* somewhat arbitrary */
00167        fprintf(stderr, "failed to open pop3dlist file after retries\n");
00168        return NULL;
00169 }
00170 
00171 /*
00172 ** Read courierpop3dsizelist
00173 */
00174 
00175 static int cmpmsgs(const void *a, const void *b);
00176 
00177 static struct msglist **readpop3dlist(unsigned long *uid)
00178 {
00179        struct msglist **a;
00180        struct msglist *list=NULL;
00181        size_t mcnt=0;
00182 
00183        char linebuf[1024];
00184 
00185        FILE *fp=openpop3dlist();
00186 
00187        size_t i;
00188        int vernum=0;
00189 
00190        uidv=time(NULL);
00191 
00192        convert_v0=0;
00193 
00194        if (fp == NULL ||
00195            fgets(linebuf, sizeof(linebuf)-1, fp) == NULL ||
00196            linebuf[0] != '/' || sscanf(linebuf+1, "%d %lu %lu", &vernum,
00197                                    uid, &uidv)
00198            < 2 || (vernum != 1 && vernum != 2))
00199        {
00200               if (fp == NULL)
00201                      convert_v0=1;
00202 
00203               if (vernum == 0 && fp && fseek(fp, 0L, SEEK_SET) >= 0)
00204               {
00205                      /* Old version 0 format courierpop3dsizelist file */
00206               }
00207               else
00208               {
00209                      if (fp)
00210                             fclose(fp);
00211                      fp=NULL;
00212               }
00213        }
00214 
00215        if (fp)
00216        {
00217               struct msglist *m;
00218 
00219               char *p, *q;
00220 
00221               size_t n=0;
00222               int ch;
00223 
00224               while ((ch=getc(fp)) != EOF)
00225               {
00226                      unsigned long sz;
00227 
00228                      if (ch != '\n')
00229                      {
00230                             if (n < sizeof(linebuf)-3)
00231                                    linebuf[n++]=ch;
00232                             continue;
00233                      }
00234                      linebuf[n]=0;
00235                      n=0;
00236 
00237                      if (vernum == 0)
00238                             strcat(linebuf, " 0");
00239                      /* Convert version 0 to version 1 format - PRESTO! */
00240 
00241                      if ((p=strrchr(linebuf, ' ')) == NULL)
00242                             continue;
00243                      *p=0;
00244                      if ((q=strrchr(linebuf, ' ')) == NULL)
00245                             continue;
00246                      *p=' ';
00247                      p=q;
00248                      *p++=0;
00249 
00250                      if (linebuf[0] == 0)
00251                             continue;
00252 
00253                      if ((m=(struct msglist *)malloc(sizeof(struct
00254                                                         msglist))) == 0)
00255                      {
00256                             perror("malloc");
00257                             exit(1);
00258                      }
00259 
00260                      if ((m->filename=strdup(linebuf)) == NULL)
00261                      {
00262                             perror("malloc");
00263                             exit(1);
00264                      }
00265 
00266                      switch (sscanf(p, "%lu %lu:%lu", &sz,
00267                                    &m->uid.n, &m->uid.uidv)) {
00268                      case 2:
00269                             m->uid.uidv=0;
00270                             /* FALLTHROUGH */
00271                      case 3:
00272                             m->size=sz;
00273                             m->next=list;
00274                             list=m;
00275                             ++mcnt;
00276                             break;
00277                      default:
00278                             free(m->filename);
00279                             free(m);
00280                      }
00281               }
00282               fclose(fp);
00283        }
00284        if ((a=(struct msglist **)malloc((mcnt+1)
00285                                     *sizeof(struct msglist *))) == 0)
00286        {
00287               perror("malloc");
00288               exit(1);
00289        }
00290 
00291        for (i=0; list; list=list->next)
00292               a[i++]=list;
00293 
00294        a[i]=NULL;
00295        qsort(a, i, sizeof(*a), cmpmsgs);
00296 
00297        return a;
00298 }
00299 
00300 static int savepop3dlist(struct msglist **a, size_t cnt,
00301                        unsigned long uid)
00302 {
00303        FILE *fp;
00304        size_t i;
00305 
00306        struct maildir_tmpcreate_info createInfo;
00307 
00308        maildir_tmpcreate_init(&createInfo);
00309 
00310        createInfo.uniq="pop3";
00311        createInfo.doordie=1;
00312 
00313        if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
00314        {
00315               maildir_tmpcreate_free(&createInfo);
00316               return -1;
00317        }
00318 
00319        fprintf(fp, "/2 %lu %lu\n", uid, uidv);
00320 
00321        for (i=0; i<cnt; i++)
00322        {
00323               char *p=a[i]->filename;
00324               char *q;
00325 
00326               if ((q=strrchr(p, '/')) != NULL)
00327                      p=q+1;
00328 
00329               fprintf(fp, "%s %lu %lu:%lu\n", p, (unsigned long)a[i]->size,
00330                      a[i]->uid.n, a[i]->uid.uidv);
00331        }
00332 
00333        if (fflush(fp) || ferror(fp))
00334        {
00335               fclose(fp);
00336               unlink(createInfo.tmpname);
00337               maildir_tmpcreate_free(&createInfo);
00338               return -1;
00339        }
00340 
00341        if (fclose(fp) ||
00342            rename(createInfo.tmpname, POP3DLIST) < 0)
00343        {
00344               unlink(createInfo.tmpname);
00345               maildir_tmpcreate_free(&createInfo);
00346               return -1;
00347        }
00348 
00349        maildir_tmpcreate_free(&createInfo);
00350        return 0;
00351 }
00352 
00353 /* Scan cur, and pick up all messages contained therein */
00354 
00355 static int scancur()
00356 {
00357 DIR    *dirp;
00358 struct dirent *de;
00359 struct msglist *m;
00360 
00361        if ((dirp=opendir("cur")) == 0)
00362        {
00363               perror("scancur opendir(\"cur\")");
00364               return 1;
00365        }
00366 
00367        while ((de=readdir(dirp)) != 0)
00368        {
00369               if ( de->d_name[0] == '.' ) continue;
00370 
00371               if ((m=(struct msglist *)malloc(sizeof(struct msglist))) == 0)
00372               {
00373                      perror("malloc");
00374                      exit(1);
00375               }
00376               if ((m->filename=(char *)malloc(strlen(de->d_name)+5)) == 0)
00377               {
00378                      free( (char *)m);
00379                      perror("malloc");
00380                      exit(1);
00381               }
00382               strcat(strcpy(m->filename, "cur/"), de->d_name);
00383               m->isdeleted=0;
00384               m->next=msglist_l;
00385               msglist_l=m;
00386               msglist_cnt++;
00387        }
00388        closedir(dirp);
00389        return 0;
00390 }
00391 
00392 /*
00393 ** When sorting messages, sort on the arrival date - the first part of the
00394 ** name of the file in the maildir is the timestamp.
00395 */
00396 
00397 static int cmpmsgs(const void *a, const void *b)
00398 {
00399        const char *aname=(*(struct msglist **)a)->filename;
00400        const char *bname=(*(struct msglist **)b)->filename;
00401        const char *ap=strrchr(aname, '/');
00402        const char *bp=strrchr(bname, '/');
00403        long   na, nb;
00404 
00405        if (ap)
00406               ++ap;
00407        else
00408               ap=aname;
00409 
00410        if (bp)
00411               ++bp;
00412        else
00413               bp=bname;
00414  
00415        na=atol(ap);
00416        nb=atol(bp);
00417 
00418        if (na < nb)  return (-1);
00419        if (na > nb)  return (1);
00420 
00421        while (*ap || *bp)
00422        {
00423               if (*ap == MDIRSEP[0] && *bp == MDIRSEP[0])
00424                      break;
00425 
00426               if (*ap < *bp)
00427                      return -1;
00428               if (*ap > *bp)
00429                      return 1;
00430               ++ap;
00431               ++bp;
00432        }
00433 
00434        return 0;
00435 }
00436 
00437 static void sortmsgs()
00438 {
00439        size_t i, n;
00440        struct msglist *m;
00441        struct msglist **prev_list;
00442        int savesizes=0;
00443 
00444        unsigned long nextuid;
00445 
00446        if (msglist_cnt == 0)       return;
00447 
00448        if ((msglist_a=(struct msglist **)malloc(
00449                      msglist_cnt*sizeof(struct msglist *))) == 0)
00450        {
00451               perror("malloc");
00452               msglist_cnt=0;
00453               return;
00454        }
00455 
00456        for (i=0, m=msglist_l; m; m=m->next, i++)
00457        {
00458               m->isnew=0;
00459               msglist_a[i]=m;
00460        }
00461        qsort(msglist_a, msglist_cnt, sizeof(*msglist_a), cmpmsgs);
00462 
00463        nextuid=1;
00464 
00465        prev_list=readpop3dlist(&nextuid);
00466 
00467        n=0;
00468 
00469        for (i=0; i<msglist_cnt; i++)
00470        {
00471               while (prev_list[n] &&
00472                      cmpmsgs(&prev_list[n], &msglist_a[i]) < 0)
00473               {
00474                      ++n;
00475                      savesizes=1;
00476               }
00477 
00478               if (prev_list[n] &&
00479                   cmpmsgs(&prev_list[n], &msglist_a[i]) == 0)
00480               {
00481                      msglist_a[i]->size=prev_list[n]->size;
00482                      msglist_a[i]->uid=prev_list[n]->uid;
00483                      n++;
00484               }
00485               else
00486               {
00487                      msglist_a[i]->uid.n=nextuid++;
00488                      msglist_a[i]->uid.uidv=uidv;
00489                      msglist_a[i]->isnew=1;
00490                      if (convert_v0)
00491                             msglist_a[i]->uid.n=0;
00492 
00493                      calcsize(msglist_a[i]);
00494                      savesizes=1;
00495               }      
00496        }
00497 
00498        if (prev_list[n])
00499               savesizes=1;
00500 
00501        for (i=0; prev_list[i]; i++)
00502        {
00503               free(prev_list[i]->filename);
00504               free(prev_list[i]);
00505        }
00506 
00507        free(prev_list);
00508 
00509        if (savesizes && savepop3dlist(msglist_a, msglist_cnt, nextuid) < 0)
00510        {
00511               fprintf(stderr, "ERR: Error while saving courierpop3dsizelist"
00512                      ", user=%s\n", authaddr);
00513 
00514               for (i=n=0; i<msglist_cnt; i++)
00515               {
00516                      if (msglist_a[i]->isnew)
00517                             continue;
00518 
00519                      msglist_a[n]=msglist_a[i];
00520                      ++n;
00521               }
00522 
00523               if (n == 0 && n < msglist_cnt &&
00524                   stat(msglist_a[0]->filename, &enomem_stat) == 0)
00525               {
00526                      enomem_1msg=1;
00527                      ++n;
00528               }
00529               msglist_cnt=n;
00530 
00531        }
00532 }
00533 
00534 static void mkupper(char *p)
00535 {
00536        while (*p)
00537        {
00538               *p=toupper(*p);
00539               p++;
00540        }
00541 }
00542 
00543 static void cleanup()
00544 {
00545        unsigned i;
00546        const char *cp=getenv("POP3_LOG_DELETIONS");
00547        int log_deletions= cp && *cp != '0';
00548 
00549        int64_t deleted_bytes=0;
00550        int64_t deleted_messages=0;
00551 
00552        for (i=0; i<msglist_cnt; i++)
00553               if (msglist_a[i]->isdeleted)
00554               {
00555                      unsigned long un=0;
00556 
00557                      const char *filename=msglist_a[i]->filename;
00558 
00559                      if (maildirquota_countfile(filename))
00560                      {
00561                             if (maildir_parsequota(filename, &un))
00562                             {
00563                                    struct stat stat_buf;
00564 
00565                                    if (stat(filename, &stat_buf) == 0)
00566                                           un=stat_buf.st_size;
00567                             }
00568                      }
00569 
00570                      if (log_deletions)
00571                             fprintf(stderr, "INFO: DELETED, user=%s, ip=[%s], filename=%s\n",
00572                                    getenv("AUTHENTICATED"),
00573                                    getenv("TCPREMOTEIP"),
00574                                    msglist_a[i]->filename);
00575 
00576                      if (unlink(msglist_a[i]->filename))
00577                             un=0;
00578 
00579                      if (un)
00580                      {
00581                             deleted_bytes -= un;
00582                             deleted_messages -= 1;
00583                      }
00584               }
00585 
00586        if (deleted_messages < 0)
00587               maildir_quota_deleted(".", deleted_bytes, deleted_messages);
00588 
00589        return;
00590 }
00591 
00592 #define printed(c) do {int cnt=(c); if (cnt > 0)               \
00593                                       bytes_sent_count += cnt; \
00594        } while(0)
00595 
00596 #define printchar(ch) do { putchar((ch)); printed(1); } while(0);
00597 
00598 /* POP3 STAT */
00599 
00600 static void do_stat()
00601 {
00602 off_t  n=0;
00603 unsigned i, j;
00604 char   buf[NUMBUFSIZE];
00605 
00606        j=0;
00607        for (i=0; i<msglist_cnt; i++)
00608        {
00609               if (msglist_a[i]->isdeleted)       continue;
00610               n += msglist_a[i]->size;
00611               ++j;
00612        }
00613 
00614        printed(printf("+OK %u %s\r\n", j, libmail_str_off_t(n, buf)));
00615        fflush(stdout);
00616 }
00617 
00618 static unsigned getmsgnum(const char *p)
00619 {
00620 unsigned i;
00621 
00622        if (!p || (i=atoi(p)) > msglist_cnt || i == 0 ||
00623               msglist_a[i-1]->isdeleted)
00624        {
00625               printed(printf("-ERR Invalid message number.\r\n"));
00626               fflush(stdout);
00627               return (0);
00628        }
00629        return (i);
00630 }
00631 
00632 /* POP3 LIST */
00633 
00634 static void do_list(const char *msgnum)
00635 {
00636 unsigned i;
00637 char   buf[NUMBUFSIZE];
00638 
00639        if (msgnum)
00640        {
00641               if ((i=getmsgnum(msgnum)) != 0)
00642               {
00643                      printed(printf("+OK %u %s\r\n", i,
00644                                    libmail_str_off_t(msglist_a[i-1]->size,
00645                                                   buf)));
00646                      fflush(stdout);
00647               }
00648               return;
00649        }
00650 
00651        printed(printf("+OK POP3 clients that break here, they violate STD53.\r\n"));
00652        for (i=0; i<msglist_cnt; i++)
00653        {
00654               if (msglist_a[i]->isdeleted)       continue;
00655               printed(printf("%u %s\r\n", i+1, libmail_str_off_t(msglist_a[i]->size, buf)));
00656        }
00657        printed(printf(".\r\n"));
00658        fflush(stdout);
00659 }
00660 
00661 /* RETR and TOP POP3 commands */
00662 
00663 static void do_retr(unsigned i, unsigned *lptr)
00664 {
00665 FILE   *f=fopen(msglist_a[i]->filename, "r");
00666 char   *p;
00667 int    c, lastc='\n';
00668 int    inheader=1;
00669 char   buf[NUMBUFSIZE];
00670 unsigned long *cntr;
00671 
00672        if (!f)
00673        {
00674               printed(printf("-ERR Can't open the message file - it's gone!\r\n"));
00675               fflush(stdout);
00676               return;
00677        }
00678        printed(printf( (lptr ? "+OK headers follow.\r\n":"+OK %s octets follow.\r\n"),
00679                      libmail_str_off_t(msglist_a[i]->size, buf)));
00680 
00681        cntr= &retr_count;
00682        if (lptr)
00683               cntr= &top_count;
00684 
00685        for (lastc=0; (c=getc(f)) >= 0; lastc=c)
00686        {
00687               if (lastc == '\n')
00688               {
00689                      if (lptr)
00690                      {
00691                             if (inheader)
00692                             {
00693                                    if (c == '\n')       inheader=0;
00694                             }
00695                             else if ( (*lptr)-- == 0)   break;
00696                      }
00697 
00698                      if (c == '.')
00699                             printchar('.');
00700               }
00701               if (c == '\n')       printchar('\r');
00702               printchar(c);
00703               ++*cntr;
00704        }
00705        if (ferror(f)) {
00706               /* Oops! All we can do is drop the connection */
00707               fprintf(stderr, "ERR: I/O error while reading message file %s: %s\n",
00708                      msglist_a[i]->filename, strerror(errno));
00709               acctout("INFO: I/O error disconnect");
00710               exit(1);
00711        }
00712        if (lastc != '\n')   printed(printf("\r\n"));
00713        printed(printf(".\r\n"));
00714        fflush(stdout);
00715        fclose(f);
00716        if (lptr)     return;
00717 
00718        if ((p=strchr(msglist_a[i]->filename, MDIRSEP[0])) != 0 &&
00719               (p[1] != '2' || p[2] != ',' || strchr(p, 'S') != 0))
00720               return;
00721 
00722        if ((p=malloc(strlen(msglist_a[i]->filename)+5)) == 0)
00723               return;
00724 
00725        strcpy(p, msglist_a[i]->filename);
00726        if (strchr(p, MDIRSEP[0]) == 0)    strcat(p, MDIRSEP "2,");
00727        strcat(p, "S");
00728 
00729        if (lptr      /* Don't mark as seen for TOP */
00730            || rename(msglist_a[i]->filename, p))
00731        {
00732               free(p);
00733               return;
00734        }
00735        free(msglist_a[i]->filename);
00736        msglist_a[i]->filename=p;
00737 }
00738 
00739 /*
00740 ** The UIDL of the message is really just its filename, up to the first MDIRSEP character
00741 */
00742 
00743 static void print_uidl(unsigned i)
00744 {
00745        const char *p;
00746 
00747        if (enomem_1msg)
00748               /* Error recovery - out of disk space, see comments
00749               ** at the beginning of this file.
00750               */
00751        {
00752               char dev_buf[NUMBUFSIZE];
00753               char ino_buf[NUMBUFSIZE];
00754               char mtime_buf[NUMBUFSIZE];
00755 
00756               printed(printf("ENOMEM-%s-%s-%s\r\n",
00757                             libmail_strh_time_t(enomem_stat.st_mtime, mtime_buf),
00758                             libmail_strh_dev_t(enomem_stat.st_dev, dev_buf),
00759                             libmail_strh_ino_t(enomem_stat.st_ino, ino_buf))
00760                      );
00761               return;
00762        }
00763 
00764        if (msglist_a[i]->uid.n != 0)
00765        {
00766               /* VERSION 1 and VERSION 2 UIDL */
00767 
00768               printed(printf((msglist_a[i]->uid.uidv ?
00769                             "UID%lu-%lu\r\n":"UID%lu\r\n"),
00770                             msglist_a[i]->uid.n, msglist_a[i]->uid.uidv));
00771               return;
00772        }
00773 
00774        /* VERSION 0 UIDL */
00775 
00776        p=strchr(msglist_a[i]->filename, '/')+1;
00777 
00778        while (*p && *p != MDIRSEP[0])
00779        {
00780               if (*p < 0x21 || *p > 0x7E || *p == '\'' || *p == '"' ||
00781                      *p == '+')
00782                      printed(printf("+%02X", (int)(unsigned char)*p));
00783               else
00784                      printchar(*p);
00785               ++p;
00786        }
00787        printed(printf("\r\n"));
00788 }
00789 
00790 static void do_uidl(const char *msgnum)
00791 {
00792 unsigned i;
00793 
00794        if (msgnum)
00795        {
00796               if ((i=getmsgnum(msgnum)) != 0)
00797               {
00798                      printed(printf("+OK %u ", i));
00799                      print_uidl(i-1);
00800                      fflush(stdout);
00801               }
00802               return;
00803        }
00804        printed(printf("+OK\r\n"));
00805        for (i=0; i<msglist_cnt; i++)
00806        {
00807               if (msglist_a[i]->isdeleted)       continue;
00808               printed(printf("%u ", i+1));
00809               print_uidl(i);
00810        }
00811        printed(printf(".\r\n"));
00812        fflush(stdout);
00813 }
00814 
00815 static void acctout(const char *disc)
00816 {
00817        static const char msg2[]=", user=";
00818        static const char msg3[]=", ip=[";
00819        static const char msgport[]="], port=[";
00820        static const char msg4[]="], top=";
00821        static const char msg5[]=", retr=";
00822        static const char msg6[]=", time=";
00823        static const char msg7[]=", stls=1";
00824        static const char msgAR[]=", rcvd=";
00825        static const char msgAS[]=", sent=";
00826 
00827        char num1[NUMBUFSIZE];
00828        char num2[NUMBUFSIZE];
00829        char num3[NUMBUFSIZE];
00830        char numAR[NUMBUFSIZE];
00831        char numAS[NUMBUFSIZE];
00832 
00833        char *p;
00834        const char *q;
00835 
00836        libmail_str_size_t(top_count, num1);
00837        libmail_str_size_t(retr_count, num2);
00838        libmail_str_time_t(time(NULL)-start_time, num3);
00839        libmail_str_size_t(bytes_received_count, numAR);
00840        libmail_str_size_t(bytes_sent_count, numAS);
00841 
00842        p=malloc(strlen(authaddr)+strlen(remoteip)+strlen(remoteport)+strlen(disc)+
00843                strlen(num1)+strlen(num2)+strlen(num3)+
00844                strlen(numAR)+strlen(numAS)+200); /* Should be enough */
00845 
00846        strcpy(p, disc);
00847        strcat(p, msg2);
00848        strcat(p, authaddr);
00849        strcat(p, msg3);
00850        strcat(p, remoteip);
00851        strcat(p, msgport);
00852        strcat(p, remoteport);
00853        strcat(p, msg4);
00854        strcat(p, num1);
00855        strcat(p, msg5);
00856        strcat(p, num2);
00857        strcat(p, msgAR);
00858        strcat(p, numAR);
00859        strcat(p, msgAS);
00860        strcat(p, numAS);
00861        strcat(p, msg6);
00862        strcat(p, num3);
00863 
00864        if ((q=getenv("POP3_TLS")) && atoi(q))
00865               strcat(p, msg7);
00866 
00867        strcat(p, "\n");
00868        if (write(2, p, strlen(p)) < 0)
00869               ; /* make gcc shut up */
00870        free(p);
00871 }
00872 
00873 static RETSIGTYPE bye(int signum)
00874 {
00875        acctout("INFO: TIMEOUT");
00876        exit(0);
00877 #if    RETSIGTYPE != void
00878        return (0);
00879 #endif
00880 }
00881 
00882 static void loop()
00883 {
00884 char   buf[BUFSIZ];
00885 char   *p;
00886 int    c;
00887 
00888        signal(SIGALRM, bye);
00889        while (alarm(300), fgets(buf, sizeof(buf), stdin))
00890        {
00891               bytes_received_count += strlen(buf);
00892 
00893               alarm(0);
00894               if ((p=strchr(buf, '\n')) != 0)
00895                      *p=0;
00896               else while ((c=getc(stdin)) >= 0 && c != '\n')
00897                      ;
00898               p=strtok(buf, " \t\r");
00899               if (!p)       p="";
00900 
00901               mkupper(p);
00902               if (strcmp(p, "QUIT") == 0)
00903               {
00904                      printed(printf("+OK Bye-bye.\r\n"));
00905                      fflush(stdout);
00906                      cleanup();
00907                      acctout("INFO: LOGOUT");
00908                      return;
00909               }
00910 
00911               if (strcmp(p, "STAT") == 0)
00912               {
00913                      do_stat();
00914                      continue;
00915               }
00916 
00917               if (strcmp(p, "LIST") == 0)
00918               {
00919                      do_list(strtok(NULL, " \t\r"));
00920                      continue;
00921               }
00922 
00923               if (strcmp(p, "RETR") == 0)
00924               {
00925               unsigned      i;
00926 
00927                      if ((i=getmsgnum(strtok(NULL, " \t\r"))) == 0)
00928                             continue;
00929 
00930                      do_retr(i-1, 0);
00931                      continue;
00932               }
00933 
00934               if (strcmp(p, "CAPA") == 0)
00935               {
00936                      pop3dcapa();
00937                      continue;
00938               }
00939 
00940               if (strcmp(p, "DELE") == 0)
00941               {
00942               unsigned      i;
00943 
00944                      if ((i=getmsgnum(strtok(NULL, " \t\r"))) == 0)
00945                             continue;
00946 
00947                      msglist_a[i-1]->isdeleted=1;
00948                      printed(printf("+OK Deleted.\r\n"));
00949                      fflush(stdout);
00950                      continue;
00951               }
00952 
00953               if (strcmp(p, "NOOP") == 0)
00954               {
00955                      printed(printf("+OK Yup.\r\n"));
00956                      fflush(stdout);
00957                      continue;
00958               }
00959 
00960               if (strcmp(p, "RSET") == 0)
00961               {
00962               unsigned i;
00963 
00964                      for (i=0; i<msglist_cnt; i++)
00965                             msglist_a[i]->isdeleted=0;
00966                      printed(printf("+OK Resurrected.\r\n"));
00967                      fflush(stdout);
00968                      continue;
00969               }
00970 
00971               if (strcmp(p, "TOP") == 0)
00972               {
00973               unsigned      i, j;
00974               const  char *q;
00975 
00976                      if ((i=getmsgnum(strtok(NULL, " \t\r"))) == 0)
00977                             continue;
00978 
00979                      q=strtok(NULL, " \t\r");
00980 
00981                      if (!q)       goto error;
00982 
00983                      j=atoi(q);
00984                      do_retr(i-1, &j);
00985                      continue;
00986               }
00987 
00988               if (strcmp(p, "UIDL") == 0)
00989               {
00990                      do_uidl(strtok(NULL, " \t\r"));
00991                      continue;
00992               }
00993 
00994 error:
00995               printed(printf("-ERR Invalid command.\r\n"));
00996               fflush(stdout);
00997        }
00998        acctout("INFO: DISCONNECTED");
00999 }
01000 
01001 /* Like every good Maildir reader, we purge the tmp subdirectory */
01002 
01003 static void purgetmp()
01004 {
01005 DIR    *p=opendir("tmp");
01006 time_t t;
01007 struct dirent *de;
01008 struct stat   stat_buf;
01009 char   *n;
01010 
01011        if (!p)       return;
01012        time (&t);
01013        t -= 48L * 60L * 60L;
01014 
01015        while ((de=readdir(p)) != 0)
01016        {
01017               if (de->d_name[0] == '.')   continue;
01018               n=malloc(strlen(de->d_name)+5);
01019               if (!n)       continue;
01020               strcat(strcpy(n, "tmp/"), de->d_name);
01021               if (stat(n, &stat_buf) == 0 && stat_buf.st_mtime < t)
01022                      unlink(n);
01023               free(n);
01024        }
01025        closedir(p);
01026 }
01027 
01028 
01029 #include      <unistd.h>
01030 
01031 int main(int argc, char **argv)
01032 {
01033 char   *p;
01034 
01035 #ifdef HAVE_SETVBUF_IOLBF
01036        setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
01037 #endif
01038        time(&start_time);
01039 
01040        if ((authaddr=getenv("AUTHADDR")) == NULL ||
01041            *authaddr == 0)
01042        {
01043               authaddr=getenv("AUTHENTICATED");
01044               if (authaddr == NULL || *authaddr == 0)
01045                      authaddr="nobody";
01046        }
01047 
01048        if ((remoteip=getenv("TCPREMOTEIP")) == NULL)
01049               remoteip="127.0.0.1";
01050 
01051        if ((remoteport=getenv("TCPREMOTEPORT")) == NULL)
01052               remoteport="0";
01053 
01054        {
01055        struct stat   buf;
01056 
01057               if ( stat(".", &buf) < 0 || buf.st_mode & S_ISVTX)
01058               {
01059                      fprintf(stderr, "INFO: LOCKED, user=%s, ip=[%s], port=[%s]\n",
01060                                                  authaddr, remoteip, remoteport);
01061                      printed(printf("-ERR Your account is temporarily unavailable (+t bit set on home directory).\r\n"));
01062                      exit(0);
01063               }
01064        }
01065 
01066        if (argc > 1)
01067               p=argv[1];
01068        else
01069               p=getenv("MAILDIR");
01070 
01071        if (!p)
01072               p="./Maildir";
01073 
01074        if (chdir(p))
01075        {
01076               fprintf(stderr, "chdir %s: %s\n", p, strerror(errno));
01077               printed(printf("-ERR chdir %s failed\n", p));
01078               fflush(stdout);
01079               exit(1);
01080        }
01081        
01082        maildir_loginexec();
01083 
01084        if (auth_getoptionenvint("disablepop3"))
01085        {
01086               printed(printf("-ERR POP3 access disabled for this account.\r\n"));
01087               fflush(stdout);
01088               exit(1);
01089        }
01090 
01091        if (    auth_getoptionenvint("disableinsecurepop3")
01092            && ((p=getenv("POP3_TLS")) == NULL || !atoi(p)))
01093        {
01094               printed(printf("-ERR POP3 access disabled via insecure connection.\r\n"));
01095               fflush(stdout);
01096               exit(1);
01097        }
01098 
01099        fprintf(stderr, "INFO: LOGIN, user=%s, ip=[%s], port=[%s]\n",
01100                      authaddr,
01101                                    remoteip,
01102                                    remoteport);
01103        fflush(stderr);
01104 
01105        msglist_cnt=0;
01106        msglist_l=0;
01107        msglist_a=0;
01108        purgetmp();
01109        maildir_getnew(".", INBOX, NULL, NULL);
01110        if (scancur())
01111        {
01112               printed(printf("-ERR Maildir invalid (no 'cur' directory)\r\n"));
01113               return (0);
01114        }
01115        sortmsgs();
01116        printed(printf("+OK logged in.\r\n"));
01117        fflush(stdout);
01118        loop();
01119        return (0);
01120 }