summaryrefslogtreecommitdiffstats
path: root/imap/pop3dserver.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /imap/pop3dserver.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'imap/pop3dserver.c')
-rw-r--r--imap/pop3dserver.c1120
1 files changed, 1120 insertions, 0 deletions
diff --git a/imap/pop3dserver.c b/imap/pop3dserver.c
new file mode 100644
index 0000000..13aced9
--- /dev/null
+++ b/imap/pop3dserver.c
@@ -0,0 +1,1120 @@
+/*
+** Copyright 1998 - 2011 Double Precision, Inc.
+** See COPYING for distribution information.
+**
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <sys/types.h>
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#if HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#if HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif
+#include <sys/types.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <signal.h>
+#include <errno.h>
+#include "numlib/numlib.h"
+#include "maildir/config.h"
+#include "maildir/maildirmisc.h"
+#include "maildir/maildirquota.h"
+#include "maildir/maildirgetquota.h"
+#include "maildir/maildircreate.h"
+#include "maildir/loginexec.h"
+#include "courierauth.h"
+
+#define POP3DLIST "courierpop3dsizelist"
+
+extern void pop3dcapa();
+static void acctout(const char *disc);
+
+static const char *authaddr, *remoteip, *remoteport;
+
+struct msglist {
+ struct msglist *next;
+ char *filename;
+ int isdeleted;
+ int isnew;
+ off_t size;
+
+ struct {
+ unsigned long uidv;
+ unsigned long n;
+ } uid;
+ } ;
+
+static struct msglist *msglist_l;
+static struct msglist **msglist_a;
+static unsigned msglist_cnt;
+
+static struct stat enomem_stat;
+static int enomem_1msg;
+
+/*
+** When a disk error occurs while saving an updated courierpop3dsizelist
+** file, proceed to recover as follows:
+**
+** If there's at least one existing message that's found in the old
+** courierpop3dsizelist, then ignore all new messages that were not found
+** in the old courierpop3dsizelist, and a new uid/uidv was assigned to them.
+** Therefore, the client doesn't see any new messages. Hopefully, after
+** at least one existing message gets deleted, there'll be room to create
+** a new courierpop3dsizelist file next time, and record new messages.
+**
+** If none of the messages in the maildir could be found in the
+** courierpop3dsizelist, take the first message in the maildir only, and
+** use a UIDL that's derived from the message's dev_t/ino_t. The client
+** will see that message only. After deleting it, hopefully a new
+** courierpop3dsizelist file could be written out next time.
+*/
+
+
+static unsigned long top_count=0;
+static unsigned long retr_count=0;
+
+static unsigned long bytes_sent_count=0;
+static unsigned long bytes_received_count=0;
+
+static unsigned long uidv=0;
+static int convert_v0=0;
+
+static time_t start_time;
+
+/*
+** The RFC is pretty strict in stating that octet size must count the CR
+** in the CRLF endofline.
+*/
+
+static void calcsize(struct msglist *m)
+{
+FILE *f=fopen(m->filename, "r");
+int c, lastc;
+
+ m->size=0;
+ if (f == 0)
+ {
+ perror("calcsize fopen");
+ return;
+ }
+ lastc='\n';
+ while ((c=getc(f)) >= 0)
+ {
+ if (c == '\n') ++m->size;
+ ++m->size;
+ lastc=c;
+ }
+ if (lastc != '\n') m->size += 2;
+
+ if (ferror(f))
+ {
+ perror("calcsize ferror");
+ return;
+ }
+ fclose(f);
+}
+
+static FILE *
+openpop3dlist()
+{
+ int tries;
+ FILE *fp;
+
+ tries = 0;
+ do {
+ fp = fopen(POP3DLIST, "r");
+ if (fp != NULL)
+ return (fp);
+ if (errno != ESTALE) {
+ if (errno != ENOENT)
+ perror("failed to open " POP3DLIST " file");
+ return (NULL);
+ }
+ ++tries;
+ } while (tries < 3); /* somewhat arbitrary */
+ fprintf(stderr, "failed to open pop3dlist file after retries\n");
+ return NULL;
+}
+
+/*
+** Read courierpop3dsizelist
+*/
+
+static int cmpmsgs(const void *a, const void *b);
+
+static struct msglist **readpop3dlist(unsigned long *uid)
+{
+ struct msglist **a;
+ struct msglist *list=NULL;
+ size_t mcnt=0;
+
+ char linebuf[1024];
+
+ FILE *fp=openpop3dlist();
+
+ size_t i;
+ int vernum=0;
+
+ uidv=time(NULL);
+
+ convert_v0=0;
+
+ if (fp == NULL ||
+ fgets(linebuf, sizeof(linebuf)-1, fp) == NULL ||
+ linebuf[0] != '/' || sscanf(linebuf+1, "%d %lu %lu", &vernum,
+ uid, &uidv)
+ < 2 || (vernum != 1 && vernum != 2))
+ {
+ if (fp == NULL)
+ convert_v0=1;
+
+ if (vernum == 0 && fp && fseek(fp, 0L, SEEK_SET) >= 0)
+ {
+ /* Old version 0 format courierpop3dsizelist file */
+ }
+ else
+ {
+ if (fp)
+ fclose(fp);
+ fp=NULL;
+ }
+ }
+
+ if (fp)
+ {
+ struct msglist *m;
+
+ char *p, *q;
+
+ size_t n=0;
+ int ch;
+
+ while ((ch=getc(fp)) != EOF)
+ {
+ unsigned long sz;
+
+ if (ch != '\n')
+ {
+ if (n < sizeof(linebuf)-3)
+ linebuf[n++]=ch;
+ continue;
+ }
+ linebuf[n]=0;
+ n=0;
+
+ if (vernum == 0)
+ strcat(linebuf, " 0");
+ /* Convert version 0 to version 1 format - PRESTO! */
+
+ if ((p=strrchr(linebuf, ' ')) == NULL)
+ continue;
+ *p=0;
+ if ((q=strrchr(linebuf, ' ')) == NULL)
+ continue;
+ *p=' ';
+ p=q;
+ *p++=0;
+
+ if (linebuf[0] == 0)
+ continue;
+
+ if ((m=(struct msglist *)malloc(sizeof(struct
+ msglist))) == 0)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ if ((m->filename=strdup(linebuf)) == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ switch (sscanf(p, "%lu %lu:%lu", &sz,
+ &m->uid.n, &m->uid.uidv)) {
+ case 2:
+ m->uid.uidv=0;
+ /* FALLTHROUGH */
+ case 3:
+ m->size=sz;
+ m->next=list;
+ list=m;
+ ++mcnt;
+ break;
+ default:
+ free(m->filename);
+ free(m);
+ }
+ }
+ fclose(fp);
+ }
+ if ((a=(struct msglist **)malloc((mcnt+1)
+ *sizeof(struct msglist *))) == 0)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ for (i=0; list; list=list->next)
+ a[i++]=list;
+
+ a[i]=NULL;
+ qsort(a, i, sizeof(*a), cmpmsgs);
+
+ return a;
+}
+
+static int savepop3dlist(struct msglist **a, size_t cnt,
+ unsigned long uid)
+{
+ FILE *fp;
+ size_t i;
+
+ struct maildir_tmpcreate_info createInfo;
+
+ maildir_tmpcreate_init(&createInfo);
+
+ createInfo.uniq="pop3";
+ createInfo.doordie=1;
+
+ if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
+ {
+ maildir_tmpcreate_free(&createInfo);
+ return -1;
+ }
+
+ fprintf(fp, "/2 %lu %lu\n", uid, uidv);
+
+ for (i=0; i<cnt; i++)
+ {
+ char *p=a[i]->filename;
+ char *q;
+
+ if ((q=strrchr(p, '/')) != NULL)
+ p=q+1;
+
+ fprintf(fp, "%s %lu %lu:%lu\n", p, (unsigned long)a[i]->size,
+ a[i]->uid.n, a[i]->uid.uidv);
+ }
+
+ if (fflush(fp) || ferror(fp))
+ {
+ fclose(fp);
+ unlink(createInfo.tmpname);
+ maildir_tmpcreate_free(&createInfo);
+ return -1;
+ }
+
+ if (fclose(fp) ||
+ rename(createInfo.tmpname, POP3DLIST) < 0)
+ {
+ unlink(createInfo.tmpname);
+ maildir_tmpcreate_free(&createInfo);
+ return -1;
+ }
+
+ maildir_tmpcreate_free(&createInfo);
+ return 0;
+}
+
+/* Scan cur, and pick up all messages contained therein */
+
+static int scancur()
+{
+DIR *dirp;
+struct dirent *de;
+struct msglist *m;
+
+ if ((dirp=opendir("cur")) == 0)
+ {
+ perror("scancur opendir(\"cur\")");
+ return 1;
+ }
+
+ while ((de=readdir(dirp)) != 0)
+ {
+ if ( de->d_name[0] == '.' ) continue;
+
+ if ((m=(struct msglist *)malloc(sizeof(struct msglist))) == 0)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ if ((m->filename=(char *)malloc(strlen(de->d_name)+5)) == 0)
+ {
+ free( (char *)m);
+ perror("malloc");
+ exit(1);
+ }
+ strcat(strcpy(m->filename, "cur/"), de->d_name);
+ m->isdeleted=0;
+ m->next=msglist_l;
+ msglist_l=m;
+ msglist_cnt++;
+ }
+ closedir(dirp);
+ return 0;
+}
+
+/*
+** When sorting messages, sort on the arrival date - the first part of the
+** name of the file in the maildir is the timestamp.
+*/
+
+static int cmpmsgs(const void *a, const void *b)
+{
+ const char *aname=(*(struct msglist **)a)->filename;
+ const char *bname=(*(struct msglist **)b)->filename;
+ const char *ap=strrchr(aname, '/');
+ const char *bp=strrchr(bname, '/');
+ long na, nb;
+
+ if (ap)
+ ++ap;
+ else
+ ap=aname;
+
+ if (bp)
+ ++bp;
+ else
+ bp=bname;
+
+ na=atol(ap);
+ nb=atol(bp);
+
+ if (na < nb) return (-1);
+ if (na > nb) return (1);
+
+ while (*ap || *bp)
+ {
+ if (*ap == MDIRSEP[0] && *bp == MDIRSEP[0])
+ break;
+
+ if (*ap < *bp)
+ return -1;
+ if (*ap > *bp)
+ return 1;
+ ++ap;
+ ++bp;
+ }
+
+ return 0;
+}
+
+static void sortmsgs()
+{
+ size_t i, n;
+ struct msglist *m;
+ struct msglist **prev_list;
+ int savesizes=0;
+
+ unsigned long nextuid;
+
+ if (msglist_cnt == 0) return;
+
+ if ((msglist_a=(struct msglist **)malloc(
+ msglist_cnt*sizeof(struct msglist *))) == 0)
+ {
+ perror("malloc");
+ msglist_cnt=0;
+ return;
+ }
+
+ for (i=0, m=msglist_l; m; m=m->next, i++)
+ {
+ m->isnew=0;
+ msglist_a[i]=m;
+ }
+ qsort(msglist_a, msglist_cnt, sizeof(*msglist_a), cmpmsgs);
+
+ nextuid=1;
+
+ prev_list=readpop3dlist(&nextuid);
+
+ n=0;
+
+ for (i=0; i<msglist_cnt; i++)
+ {
+ while (prev_list[n] &&
+ cmpmsgs(&prev_list[n], &msglist_a[i]) < 0)
+ {
+ ++n;
+ savesizes=1;
+ }
+
+ if (prev_list[n] &&
+ cmpmsgs(&prev_list[n], &msglist_a[i]) == 0)
+ {
+ msglist_a[i]->size=prev_list[n]->size;
+ msglist_a[i]->uid=prev_list[n]->uid;
+ n++;
+ }
+ else
+ {
+ msglist_a[i]->uid.n=nextuid++;
+ msglist_a[i]->uid.uidv=uidv;
+ msglist_a[i]->isnew=1;
+ if (convert_v0)
+ msglist_a[i]->uid.n=0;
+
+ calcsize(msglist_a[i]);
+ savesizes=1;
+ }
+ }
+
+ if (prev_list[n])
+ savesizes=1;
+
+ for (i=0; prev_list[i]; i++)
+ {
+ free(prev_list[i]->filename);
+ free(prev_list[i]);
+ }
+
+ free(prev_list);
+
+ if (savesizes && savepop3dlist(msglist_a, msglist_cnt, nextuid) < 0)
+ {
+ fprintf(stderr, "ERR: Error while saving courierpop3dsizelist"
+ ", user=%s\n", authaddr);
+
+ for (i=n=0; i<msglist_cnt; i++)
+ {
+ if (msglist_a[i]->isnew)
+ continue;
+
+ msglist_a[n]=msglist_a[i];
+ ++n;
+ }
+
+ if (n == 0 && n < msglist_cnt &&
+ stat(msglist_a[0]->filename, &enomem_stat) == 0)
+ {
+ enomem_1msg=1;
+ ++n;
+ }
+ msglist_cnt=n;
+
+ }
+}
+
+static void mkupper(char *p)
+{
+ while (*p)
+ {
+ *p=toupper(*p);
+ p++;
+ }
+}
+
+static void cleanup()
+{
+ unsigned i;
+ const char *cp=getenv("POP3_LOG_DELETIONS");
+ int log_deletions= cp && *cp != '0';
+
+ int64_t deleted_bytes=0;
+ int64_t deleted_messages=0;
+
+ for (i=0; i<msglist_cnt; i++)
+ if (msglist_a[i]->isdeleted)
+ {
+ unsigned long un=0;
+
+ const char *filename=msglist_a[i]->filename;
+
+ if (maildirquota_countfile(filename))
+ {
+ if (maildir_parsequota(filename, &un))
+ {
+ struct stat stat_buf;
+
+ if (stat(filename, &stat_buf) == 0)
+ un=stat_buf.st_size;
+ }
+ }
+
+ if (log_deletions)
+ fprintf(stderr, "INFO: DELETED, user=%s, ip=[%s], filename=%s\n",
+ getenv("AUTHENTICATED"),
+ getenv("TCPREMOTEIP"),
+ msglist_a[i]->filename);
+
+ if (unlink(msglist_a[i]->filename))
+ un=0;
+
+ if (un)
+ {
+ deleted_bytes -= un;
+ deleted_messages -= 1;
+ }
+ }
+
+ if (deleted_messages < 0)
+ maildir_quota_deleted(".", deleted_bytes, deleted_messages);
+
+ return;
+}
+
+#define printed(c) do {int cnt=(c); if (cnt > 0) \
+ bytes_sent_count += cnt; \
+ } while(0)
+
+#define printchar(ch) do { putchar((ch)); printed(1); } while(0);
+
+/* POP3 STAT */
+
+static void do_stat()
+{
+off_t n=0;
+unsigned i, j;
+char buf[NUMBUFSIZE];
+
+ j=0;
+ for (i=0; i<msglist_cnt; i++)
+ {
+ if (msglist_a[i]->isdeleted) continue;
+ n += msglist_a[i]->size;
+ ++j;
+ }
+
+ printed(printf("+OK %u %s\r\n", j, libmail_str_off_t(n, buf)));
+ fflush(stdout);
+}
+
+static unsigned getmsgnum(const char *p)
+{
+unsigned i;
+
+ if (!p || (i=atoi(p)) > msglist_cnt || i == 0 ||
+ msglist_a[i-1]->isdeleted)
+ {
+ printed(printf("-ERR Invalid message number.\r\n"));
+ fflush(stdout);
+ return (0);
+ }
+ return (i);
+}
+
+/* POP3 LIST */
+
+static void do_list(const char *msgnum)
+{
+unsigned i;
+char buf[NUMBUFSIZE];
+
+ if (msgnum)
+ {
+ if ((i=getmsgnum(msgnum)) != 0)
+ {
+ printed(printf("+OK %u %s\r\n", i,
+ libmail_str_off_t(msglist_a[i-1]->size,
+ buf)));
+ fflush(stdout);
+ }
+ return;
+ }
+
+ printed(printf("+OK POP3 clients that break here, they violate STD53.\r\n"));
+ for (i=0; i<msglist_cnt; i++)
+ {
+ if (msglist_a[i]->isdeleted) continue;
+ printed(printf("%u %s\r\n", i+1, libmail_str_off_t(msglist_a[i]->size, buf)));
+ }
+ printed(printf(".\r\n"));
+ fflush(stdout);
+}
+
+/* RETR and TOP POP3 commands */
+
+static void do_retr(unsigned i, unsigned *lptr)
+{
+FILE *f=fopen(msglist_a[i]->filename, "r");
+char *p;
+int c, lastc='\n';
+int inheader=1;
+char buf[NUMBUFSIZE];
+unsigned long *cntr;
+
+ if (!f)
+ {
+ printed(printf("-ERR Can't open the message file - it's gone!\r\n"));
+ fflush(stdout);
+ return;
+ }
+ printed(printf( (lptr ? "+OK headers follow.\r\n":"+OK %s octets follow.\r\n"),
+ libmail_str_off_t(msglist_a[i]->size, buf)));
+
+ cntr= &retr_count;
+ if (lptr)
+ cntr= &top_count;
+
+ for (lastc=0; (c=getc(f)) >= 0; lastc=c)
+ {
+ if (lastc == '\n')
+ {
+ if (lptr)
+ {
+ if (inheader)
+ {
+ if (c == '\n') inheader=0;
+ }
+ else if ( (*lptr)-- == 0) break;
+ }
+
+ if (c == '.')
+ printchar('.');
+ }
+ if (c == '\n') printchar('\r');
+ printchar(c);
+ ++*cntr;
+ }
+ if (ferror(f)) {
+ /* Oops! All we can do is drop the connection */
+ fprintf(stderr, "ERR: I/O error while reading message file %s: %s\n",
+ msglist_a[i]->filename, strerror(errno));
+ acctout("INFO: I/O error disconnect");
+ exit(1);
+ }
+ if (lastc != '\n') printed(printf("\r\n"));
+ printed(printf(".\r\n"));
+ fflush(stdout);
+ fclose(f);
+ if (lptr) return;
+
+ if ((p=strchr(msglist_a[i]->filename, MDIRSEP[0])) != 0 &&
+ (p[1] != '2' || p[2] != ',' || strchr(p, 'S') != 0))
+ return;
+
+ if ((p=malloc(strlen(msglist_a[i]->filename)+5)) == 0)
+ return;
+
+ strcpy(p, msglist_a[i]->filename);
+ if (strchr(p, MDIRSEP[0]) == 0) strcat(p, MDIRSEP "2,");
+ strcat(p, "S");
+
+ if (lptr /* Don't mark as seen for TOP */
+ || rename(msglist_a[i]->filename, p))
+ {
+ free(p);
+ return;
+ }
+ free(msglist_a[i]->filename);
+ msglist_a[i]->filename=p;
+}
+
+/*
+** The UIDL of the message is really just its filename, up to the first MDIRSEP character
+*/
+
+static void print_uidl(unsigned i)
+{
+ const char *p;
+
+ if (enomem_1msg)
+ /* Error recovery - out of disk space, see comments
+ ** at the beginning of this file.
+ */
+ {
+ char dev_buf[NUMBUFSIZE];
+ char ino_buf[NUMBUFSIZE];
+ char mtime_buf[NUMBUFSIZE];
+
+ printed(printf("ENOMEM-%s-%s-%s\r\n",
+ libmail_strh_time_t(enomem_stat.st_mtime, mtime_buf),
+ libmail_strh_dev_t(enomem_stat.st_dev, dev_buf),
+ libmail_strh_ino_t(enomem_stat.st_ino, ino_buf))
+ );
+ return;
+ }
+
+ if (msglist_a[i]->uid.n != 0)
+ {
+ /* VERSION 1 and VERSION 2 UIDL */
+
+ printed(printf((msglist_a[i]->uid.uidv ?
+ "UID%lu-%lu\r\n":"UID%lu\r\n"),
+ msglist_a[i]->uid.n, msglist_a[i]->uid.uidv));
+ return;
+ }
+
+ /* VERSION 0 UIDL */
+
+ p=strchr(msglist_a[i]->filename, '/')+1;
+
+ while (*p && *p != MDIRSEP[0])
+ {
+ if (*p < 0x21 || *p > 0x7E || *p == '\'' || *p == '"' ||
+ *p == '+')
+ printed(printf("+%02X", (int)(unsigned char)*p));
+ else
+ printchar(*p);
+ ++p;
+ }
+ printed(printf("\r\n"));
+}
+
+static void do_uidl(const char *msgnum)
+{
+unsigned i;
+
+ if (msgnum)
+ {
+ if ((i=getmsgnum(msgnum)) != 0)
+ {
+ printed(printf("+OK %u ", i));
+ print_uidl(i-1);
+ fflush(stdout);
+ }
+ return;
+ }
+ printed(printf("+OK\r\n"));
+ for (i=0; i<msglist_cnt; i++)
+ {
+ if (msglist_a[i]->isdeleted) continue;
+ printed(printf("%u ", i+1));
+ print_uidl(i);
+ }
+ printed(printf(".\r\n"));
+ fflush(stdout);
+}
+
+static void acctout(const char *disc)
+{
+ static const char msg2[]=", user=";
+ static const char msg3[]=", ip=[";
+ static const char msgport[]="], port=[";
+ static const char msg4[]="], top=";
+ static const char msg5[]=", retr=";
+ static const char msg6[]=", time=";
+ static const char msg7[]=", stls=1";
+ static const char msgAR[]=", rcvd=";
+ static const char msgAS[]=", sent=";
+
+ char num1[NUMBUFSIZE];
+ char num2[NUMBUFSIZE];
+ char num3[NUMBUFSIZE];
+ char numAR[NUMBUFSIZE];
+ char numAS[NUMBUFSIZE];
+
+ char *p;
+ const char *q;
+
+ libmail_str_size_t(top_count, num1);
+ libmail_str_size_t(retr_count, num2);
+ libmail_str_time_t(time(NULL)-start_time, num3);
+ libmail_str_size_t(bytes_received_count, numAR);
+ libmail_str_size_t(bytes_sent_count, numAS);
+
+ p=malloc(strlen(authaddr)+strlen(remoteip)+strlen(remoteport)+strlen(disc)+
+ strlen(num1)+strlen(num2)+strlen(num3)+
+ strlen(numAR)+strlen(numAS)+200); /* Should be enough */
+
+ strcpy(p, disc);
+ strcat(p, msg2);
+ strcat(p, authaddr);
+ strcat(p, msg3);
+ strcat(p, remoteip);
+ strcat(p, msgport);
+ strcat(p, remoteport);
+ strcat(p, msg4);
+ strcat(p, num1);
+ strcat(p, msg5);
+ strcat(p, num2);
+ strcat(p, msgAR);
+ strcat(p, numAR);
+ strcat(p, msgAS);
+ strcat(p, numAS);
+ strcat(p, msg6);
+ strcat(p, num3);
+
+ if ((q=getenv("POP3_TLS")) && atoi(q))
+ strcat(p, msg7);
+
+ strcat(p, "\n");
+ if (write(2, p, strlen(p)) < 0)
+ ; /* make gcc shut up */
+ free(p);
+}
+
+static RETSIGTYPE bye(int signum)
+{
+ acctout("INFO: TIMEOUT");
+ exit(0);
+#if RETSIGTYPE != void
+ return (0);
+#endif
+}
+
+static void loop()
+{
+char buf[BUFSIZ];
+char *p;
+int c;
+
+ signal(SIGALRM, bye);
+ while (alarm(300), fgets(buf, sizeof(buf), stdin))
+ {
+ bytes_received_count += strlen(buf);
+
+ alarm(0);
+ if ((p=strchr(buf, '\n')) != 0)
+ *p=0;
+ else while ((c=getc(stdin)) >= 0 && c != '\n')
+ ;
+ p=strtok(buf, " \t\r");
+ if (!p) p="";
+
+ mkupper(p);
+ if (strcmp(p, "QUIT") == 0)
+ {
+ printed(printf("+OK Bye-bye.\r\n"));
+ fflush(stdout);
+ cleanup();
+ acctout("INFO: LOGOUT");
+ return;
+ }
+
+ if (strcmp(p, "STAT") == 0)
+ {
+ do_stat();
+ continue;
+ }
+
+ if (strcmp(p, "LIST") == 0)
+ {
+ do_list(strtok(NULL, " \t\r"));
+ continue;
+ }
+
+ if (strcmp(p, "RETR") == 0)
+ {
+ unsigned i;
+
+ if ((i=getmsgnum(strtok(NULL, " \t\r"))) == 0)
+ continue;
+
+ do_retr(i-1, 0);
+ continue;
+ }
+
+ if (strcmp(p, "CAPA") == 0)
+ {
+ pop3dcapa();
+ continue;
+ }
+
+ if (strcmp(p, "DELE") == 0)
+ {
+ unsigned i;
+
+ if ((i=getmsgnum(strtok(NULL, " \t\r"))) == 0)
+ continue;
+
+ msglist_a[i-1]->isdeleted=1;
+ printed(printf("+OK Deleted.\r\n"));
+ fflush(stdout);
+ continue;
+ }
+
+ if (strcmp(p, "NOOP") == 0)
+ {
+ printed(printf("+OK Yup.\r\n"));
+ fflush(stdout);
+ continue;
+ }
+
+ if (strcmp(p, "RSET") == 0)
+ {
+ unsigned i;
+
+ for (i=0; i<msglist_cnt; i++)
+ msglist_a[i]->isdeleted=0;
+ printed(printf("+OK Resurrected.\r\n"));
+ fflush(stdout);
+ continue;
+ }
+
+ if (strcmp(p, "TOP") == 0)
+ {
+ unsigned i, j;
+ const char *q;
+
+ if ((i=getmsgnum(strtok(NULL, " \t\r"))) == 0)
+ continue;
+
+ q=strtok(NULL, " \t\r");
+
+ if (!q) goto error;
+
+ j=atoi(q);
+ do_retr(i-1, &j);
+ continue;
+ }
+
+ if (strcmp(p, "UIDL") == 0)
+ {
+ do_uidl(strtok(NULL, " \t\r"));
+ continue;
+ }
+
+error:
+ printed(printf("-ERR Invalid command.\r\n"));
+ fflush(stdout);
+ }
+ acctout("INFO: DISCONNECTED");
+}
+
+/* Like every good Maildir reader, we purge the tmp subdirectory */
+
+static void purgetmp()
+{
+DIR *p=opendir("tmp");
+time_t t;
+struct dirent *de;
+struct stat stat_buf;
+char *n;
+
+ if (!p) return;
+ time (&t);
+ t -= 48L * 60L * 60L;
+
+ while ((de=readdir(p)) != 0)
+ {
+ if (de->d_name[0] == '.') continue;
+ n=malloc(strlen(de->d_name)+5);
+ if (!n) continue;
+ strcat(strcpy(n, "tmp/"), de->d_name);
+ if (stat(n, &stat_buf) == 0 && stat_buf.st_mtime < t)
+ unlink(n);
+ free(n);
+ }
+ closedir(p);
+}
+
+
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+char *p;
+
+#ifdef HAVE_SETVBUF_IOLBF
+ setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
+#endif
+ time(&start_time);
+
+ if ((authaddr=getenv("AUTHADDR")) == NULL ||
+ *authaddr == 0)
+ {
+ authaddr=getenv("AUTHENTICATED");
+ if (authaddr == NULL || *authaddr == 0)
+ authaddr="nobody";
+ }
+
+ if ((remoteip=getenv("TCPREMOTEIP")) == NULL)
+ remoteip="127.0.0.1";
+
+ if ((remoteport=getenv("TCPREMOTEPORT")) == NULL)
+ remoteport="0";
+
+ {
+ struct stat buf;
+
+ if ( stat(".", &buf) < 0 || buf.st_mode & S_ISVTX)
+ {
+ fprintf(stderr, "INFO: LOCKED, user=%s, ip=[%s], port=[%s]\n",
+ authaddr, remoteip, remoteport);
+ printed(printf("-ERR Your account is temporarily unavailable (+t bit set on home directory).\r\n"));
+ exit(0);
+ }
+ }
+
+ if (argc > 1)
+ p=argv[1];
+ else
+ p=getenv("MAILDIR");
+
+ if (!p)
+ p="./Maildir";
+
+ if (chdir(p))
+ {
+ fprintf(stderr, "chdir %s: %s\n", p, strerror(errno));
+ printed(printf("-ERR chdir %s failed\n", p));
+ fflush(stdout);
+ exit(1);
+ }
+
+ maildir_loginexec();
+
+ if (auth_getoptionenvint("disablepop3"))
+ {
+ printed(printf("-ERR POP3 access disabled for this account.\r\n"));
+ fflush(stdout);
+ exit(1);
+ }
+
+ if ( auth_getoptionenvint("disableinsecurepop3")
+ && ((p=getenv("POP3_TLS")) == NULL || !atoi(p)))
+ {
+ printed(printf("-ERR POP3 access disabled via insecure connection.\r\n"));
+ fflush(stdout);
+ exit(1);
+ }
+
+ fprintf(stderr, "INFO: LOGIN, user=%s, ip=[%s], port=[%s]\n",
+ authaddr,
+ remoteip,
+ remoteport);
+ fflush(stderr);
+
+ msglist_cnt=0;
+ msglist_l=0;
+ msglist_a=0;
+ purgetmp();
+ maildir_getnew(".", INBOX, NULL, NULL);
+ if (scancur())
+ {
+ printed(printf("-ERR Maildir invalid (no 'cur' directory)\r\n"));
+ return (0);
+ }
+ sortmsgs();
+ printed(printf("+OK logged in.\r\n"));
+ fflush(stdout);
+ loop();
+ return (0);
+}