/* ** Copyright 1998 - 2018 Double Precision, Inc. ** See COPYING for distribution information. ** */ #if HAVE_CONFIG_H #include "config.h" #endif #include #if HAVE_DIRENT_H #include #define NAMLEN(dirent) strlen((dirent)->d_name) #else #define dirent direct #define NAMLEN(dirent) (dirent)->d_namlen #if HAVE_SYS_NDIR_H #include #endif #if HAVE_SYS_DIR_H #include #endif #if HAVE_NDIR_H #include #endif #endif #include #if HAVE_SYS_STAT_H #include #endif #if TIME_WITH_SYS_TIME #include #include #else #if HAVE_SYS_TIME_H #include #else #include #endif #endif #include #include #include #include #if HAVE_UNISTD_H #include #endif #include #include #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 "rfc2045/rfc2045.h" #include "courierauth.h" #define POP3DLIST "courierpop3dsizelist" #define LISTVERSION 3 #ifndef RUNTIME_START #define RUNTIME_START time(NULL) #endif #ifndef RUNTIME_CUR #define RUNTIME_CUR time(NULL) #endif extern void pop3dcapa(); extern void pop3dlang(const char *); static void acctout(const char *disc); void rfc2045_error(const char *p) { if (write(2, p, strlen(p)) < 0) _exit(1); _exit(0); } static const char *authaddr, *remoteip, *remoteport; struct msglist { struct msglist *next; char *filename; int isdeleted; int isnew; int isutf8; 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; int utf8_enabled; int savesizes=0; /* ** 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 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"); struct rfc2045 *p=rfc2045_fromfp(f); m->size=p->nlines + p->endpos; clearerr(f); if (m->size > 0 && fseek(f, -1, SEEK_SET) == 0 && getc(f) != '\n') m->size+=2; /* We'll add an extra CRLF ourselves */ m->isutf8=0; if (p->rfcviolation & RFC2045_ERR8BITHEADER) m->isutf8=1; rfc2045_free(p); 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[2048]; FILE *fp=openpop3dlist(); size_t i; int vernum=0; unsigned long size; uidv=RUNTIME_CUR; if (fp == NULL || fgets(linebuf, sizeof(linebuf)-1, fp) == NULL || linebuf[0] != '/' || sscanf(linebuf+1, "%d %lu %lu", &vernum, uid, &uidv) < 2 || (vernum != LISTVERSION && vernum != (LISTVERSION-1))) { if (fp) fclose(fp); fp=NULL; } else { if (vernum != LISTVERSION) savesizes=1; } if (fp) { struct msglist *m; char *p, *q; size_t n=0; int ch; while ((ch=getc(fp)) != EOF) { if (ch != '\n') { if (n < sizeof(linebuf)-20) linebuf[n++]=ch; continue; } linebuf[n]=0; n=0; 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); } // Converting from LISTVERSION 2, assuming no UTF-8. // We have extra room at the end. strcat(p, ":0"); if (sscanf(p, "%lu %lu:%lu:%d", &size, &m->uid.n, &m->uid.uidv, &m->isutf8) == 4) { m->size=size; m->next=list; list=m; ++mcnt; } else { 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; #ifdef SAVEHOOK SAVEHOOK(); #endif 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, "/%d %lu %lu\n", LISTVERSION, uid, uidv); for (i=0; ifilename; char *q; if ((q=strrchr(p, '/')) != NULL) p=q+1; fprintf(fp, "%s %lu %lu:%lu:%d\n", p, (unsigned long)a[i]->size, a[i]->uid.n, a[i]->uid.uidv, a[i]->isutf8); } 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; 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; isize=prev_list[n]->size; msglist_a[i]->uid=prev_list[n]->uid; msglist_a[i]->isutf8=prev_list[n]->isutf8; n++; } else { msglist_a[i]->uid.n=nextuid++; msglist_a[i]->uid.uidv=uidv; msglist_a[i]->isnew=1; 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; iisnew) 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; iisdeleted) { 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], port=[%s], filename=%s\n", getenv("AUTHENTICATED"), remoteip, remoteport, 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; iisdeleted) 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; iisdeleted) 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 */ #define MIMEWRAPTXT \ "From: Mail Delivery Subsystem \n" \ "Subject: Cannot display Unicode content\n" \ "Mime-Version: 1.0\n" \ "Content-Type: multipart/mixed; boundary=\"%s\"\n" \ "\n\n\n--%s\n" \ "Content-Type: text/plain\n\n" \ "This E-mail message was determined to be Unicode-formatted\n" \ "but your E-mail reader does not support Unicode E-mail.\n\n" \ "Please use an E-mail reader that supports POP3 with UTF-8\n" \ "(see https://tools.ietf.org/html/rfc6856.html).\n\n" \ "This can also happen when the sender's E-mail program does not\n" \ "correctly format the sent message.\n\n" \ "The original message is included as a separate attachment\n" \ "so that it can be downloaded manually.\n\n" \ "--%s\n" \ "Content-Type: %s\n\n" struct retr_source { char *wrapheader; FILE *f; char *wrapfooter; }; static int get_retr_source(struct retr_source *s) { int c=(unsigned char)*s->wrapheader; if (c) { ++s->wrapheader; return c; } if (s->f) { int c=getc(s->f); if (c >= 0) return c; s->f=0; } if (*s->wrapfooter) { int c=(unsigned char)*s->wrapfooter; ++s->wrapfooter; return c; } return -1; } static void do_retr(unsigned i, unsigned *lptr) { FILE *f; char *p; int c, lastc='\n'; int inheader=1; char buf[NUMBUFSIZE]; unsigned long *cntr; char boundary[256]; char wrapheader[sizeof(MIMEWRAPTXT)+1024]; char wrapfooter[512]; const char *mime_message_type=getenv("MIME_UNICODE_MESSAGE_TYPE"); struct retr_source rs; wrapheader[0]=0; wrapfooter[0]=0; if (!mime_message_type) mime_message_type="message/global"; if (msglist_a[i]->isutf8 && !utf8_enabled) { sprintf(boundary, "=_%d-%d", (int)getpid(), (int)RUNTIME_CUR); sprintf(wrapheader, MIMEWRAPTXT, boundary, boundary, boundary, mime_message_type); sprintf(wrapfooter, "\n--%s--\n", boundary); } f=fopen(msglist_a[i]->filename, "r"); 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 + strlen(wrapheader) + strlen(wrapfooter), buf))); cntr= &retr_count; if (lptr) cntr= &top_count; rs.wrapheader=wrapheader; rs.f=f; rs.wrapfooter=wrapfooter; for (lastc=0; (c=get_retr_source(&rs)) >= 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; iisdeleted) 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(RUNTIME_CUR-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, "LANG") == 0) { pop3dlang(strtok(NULL, "\r\n")); 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; iisdeleted=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; } if (strcmp(p, "UTF8") == 0) { /* XXX workaround for MS Outlook */ utf8_enabled=1; printed(printf("+OK UTF8 enabled\r\n")); fflush(stdout); 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 int main(int argc, char **argv) { char *p; #ifdef HAVE_SETVBUF_IOLBF setvbuf(stderr, NULL, _IOLBF, BUFSIZ); #endif start_time=RUNTIME_START; if ((authaddr=getenv("AUTHADDR")) == NULL || *authaddr == 0) { authaddr=getenv("AUTHENTICATED"); if (authaddr == NULL || *authaddr == 0) authaddr="nobody"; } utf8_enabled=1; /* Until proven otherwise */ { const char *utf8=getenv("UTF8"); if (utf8) utf8_enabled=atoi(utf8); } 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); }