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

📄 imap4d.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 3 页
字号:
#include <u.h>#include <libc.h>#include <auth.h>#include <bio.h>#include "imap4d.h"/* * these should be in libraries */char	*csquery(char *attr, char *val, char *rattr);/* * /lib/rfc/rfc2060 imap4rev1 * /lib/rfc/rfc2683 is implementation advice * /lib/rfc/rfc2342 is namespace capability * /lib/rfc/rfc2222 is security protocols * /lib/rfc/rfc1731 is security protocols * /lib/rfc/rfc2221 is LOGIN-REFERRALS * /lib/rfc/rfc2193 is MAILBOX-REFERRALS * /lib/rfc/rfc2177 is IDLE capability * /lib/rfc/rfc2195 is CRAM-MD5 authentication * /lib/rfc/rfc2088 is LITERAL+ capability * /lib/rfc/rfc1760 is S/Key authentication * * outlook uses "Secure Password Authentication" aka ntlm authentication * * capabilities from nslocum * CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT */typedef struct	ParseCmd	ParseCmd;enum{	UlongMax	= 4294967295,};struct ParseCmd{	char	*name;	void	(*f)(char *tg, char *cmd);};static	void	appendCmd(char *tg, char *cmd);static	void	authenticateCmd(char *tg, char *cmd);static	void	capabilityCmd(char *tg, char *cmd);static	void	closeCmd(char *tg, char *cmd);static	void	copyCmd(char *tg, char *cmd);static	void	createCmd(char *tg, char *cmd);static	void	deleteCmd(char *tg, char *cmd);static	void	expungeCmd(char *tg, char *cmd);static	void	fetchCmd(char *tg, char *cmd);static	void	idleCmd(char *tg, char *cmd);static	void	listCmd(char *tg, char *cmd);static	void	loginCmd(char *tg, char *cmd);static	void	logoutCmd(char *tg, char *cmd);static	void	namespaceCmd(char *tg, char *cmd);static	void	noopCmd(char *tg, char *cmd);static	void	renameCmd(char *tg, char *cmd);static	void	searchCmd(char *tg, char *cmd);static	void	selectCmd(char *tg, char *cmd);static	void	statusCmd(char *tg, char *cmd);static	void	storeCmd(char *tg, char *cmd);static	void	subscribeCmd(char *tg, char *cmd);static	void	uidCmd(char *tg, char *cmd);static	void	unsubscribeCmd(char *tg, char *cmd);static	void	copyUCmd(char *tg, char *cmd, int uids);static	void	fetchUCmd(char *tg, char *cmd, int uids);static	void	searchUCmd(char *tg, char *cmd, int uids);static	void	storeUCmd(char *tg, char *cmd, int uids);static	void	imap4(int);static	void	status(int expungeable, int uids);static	void	cleaner(void);static	void	check(void);static	int	catcher(void*, char*);static	Search	*searchKey(int first);static	Search	*searchKeys(int first, Search *tail);static	char	*astring(void);static	char	*atomString(char *disallowed, char *initial);static	char	*atom(void);static	void	badsyn(void);static	void	clearcmd(void);static	char	*command(void);static	void	crnl(void);static	Fetch	*fetchAtt(char *s, Fetch *f);static	Fetch	*fetchWhat(void);static	int	flagList(void);static	int	flags(void);static	int	getc(void);static	char	*listmbox(void);static	char	*literal(void);static	ulong	litlen(void);static	MsgSet	*msgSet(int);static	void	mustBe(int c);static	ulong	number(int nonzero);static	int	peekc(void);static	char	*quoted(void);static	void	sectText(Fetch *f, int mimeOk);static	ulong	seqNo(void);static	Store	*storeWhat(void);static	char	*tag(void);static	ulong	uidNo(void);static	void	ungetc(void);static	ParseCmd	SNonAuthed[] ={	{"capability",		capabilityCmd},	{"logout",		logoutCmd},	{"x-exit",		logoutCmd},	{"noop",		noopCmd},	{"login",		loginCmd},	{"authenticate",	authenticateCmd},	nil};static	ParseCmd	SAuthed[] ={	{"capability",		capabilityCmd},	{"logout",		logoutCmd},	{"x-exit",		logoutCmd},	{"noop",		noopCmd},	{"append",		appendCmd},	{"create",		createCmd},	{"delete",		deleteCmd},	{"examine",		selectCmd},	{"select",		selectCmd},	{"idle",		idleCmd},	{"list",		listCmd},	{"lsub",		listCmd},	{"namespace",		namespaceCmd},	{"rename",		renameCmd},	{"status",		statusCmd},	{"subscribe",		subscribeCmd},	{"unsubscribe",		unsubscribeCmd},	nil};static	ParseCmd	SSelected[] ={	{"capability",		capabilityCmd},	{"logout",		logoutCmd},	{"x-exit",		logoutCmd},	{"noop",		noopCmd},	{"append",		appendCmd},	{"create",		createCmd},	{"delete",		deleteCmd},	{"examine",		selectCmd},	{"select",		selectCmd},	{"idle",		idleCmd},	{"list",		listCmd},	{"lsub",		listCmd},	{"namespace",		namespaceCmd},	{"rename",		renameCmd},	{"status",		statusCmd},	{"subscribe",		subscribeCmd},	{"unsubscribe",		unsubscribeCmd},	{"check",		noopCmd},	{"close",		closeCmd},	{"copy",		copyCmd},	{"expunge",		expungeCmd},	{"fetch",		fetchCmd},	{"search",		searchCmd},	{"store",		storeCmd},	{"uid",			uidCmd},	nil};static	char		*atomStop = "(){%*\"\\";static	Chalstate	*chal;static	int		chaled;static	ParseCmd	*imapState;static	jmp_buf		parseJmp;static	char		*parseMsg;static	int		allowPass;static	int		allowCR;static	int		exiting;static	QLock		imaplock;static	int		idlepid = -1;Biobuf	bout;Biobuf	bin;char	username[UserNameLen];char	mboxDir[MboxNameLen];char	*servername;char	*site;char	*remote;Box	*selected;Bin	*parseBin;int	debug;voidmain(int argc, char *argv[]){	char *s, *t;	int preauth, n;	Binit(&bin, 0, OREAD);	Binit(&bout, 1, OWRITE);	preauth = 0;	allowPass = 0;	allowCR = 0;	ARGBEGIN{	case 'a':		preauth = 1;		break;	case 'd':		site = ARGF();		break;	case 'c':		allowCR = 1;		break;	case 'p':		allowPass = 1;		break;	case 'r':		remote = ARGF();		break;	case 's':		servername = ARGF();		break;	case 'v':		debug = 1;		debuglog("imap4d debugging enabled\n");		break;	default:		fprint(2, "usage: ip/imap4d [-acpv] [-d site] [-r remotehost] [-s servername]\n");		bye("usage");		break;	}ARGEND	if(allowPass && allowCR){		fprint(2, "%s: -c and -p are mutually exclusive\n", argv0);		bye("usage");	}	if(preauth)		setupuser(nil);	if(servername == nil){		servername = csquery("sys", sysname(), "dom");		if(servername == nil)			servername = sysname();		if(servername == nil){			fprint(2, "ip/imap4d can't find server name: %r\n");			bye("can't find system name");		}	}	if(site == nil){		t = getenv("site");		if(t == nil)			site = servername;		else{			n = strlen(t);			s = strchr(servername, '.');			if(s == nil)				s = servername;			else				s++;			n += strlen(s) + 2;			site = emalloc(n);			snprint(site, n, "%s.%s", t, s);		}	}	rfork(RFNOTEG|RFREND);	atnotify(catcher, 1);	qlock(&imaplock);	atexit(cleaner);	imap4(preauth);}static voidimap4(int preauth){	char *volatile tg;	char *volatile cmd;	ParseCmd *st;	if(preauth){		Bprint(&bout, "* preauth %s IMAP4rev1 server ready user %s authenticated\r\n", servername, username);		imapState = SAuthed;	}else{		Bprint(&bout, "* OK %s IMAP4rev1 server ready\r\n", servername);		imapState = SNonAuthed;	}	if(Bflush(&bout) < 0)		writeErr();	chaled = 0;	tg = nil;	cmd = nil;	if(setjmp(parseJmp)){		if(tg == nil)			Bprint(&bout, "* bad empty command line: %s\r\n", parseMsg);		else if(cmd == nil)			Bprint(&bout, "%s BAD no command: %s\r\n", tg, parseMsg);		else			Bprint(&bout, "%s BAD %s %s\r\n", tg, cmd, parseMsg);		clearcmd();		if(Bflush(&bout) < 0)			writeErr();		binfree(&parseBin);	}	for(;;){		if(mbLocked())			bye("internal error: mailbox lock held");		tg = nil;		cmd = nil;		tg = tag();		mustBe(' ');		cmd = atom();		/*		 * note: outlook express is broken: it requires echoing the		 * command as part of matching response		 */		for(st = imapState; st->name != nil; st++){			if(cistrcmp(cmd, st->name) == 0){				(*st->f)(tg, cmd);				break;			}		}		if(st->name == nil){			clearcmd();			Bprint(&bout, "%s BAD %s illegal command\r\n", tg, cmd);		}		if(Bflush(&bout) < 0)			writeErr();		binfree(&parseBin);	}}voidbye(char *fmt, ...){	va_list arg;	va_start(arg, fmt);	Bprint(&bout, "* bye ");	Bvprint(&bout, fmt, arg);	Bprint(&bout, "\r\n");	Bflush(&bout);exits("rob2");	exits(0);}voidparseErr(char *msg){	parseMsg = msg;	longjmp(parseJmp, 1);}/* * an error occured while writing to the client */voidwriteErr(void){	cleaner();	_exits("connection closed");}static intcatcher(void *v, char *msg){	USED(v);	if(strstr(msg, "closed pipe") != nil)		return 1;	return 0;}/* * wipes out the idleCmd backgroung process if it is around. * this can only be called if the current proc has qlocked imaplock. * it must be the last piece of imap4d code executed. */static voidcleaner(void){	int i;	if(idlepid < 0)		return;	exiting = 1;	close(0);	close(1);	close(2);	/*	 * the other proc is either stuck in a read, a sleep,	 * or is trying to lock imap4lock.	 * get him out of it so he can exit cleanly	 */	qunlock(&imaplock);	for(i = 0; i < 4; i++)		postnote(PNGROUP, getpid(), "die");}/* * send any pending status updates to the client * careful: shouldn't exit, because called by idle polling proc * * can't always send pending info * in particular, can't send expunge info * in response to a fetch, store, or search command. *  * rfc2060 5.2:	server must send mailbox size updates * rfc2060 5.2:	server may send flag updates * rfc2060 5.5:	servers prohibited from sending expunge while fetch, store, search in progress * rfc2060 7:	in selected state, server checks mailbox for new messages as part of every command * 		sends untagged EXISTS and RECENT respsonses reflecting new size of the mailbox * 		should also send appropriate untagged FETCH and EXPUNGE messages if another agent * 		changes the state of any message flags or expunges any messages * rfc2060 7.4.1	expunge server response must not be sent when no command is in progress, * 		nor while responding to a fetch, stort, or search command (uid versions are ok) * 		command only "in progress" after entirely parsed. * * strategy for third party deletion of messages or of a mailbox * * deletion of a selected mailbox => act like all message are expunged *	not strictly allowed by rfc2180, but close to method 3.2. * * renaming same as deletion * * copy *	reject iff a deleted message is in the request * * search, store, fetch operations on expunged messages *	ignore the expunged messages *	return tagged no if referenced */static voidstatus(int expungeable, int uids){	int tell;	if(!selected)		return;	tell = 0;	if(expungeable)		tell = expungeMsgs(selected, 1);	if(selected->sendFlags)		sendFlags(selected, uids);	if(tell || selected->toldMax != selected->max){		Bprint(&bout, "* %lud EXISTS\r\n", selected->max);		selected->toldMax = selected->max;	}	if(tell || selected->toldRecent != selected->recent){		Bprint(&bout, "* %lud RECENT\r\n", selected->recent);		selected->toldRecent = selected->recent;	}	if(tell)		closeImp(selected, checkBox(selected, 1));}/* * careful: can't exit, because called by idle polling proc */static voidcheck(void){	if(!selected)		return;	checkBox(selected, 0);	status(1, 0);}static voidappendCmd(char *tg, char *cmd){	char *mbox, head[128];	ulong t, n, now;	int flags, ok;	mustBe(' ');	mbox = astring();	mustBe(' ');	flags = 0;	if(peekc() == '('){		flags = flagList();		mustBe(' ');	}	now = time(nil);	if(peekc() == '"'){		t = imap4DateTime(quoted());		if(t == ~0)			parseErr("illegal date format");		mustBe(' ');		if(t > now)			t = now;	}else		t = now;	n = litlen();	mbox = mboxName(mbox);	if(mbox == nil || !okMbox(mbox)){		check();		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);		return;	}	if(!cdExists(mboxDir, mbox)){		check();		Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);		return;	}	snprint(head, sizeof(head), "From %s %s", username, ctime(t));	ok = appendSave(mbox, flags, head, &bin, n);	crnl();	check();	if(ok)		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);	else		Bprint(&bout, "%s NO %s message save failed\r\n", tg, cmd);}static voidauthenticateCmd(char *tg, char *cmd){	char *s, *t;	mustBe(' ');	s = atom();	crnl();	auth_freechal(chal);	chal = nil;	if(cistrcmp(s, "cram-md5") == 0){		t = cramauth();		if(t == nil){			Bprint(&bout, "%s OK %s\r\n", tg, cmd);			imapState = SAuthed;		}else			Bprint(&bout, "%s NO %s failed %s\r\n", tg, cmd, t);	}else		Bprint(&bout, "%s NO %s unsupported authentication protocol\r\n", tg, cmd);}static voidcapabilityCmd(char *tg, char *cmd){	crnl();	check();// nslocum's capabilities//	Bprint(&bout, "* CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT\r\n");	Bprint(&bout, "* CAPABILITY IMAP4REV1 IDLE NAMESPACE AUTH=CRAM-MD5\r\n");	Bprint(&bout, "%s OK %s\r\n", tg, cmd);}static voidcloseCmd(char *tg, char *cmd){	crnl();	imapState = SAuthed;	closeBox(selected, 1);	selected = nil;	Bprint(&bout, "%s OK %s mailbox closed, now in authenticated state\r\n", tg, cmd);}/* * note: message id's are before any pending expunges */static voidcopyCmd(char *tg, char *cmd){	copyUCmd(tg, cmd, 0);}static voidcopyUCmd(char *tg, char *cmd, int uids){	MsgSet *ms;	char *uid, *mbox;	ulong max;	int ok;	mustBe(' ');	ms = msgSet(uids);	mustBe(' ');	mbox = astring();	crnl();	uid = "";	if(uids)		uid = "uid ";	mbox = mboxName(mbox);	if(mbox == nil || !okMbox(mbox)){		status(1, uids);		Bprint(&bout, "%s NO %s%s bad mailbox\r\n", tg, uid, cmd);		return;	}	if(!cdExists(mboxDir, mbox)){		check();		Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);		return;	}	max = selected->max;	checkBox(selected, 0);	ok = forMsgs(selected, ms, max, uids, copyCheck, nil);	if(ok)		ok = forMsgs(selected, ms, max, uids, copySave, mbox);	status(1, uids);	if(ok)		Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);	else		Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);}static voidcreateCmd(char *tg, char *cmd){	char *mbox, *m;	int fd, slash;	mustBe(' ');	mbox = astring();	crnl();	check();	m = strchr(mbox, '\0');	slash = m != mbox && m[-1] == '/';	mbox = mboxName(mbox);	if(mbox == nil || !okMbox(mbox)){		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);		return;	}	if(cistrcmp(mbox, "inbox") == 0){		Bprint(&bout, "%s NO %s cannot remotely create INBOX\r\n", tg, cmd);		return;	}	if(access(mbox, AEXIST) >= 0){		Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);		return;	}	fd = createBox(mbox, slash);	close(fd);	if(fd < 0)		Bprint(&bout, "%s NO %s cannot create mailbox %s\r\n", tg, cmd, mbox);	else		Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);}static voiddeleteCmd(char *tg, char *cmd){	char *mbox, *imp;	mustBe(' ');	mbox = astring();	crnl();	check();	mbox = mboxName(mbox);	if(mbox == nil || !okMbox(mbox)){		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);		return;	}	imp = impName(mbox);	if(cistrcmp(mbox, "inbox") == 0	|| imp != nil && cdRemove(mboxDir, imp) < 0 && cdExists(mboxDir, imp)	|| cdRemove(mboxDir, mbox) < 0)		Bprint(&bout, "%s NO %s cannot delete mailbox %s\r\n", tg, cmd, mbox);	else		Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);}static voidexpungeCmd(char *tg, char *cmd){	int ok;	crnl();	ok = deleteMsgs(selected);	check();	if(ok)		Bprint(&bout, "%s OK %s messages erased\r\n", tg, cmd);	else		Bprint(&bout, "%s NO %s some messages not expunged\r\n", tg, cmd);}static void

⌨️ 快捷键说明

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