diff options
Diffstat (limited to 'imap/pop3dserver.c')
| -rw-r--r-- | imap/pop3dserver.c | 1120 | 
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); +} | 
