⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pop3.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include "common.h"#include <ctype.h>#include <auth.h>#include <libsec.h>typedef struct Cmd Cmd;struct Cmd{	char *name;	int needauth;	int (*f)(char*);};static void hello(void);static int apopcmd(char*);static int capacmd(char*);static int delecmd(char*);static int listcmd(char*);static int noopcmd(char*);static int passcmd(char*);static int quitcmd(char*);static int rsetcmd(char*);static int retrcmd(char*);static int statcmd(char*);static int stlscmd(char*);static int topcmd(char*);static int synccmd(char*);static int uidlcmd(char*);static int usercmd(char*);static char *nextarg(char*);static int getcrnl(char*, int);static int readmbox(char*);static void sendcrnl(char*, ...);static int senderr(char*, ...);static int sendok(char*, ...);#pragma varargck argpos sendcrnl 1#pragma varargck argpos senderr 1#pragma varargck argpos sendok 1Cmd cmdtab[] ={	"apop", 0, apopcmd,	"capa", 0, capacmd,	"dele", 1, delecmd,	"list", 1, listcmd,	"noop", 0, noopcmd,	"pass", 0, passcmd,	"quit", 0, quitcmd,	"rset", 0, rsetcmd,	"retr", 1, retrcmd,	"stat", 1, statcmd,	"stls", 0, stlscmd,	"sync", 1, synccmd,	"top", 1, topcmd,	"uidl", 1, uidlcmd,	"user", 0, usercmd,	0, 0, 0,};static Biobuf in;static Biobuf out;static int passwordinclear;static int didtls;typedef struct Msg Msg;struct Msg {	int upasnum;	char digest[64];	int bytes;	int deleted;};static int totalbytes;static int totalmsgs;static Msg *msg;static int nmsg;static int loggedin;static int debug;static uchar *tlscert;static int ntlscert;static char *peeraddr;static char tmpaddr[64];voidusage(void){	fprint(2, "usage: upas/pop3 [-a authmboxfile] [-d debugfile] [-p]\n");	exits("usage");}voidmain(int argc, char **argv){	int fd;	char *arg, cmdbuf[1024];	Cmd *c;	rfork(RFNAMEG);	Binit(&in, 0, OREAD);	Binit(&out, 1, OWRITE);	ARGBEGIN{	case 'a':		loggedin = 1;		if(readmbox(EARGF(usage())) < 0)			exits(nil);		break;	case 'd':		debug++;		if((fd = create(EARGF(usage()), OWRITE, 0666)) >= 0 && fd != 2){			dup(fd, 2);			close(fd);		}		break;	case 'r':		strecpy(tmpaddr, tmpaddr+sizeof tmpaddr, EARGF(usage()));		if(arg = strchr(tmpaddr, '!'))			*arg = '\0';		peeraddr = tmpaddr;		break;	case 't':		tlscert = readcert(EARGF(usage()), &ntlscert);		if(tlscert == nil){			senderr("cannot read TLS certificate: %r");			exits(nil);		}		break;	case 'p':		passwordinclear = 1;		break;	}ARGEND	/* do before TLS */	if(peeraddr == nil)		peeraddr = remoteaddr(0,0);	hello();	while(Bflush(&out), getcrnl(cmdbuf, sizeof cmdbuf) > 0){		arg = nextarg(cmdbuf);		for(c=cmdtab; c->name; c++)			if(cistrcmp(c->name, cmdbuf) == 0)				break;		if(c->name == 0){			senderr("unknown command %s", cmdbuf);			continue;		}		if(c->needauth && !loggedin){			senderr("%s requires authentication", cmdbuf);			continue;		}		(*c->f)(arg);	}	exits(nil);}/* sort directories in increasing message number order */static intdircmp(void *a, void *b){	return atoi(((Dir*)a)->name) - atoi(((Dir*)b)->name);}static intreadmbox(char *box){	int fd, i, n, nd, lines, pid;	char buf[100], err[ERRMAX];	char *p;	Biobuf *b;	Dir *d, *draw;	Msg *m;	Waitmsg *w;	unmount(nil, "/mail/fs");	switch(pid = fork()){	case -1:		return senderr("can't fork to start upas/fs");	case 0:		close(0);		close(1);		open("/dev/null", OREAD);		open("/dev/null", OWRITE);		execl("/bin/upas/fs", "upas/fs", "-np", "-f", box, nil);		snprint(err, sizeof err, "upas/fs: %r");		_exits(err);		break;	default:		break;	}	if((w = wait()) == nil || w->pid != pid || w->msg[0] != '\0'){		if(w && w->pid==pid)			return senderr("%s", w->msg);		else			return senderr("can't initialize upas/fs");	}	free(w);	if(chdir("/mail/fs/mbox") < 0)		return senderr("can't initialize upas/fs: %r");	if((fd = open(".", OREAD)) < 0)		return senderr("cannot open /mail/fs/mbox: %r");	nd = dirreadall(fd, &d);	close(fd);	if(nd < 0)		return senderr("cannot read from /mail/fs/mbox: %r");	msg = mallocz(sizeof(Msg)*nd, 1);	if(msg == nil)		return senderr("out of memory");	if(nd == 0)		return 0;	qsort(d, nd, sizeof(d[0]), dircmp);	for(i=0; i<nd; i++){		m = &msg[nmsg];		m->upasnum = atoi(d[i].name);		sprint(buf, "%d/digest", m->upasnum);		if((fd = open(buf, OREAD)) < 0)			continue;		n = readn(fd, m->digest, sizeof m->digest - 1);		close(fd);		if(n < 0)			continue;		m->digest[n] = '\0';		/*		 * We need the number of message lines so that we		 * can adjust the byte count to include \r's.		 * Upas/fs gives us the number of lines in the raw body		 * in the lines file, but we have to count rawheader ourselves.		 * There is one blank line between raw header and raw body.		 */		sprint(buf, "%d/rawheader", m->upasnum);		if((b = Bopen(buf, OREAD)) == nil)			continue;		lines = 0;		for(;;){			p = Brdline(b, '\n');			if(p == nil){				if((n = Blinelen(b)) == 0)					break;				Bseek(b, n, 1);			}else				lines++;		}		Bterm(b);		lines++;		sprint(buf, "%d/lines", m->upasnum);		if((fd = open(buf, OREAD)) < 0)			continue;		n = readn(fd, buf, sizeof buf - 1);		close(fd);		if(n < 0)			continue;		buf[n] = '\0';		lines += atoi(buf);		sprint(buf, "%d/raw", m->upasnum);		if((draw = dirstat(buf)) == nil)			continue;		m->bytes = lines+draw->length;		free(draw);		nmsg++;		totalmsgs++;		totalbytes += m->bytes;	}	return 0;}/* *  get a line that ends in crnl or cr, turn terminating crnl into a nl * *  return 0 on EOF */static intgetcrnl(char *buf, int n){	int c;	char *ep;	char *bp;	Biobuf *fp = &in;	Bflush(&out);	bp = buf;	ep = bp + n - 1;	while(bp != ep){		c = Bgetc(fp);		if(debug) {			seek(2, 0, 2);			fprint(2, "%c", c);		}		switch(c){		case -1:			*bp = 0;			if(bp==buf)				return 0;			else				return bp-buf;		case '\r':			c = Bgetc(fp);			if(c == '\n'){				if(debug) {					seek(2, 0, 2);					fprint(2, "%c", c);				}				*bp = 0;				return bp-buf;			}			Bungetc(fp);			c = '\r';			break;		case '\n':			*bp = 0;			return bp-buf;		}		*bp++ = c;	}	*bp = 0;	return bp-buf;}static voidsendcrnl(char *fmt, ...){	char buf[1024];	va_list arg;	va_start(arg, fmt);	vseprint(buf, buf+sizeof(buf), fmt, arg);	va_end(arg);	if(debug)		fprint(2, "-> %s\n", buf);	Bprint(&out, "%s\r\n", buf);}static intsenderr(char *fmt, ...){	char buf[1024];	va_list arg;	va_start(arg, fmt);	vseprint(buf, buf+sizeof(buf), fmt, arg);	va_end(arg);	if(debug)		fprint(2, "-> -ERR %s\n", buf);	Bprint(&out, "-ERR %s\r\n", buf);	return -1;}static intsendok(char *fmt, ...){	char buf[1024];	va_list arg;	va_start(arg, fmt);	vseprint(buf, buf+sizeof(buf), fmt, arg);	va_end(arg);	if(*buf){		if(debug)			fprint(2, "-> +OK %s\n", buf);		Bprint(&out, "+OK %s\r\n", buf);	} else {		if(debug)			fprint(2, "-> +OK\n");		Bprint(&out, "+OK\r\n");	}	return 0;}static intcapacmd(char*){	sendok("");	sendcrnl("TOP");	if(passwordinclear || didtls)		sendcrnl("USER");	sendcrnl("PIPELINING");	sendcrnl("UIDL");	sendcrnl("STLS");	sendcrnl(".");	return 0;}static intdelecmd(char *arg){	int n;	if(*arg==0)		return senderr("DELE requires a message number");	n = atoi(arg)-1;	if(n < 0 || n >= nmsg || msg[n].deleted)		return senderr("no such message");	msg[n].deleted = 1;	totalmsgs--;	totalbytes -= msg[n].bytes;	sendok("message %d deleted", n+1);	return 0;}static intlistcmd(char *arg){	int i, n;	if(*arg == 0){		sendok("+%d message%s (%d octets)", totalmsgs, totalmsgs==1 ? "":"s", totalbytes);		for(i=0; i<nmsg; i++){			if(msg[i].deleted)				continue;			sendcrnl("%d %d", i+1, msg[i].bytes);		}		sendcrnl(".");	}else{		n = atoi(arg)-1;		if(n < 0 || n >= nmsg || msg[n].deleted)			return senderr("no such message");		sendok("%d %d", n+1, msg[n].bytes);	}	return 0;}static intnoopcmd(char *arg){	USED(arg);	sendok("");	return 0;}static void_synccmd(char*){	int i, fd;	char *s;	Fmt f;	if(!loggedin){		sendok("");		return;	}	fmtstrinit(&f);	fmtprint(&f, "delete mbox");	for(i=0; i<nmsg; i++)		if(msg[i].deleted)			fmtprint(&f, " %d", msg[i].upasnum);	s = fmtstrflush(&f);	if(strcmp(s, "delete mbox") != 0){	/* must have something to delete */		if((fd = open("../ctl", OWRITE)) < 0){			senderr("open ctl to delete messages: %r");			return;		}		if(write(fd, s, strlen(s)) < 0){			senderr("error deleting messages: %r");			return;		}	}	sendok("");}static intsynccmd(char*){	_synccmd(nil);	return 0;}static intquitcmd(char*){	synccmd(nil);	exits(nil);	return 0;}static intretrcmd(char *arg){	int n;	Biobuf *b;	char buf[40], *p;	if(*arg == 0)		return senderr("RETR requires a message number");	n = atoi(arg)-1;	if(n < 0 || n >= nmsg || msg[n].deleted)		return senderr("no such message");	snprint(buf, sizeof buf, "%d/raw", msg[n].upasnum);	if((b = Bopen(buf, OREAD)) == nil)		return senderr("message disappeared");	sendok("");	while((p = Brdstr(b, '\n', 1)) != nil){		if(p[0]=='.')			Bwrite(&out, ".", 1);		Bwrite(&out, p, strlen(p));		Bwrite(&out, "\r\n", 2);		free(p);	}	Bterm(b);	sendcrnl(".");	return 0;}static intrsetcmd(char*){	int i;	for(i=0; i<nmsg; i++){		if(msg[i].deleted){			msg[i].deleted = 0;			totalmsgs++;			totalbytes += msg[i].bytes;		}	}	return sendok("");}static intstatcmd(char*){	return sendok("%d %d", totalmsgs, totalbytes);}static inttrace(char *fmt, ...){	va_list arg;	int n;	va_start(arg, fmt);	n = vfprint(2, fmt, arg);	va_end(arg);	return n;}static intstlscmd(char*){	int fd;	TLSconn conn;	if(didtls)		return senderr("tls already started");	if(!tlscert)		return senderr("don't have any tls credentials");	sendok("");	Bflush(&out);	memset(&conn, 0, sizeof conn);	conn.cert = tlscert;	conn.certlen = ntlscert;	if(debug)		conn.trace = trace;	fd = tlsServer(0, &conn);	if(fd < 0)		sysfatal("tlsServer: %r");	dup(fd, 0);	dup(fd, 1);	close(fd);	Binit(&in, 0, OREAD);	Binit(&out, 1, OWRITE);	didtls = 1;	return 0;}		static inttopcmd(char *arg){	int done, i, lines, n;	char buf[40], *p;	Biobuf *b;	if(*arg == 0)		return senderr("TOP requires a message number");	n = atoi(arg)-1;	if(n < 0 || n >= nmsg || msg[n].deleted)		return senderr("no such message");	arg = nextarg(arg);	if(*arg == 0)		return senderr("TOP requires a line count");	lines = atoi(arg);	if(lines < 0)		return senderr("bad args to TOP");	snprint(buf, sizeof buf, "%d/raw", msg[n].upasnum);	if((b = Bopen(buf, OREAD)) == nil)		return senderr("message disappeared");	sendok("");	while(p = Brdstr(b, '\n', 1)){		if(p[0]=='.')			Bputc(&out, '.');		Bwrite(&out, p, strlen(p));		Bwrite(&out, "\r\n", 2);		done = p[0]=='\0';		free(p);		if(done)			break;	}	for(i=0; i<lines; i++){		p = Brdstr(b, '\n', 1);		if(p == nil)			break;		if(p[0]=='.')			Bwrite(&out, ".", 1);		Bwrite(&out, p, strlen(p));		Bwrite(&out, "\r\n", 2);		free(p);	}	sendcrnl(".");	Bterm(b);	return 0;}static intuidlcmd(char *arg){	int n;	if(*arg==0){		sendok("");		for(n=0; n<nmsg; n++){			if(msg[n].deleted)				continue;			sendcrnl("%d %s", n+1, msg[n].digest);		}		sendcrnl(".");	}else{		n = atoi(arg)-1;		if(n < 0 || n >= nmsg || msg[n].deleted)			return senderr("no such message");		sendok("%d %s", n+1, msg[n].digest);	}	return 0;	}static char*nextarg(char *p){	while(*p && *p != ' ' && *p != '\t')		p++;	while(*p == ' ' || *p == '\t')		*p++ = 0;	return p;}/* * authentication */Chalstate *chs;char user[256];char box[256];char cbox[256];static voidhello(void){	fmtinstall('H', encodefmt);	if((chs = auth_challenge("proto=apop role=server")) == nil){		senderr("auth server not responding, try later");		exits(nil);	}	sendok("POP3 server ready %s", chs->chal);}static intsetuser(char *arg){	char *p;	strcpy(box, "/mail/box/");	strecpy(box+strlen(box), box+sizeof box-7, arg);	strcpy(cbox, box);	cleanname(cbox);	if(strcmp(cbox, box) != 0)		return senderr("bad mailbox name");	strcat(box, "/mbox");	strecpy(user, user+sizeof user, arg);	if(p = strchr(user, '/'))		*p = '\0';	return 0;}static intusercmd(char *arg){	if(loggedin)		return senderr("already authenticated");	if(*arg == 0)		return senderr("USER requires argument");	if(setuser(arg) < 0)		return -1;	return sendok("");}static voidenableaddr(void){	int fd;	char buf[64];	/* hide the peer IP address under a rock in the ratifier FS */	if(peeraddr == 0 || *peeraddr == 0)		return;	sprint(buf, "/mail/ratify/trusted/%s#32", peeraddr);	/*	 * if the address is already there and the user owns it,	 * remove it and recreate it to give him a new time quanta.	 */	if(access(buf, 0) >= 0  && remove(buf) < 0)		return;	fd = create(buf, OREAD, 0666);	if(fd >= 0){		close(fd);//		syslog(0, "pop3", "ratified %s", peeraddr);	}}static intdologin(char *response){	AuthInfo *ai;	static int tries;	chs->user = user;	chs->resp = response;	chs->nresp = strlen(response);	if((ai = auth_response(chs)) == nil){		if(tries++ >= 5){			senderr("authentication failed: %r; server exiting");			exits(nil);		}			return senderr("authentication failed");	}	if(auth_chuid(ai, nil) < 0){		senderr("chuid failed: %r; server exiting");		exits(nil);	}	auth_freeAI(ai);	auth_freechal(chs);	chs = nil;	loggedin = 1;	if(newns(user, 0) < 0){		senderr("newns failed: %r; server exiting");		exits(nil);	}	enableaddr();	if(readmbox(box) < 0)		exits(nil);	return sendok("mailbox is %s", box);}static intpasscmd(char *arg){	DigestState *s;	uchar digest[MD5dlen];	char response[2*MD5dlen+1];	if(passwordinclear==0 && didtls==0)		return senderr("password in the clear disallowed");	/* use password to encode challenge */	if((chs = auth_challenge("proto=apop role=server")) == nil)		return senderr("couldn't get apop challenge");	// hash challenge with secret and convert to ascii	s = md5((uchar*)chs->chal, chs->nchal, 0, 0);	md5((uchar*)arg, strlen(arg), digest, s);	snprint(response, sizeof response, "%.*H", MD5dlen, digest);	return dologin(response);}static intapopcmd(char *arg){	char *resp;	resp = nextarg(arg);	if(setuser(arg) < 0)		return -1;	return dologin(resp);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -