xref: /plan9/sys/src/cmd/ip/imap4d/imap4d.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <auth.h>
47dd7cddfSDavid du Colombier #include <bio.h>
57dd7cddfSDavid du Colombier #include "imap4d.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier /*
87dd7cddfSDavid du Colombier  * these should be in libraries
97dd7cddfSDavid du Colombier  */
107dd7cddfSDavid du Colombier char	*csquery(char *attr, char *val, char *rattr);
117dd7cddfSDavid du Colombier 
127dd7cddfSDavid du Colombier /*
137dd7cddfSDavid du Colombier  * /lib/rfc/rfc2060 imap4rev1
147dd7cddfSDavid du Colombier  * /lib/rfc/rfc2683 is implementation advice
157dd7cddfSDavid du Colombier  * /lib/rfc/rfc2342 is namespace capability
167dd7cddfSDavid du Colombier  * /lib/rfc/rfc2222 is security protocols
177dd7cddfSDavid du Colombier  * /lib/rfc/rfc1731 is security protocols
187dd7cddfSDavid du Colombier  * /lib/rfc/rfc2221 is LOGIN-REFERRALS
197dd7cddfSDavid du Colombier  * /lib/rfc/rfc2193 is MAILBOX-REFERRALS
207dd7cddfSDavid du Colombier  * /lib/rfc/rfc2177 is IDLE capability
217dd7cddfSDavid du Colombier  * /lib/rfc/rfc2195 is CRAM-MD5 authentication
227dd7cddfSDavid du Colombier  * /lib/rfc/rfc2088 is LITERAL+ capability
237dd7cddfSDavid du Colombier  * /lib/rfc/rfc1760 is S/Key authentication
247dd7cddfSDavid du Colombier  *
257dd7cddfSDavid du Colombier  * outlook uses "Secure Password Authentication" aka ntlm authentication
267dd7cddfSDavid du Colombier  *
277dd7cddfSDavid du Colombier  * capabilities from nslocum
287dd7cddfSDavid du Colombier  * CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT
297dd7cddfSDavid du Colombier  */
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier typedef struct	ParseCmd	ParseCmd;
327dd7cddfSDavid du Colombier 
337dd7cddfSDavid du Colombier enum
347dd7cddfSDavid du Colombier {
357dd7cddfSDavid du Colombier 	UlongMax	= 4294967295,
367dd7cddfSDavid du Colombier };
377dd7cddfSDavid du Colombier 
387dd7cddfSDavid du Colombier struct ParseCmd
397dd7cddfSDavid du Colombier {
407dd7cddfSDavid du Colombier 	char	*name;
417dd7cddfSDavid du Colombier 	void	(*f)(char *tg, char *cmd);
427dd7cddfSDavid du Colombier };
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier static	void	appendCmd(char *tg, char *cmd);
457dd7cddfSDavid du Colombier static	void	authenticateCmd(char *tg, char *cmd);
467dd7cddfSDavid du Colombier static	void	capabilityCmd(char *tg, char *cmd);
477dd7cddfSDavid du Colombier static	void	closeCmd(char *tg, char *cmd);
487dd7cddfSDavid du Colombier static	void	copyCmd(char *tg, char *cmd);
497dd7cddfSDavid du Colombier static	void	createCmd(char *tg, char *cmd);
507dd7cddfSDavid du Colombier static	void	deleteCmd(char *tg, char *cmd);
517dd7cddfSDavid du Colombier static	void	expungeCmd(char *tg, char *cmd);
527dd7cddfSDavid du Colombier static	void	fetchCmd(char *tg, char *cmd);
537dd7cddfSDavid du Colombier static	void	idleCmd(char *tg, char *cmd);
547dd7cddfSDavid du Colombier static	void	listCmd(char *tg, char *cmd);
557dd7cddfSDavid du Colombier static	void	loginCmd(char *tg, char *cmd);
567dd7cddfSDavid du Colombier static	void	logoutCmd(char *tg, char *cmd);
577dd7cddfSDavid du Colombier static	void	namespaceCmd(char *tg, char *cmd);
587dd7cddfSDavid du Colombier static	void	noopCmd(char *tg, char *cmd);
597dd7cddfSDavid du Colombier static	void	renameCmd(char *tg, char *cmd);
607dd7cddfSDavid du Colombier static	void	searchCmd(char *tg, char *cmd);
617dd7cddfSDavid du Colombier static	void	selectCmd(char *tg, char *cmd);
627dd7cddfSDavid du Colombier static	void	statusCmd(char *tg, char *cmd);
637dd7cddfSDavid du Colombier static	void	storeCmd(char *tg, char *cmd);
647dd7cddfSDavid du Colombier static	void	subscribeCmd(char *tg, char *cmd);
657dd7cddfSDavid du Colombier static	void	uidCmd(char *tg, char *cmd);
667dd7cddfSDavid du Colombier static	void	unsubscribeCmd(char *tg, char *cmd);
677dd7cddfSDavid du Colombier 
687dd7cddfSDavid du Colombier static	void	copyUCmd(char *tg, char *cmd, int uids);
697dd7cddfSDavid du Colombier static	void	fetchUCmd(char *tg, char *cmd, int uids);
707dd7cddfSDavid du Colombier static	void	searchUCmd(char *tg, char *cmd, int uids);
717dd7cddfSDavid du Colombier static	void	storeUCmd(char *tg, char *cmd, int uids);
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier static	void	imap4(int);
747dd7cddfSDavid du Colombier static	void	status(int expungeable, int uids);
757dd7cddfSDavid du Colombier static	void	cleaner(void);
767dd7cddfSDavid du Colombier static	void	check(void);
7759cc4ca5SDavid du Colombier static	int	catcher(void*, char*);
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier static	Search	*searchKey(int first);
807dd7cddfSDavid du Colombier static	Search	*searchKeys(int first, Search *tail);
817dd7cddfSDavid du Colombier static	char	*astring(void);
827dd7cddfSDavid du Colombier static	char	*atomString(char *disallowed, char *initial);
837dd7cddfSDavid du Colombier static	char	*atom(void);
847dd7cddfSDavid du Colombier static	void	badsyn(void);
857dd7cddfSDavid du Colombier static	void	clearcmd(void);
867dd7cddfSDavid du Colombier static	char	*command(void);
877dd7cddfSDavid du Colombier static	void	crnl(void);
887dd7cddfSDavid du Colombier static	Fetch	*fetchAtt(char *s, Fetch *f);
897dd7cddfSDavid du Colombier static	Fetch	*fetchWhat(void);
907dd7cddfSDavid du Colombier static	int	flagList(void);
917dd7cddfSDavid du Colombier static	int	flags(void);
927dd7cddfSDavid du Colombier static	int	getc(void);
937dd7cddfSDavid du Colombier static	char	*listmbox(void);
947dd7cddfSDavid du Colombier static	char	*literal(void);
957dd7cddfSDavid du Colombier static	ulong	litlen(void);
96becaf2abSDavid du Colombier static	MsgSet	*msgSet(int);
977dd7cddfSDavid du Colombier static	void	mustBe(int c);
987dd7cddfSDavid du Colombier static	ulong	number(int nonzero);
997dd7cddfSDavid du Colombier static	int	peekc(void);
1007dd7cddfSDavid du Colombier static	char	*quoted(void);
1017dd7cddfSDavid du Colombier static	void	sectText(Fetch *f, int mimeOk);
1027dd7cddfSDavid du Colombier static	ulong	seqNo(void);
1037dd7cddfSDavid du Colombier static	Store	*storeWhat(void);
1047dd7cddfSDavid du Colombier static	char	*tag(void);
105becaf2abSDavid du Colombier static	ulong	uidNo(void);
1067dd7cddfSDavid du Colombier static	void	ungetc(void);
1077dd7cddfSDavid du Colombier 
1087dd7cddfSDavid du Colombier static	ParseCmd	SNonAuthed[] =
1097dd7cddfSDavid du Colombier {
1107dd7cddfSDavid du Colombier 	{"capability",		capabilityCmd},
1117dd7cddfSDavid du Colombier 	{"logout",		logoutCmd},
1127dd7cddfSDavid du Colombier 	{"x-exit",		logoutCmd},
1137dd7cddfSDavid du Colombier 	{"noop",		noopCmd},
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier 	{"login",		loginCmd},
1167dd7cddfSDavid du Colombier 	{"authenticate",	authenticateCmd},
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier 	nil
1197dd7cddfSDavid du Colombier };
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier static	ParseCmd	SAuthed[] =
1227dd7cddfSDavid du Colombier {
1237dd7cddfSDavid du Colombier 	{"capability",		capabilityCmd},
1247dd7cddfSDavid du Colombier 	{"logout",		logoutCmd},
1257dd7cddfSDavid du Colombier 	{"x-exit",		logoutCmd},
1267dd7cddfSDavid du Colombier 	{"noop",		noopCmd},
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier 	{"append",		appendCmd},
1297dd7cddfSDavid du Colombier 	{"create",		createCmd},
1307dd7cddfSDavid du Colombier 	{"delete",		deleteCmd},
1317dd7cddfSDavid du Colombier 	{"examine",		selectCmd},
1327dd7cddfSDavid du Colombier 	{"select",		selectCmd},
1337dd7cddfSDavid du Colombier 	{"idle",		idleCmd},
1347dd7cddfSDavid du Colombier 	{"list",		listCmd},
1357dd7cddfSDavid du Colombier 	{"lsub",		listCmd},
1367dd7cddfSDavid du Colombier 	{"namespace",		namespaceCmd},
1377dd7cddfSDavid du Colombier 	{"rename",		renameCmd},
1387dd7cddfSDavid du Colombier 	{"status",		statusCmd},
1397dd7cddfSDavid du Colombier 	{"subscribe",		subscribeCmd},
1407dd7cddfSDavid du Colombier 	{"unsubscribe",		unsubscribeCmd},
1417dd7cddfSDavid du Colombier 
1427dd7cddfSDavid du Colombier 	nil
1437dd7cddfSDavid du Colombier };
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier static	ParseCmd	SSelected[] =
1467dd7cddfSDavid du Colombier {
1477dd7cddfSDavid du Colombier 	{"capability",		capabilityCmd},
1487dd7cddfSDavid du Colombier 	{"logout",		logoutCmd},
1497dd7cddfSDavid du Colombier 	{"x-exit",		logoutCmd},
1507dd7cddfSDavid du Colombier 	{"noop",		noopCmd},
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	{"append",		appendCmd},
1537dd7cddfSDavid du Colombier 	{"create",		createCmd},
1547dd7cddfSDavid du Colombier 	{"delete",		deleteCmd},
1557dd7cddfSDavid du Colombier 	{"examine",		selectCmd},
1567dd7cddfSDavid du Colombier 	{"select",		selectCmd},
1577dd7cddfSDavid du Colombier 	{"idle",		idleCmd},
1587dd7cddfSDavid du Colombier 	{"list",		listCmd},
1597dd7cddfSDavid du Colombier 	{"lsub",		listCmd},
1607dd7cddfSDavid du Colombier 	{"namespace",		namespaceCmd},
1617dd7cddfSDavid du Colombier 	{"rename",		renameCmd},
1627dd7cddfSDavid du Colombier 	{"status",		statusCmd},
1637dd7cddfSDavid du Colombier 	{"subscribe",		subscribeCmd},
1647dd7cddfSDavid du Colombier 	{"unsubscribe",		unsubscribeCmd},
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier 	{"check",		noopCmd},
1677dd7cddfSDavid du Colombier 	{"close",		closeCmd},
1687dd7cddfSDavid du Colombier 	{"copy",		copyCmd},
1697dd7cddfSDavid du Colombier 	{"expunge",		expungeCmd},
1707dd7cddfSDavid du Colombier 	{"fetch",		fetchCmd},
1717dd7cddfSDavid du Colombier 	{"search",		searchCmd},
1727dd7cddfSDavid du Colombier 	{"store",		storeCmd},
1737dd7cddfSDavid du Colombier 	{"uid",			uidCmd},
1747dd7cddfSDavid du Colombier 
1757dd7cddfSDavid du Colombier 	nil
1767dd7cddfSDavid du Colombier };
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier static	char		*atomStop = "(){%*\"\\";
1799a747e4fSDavid du Colombier static	Chalstate	*chal;
1807dd7cddfSDavid du Colombier static	int		chaled;
1817dd7cddfSDavid du Colombier static	ParseCmd	*imapState;
1827dd7cddfSDavid du Colombier static	jmp_buf		parseJmp;
1837dd7cddfSDavid du Colombier static	char		*parseMsg;
1847dd7cddfSDavid du Colombier static	int		allowPass;
185d9306527SDavid du Colombier static	int		allowCR;
18659cc4ca5SDavid du Colombier static	int		exiting;
18759cc4ca5SDavid du Colombier static	QLock		imaplock;
18859cc4ca5SDavid du Colombier static	int		idlepid = -1;
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier Biobuf	bout;
1917dd7cddfSDavid du Colombier Biobuf	bin;
1929a747e4fSDavid du Colombier char	username[UserNameLen];
1939a747e4fSDavid du Colombier char	mboxDir[MboxNameLen];
1947dd7cddfSDavid du Colombier char	*servername;
1957dd7cddfSDavid du Colombier char	*site;
19680ee5cbfSDavid du Colombier char	*remote;
1977dd7cddfSDavid du Colombier Box	*selected;
19880ee5cbfSDavid du Colombier Bin	*parseBin;
1999a747e4fSDavid du Colombier int	debug;
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier void
main(int argc,char * argv[])2027dd7cddfSDavid du Colombier main(int argc, char *argv[])
2037dd7cddfSDavid du Colombier {
2047dd7cddfSDavid du Colombier 	char *s, *t;
2057dd7cddfSDavid du Colombier 	int preauth, n;
2067dd7cddfSDavid du Colombier 
20780ee5cbfSDavid du Colombier 	Binit(&bin, 0, OREAD);
20880ee5cbfSDavid du Colombier 	Binit(&bout, 1, OWRITE);
20980ee5cbfSDavid du Colombier 
2107dd7cddfSDavid du Colombier 	preauth = 0;
2117dd7cddfSDavid du Colombier 	allowPass = 0;
212d9306527SDavid du Colombier 	allowCR = 0;
2137dd7cddfSDavid du Colombier 	ARGBEGIN{
2147dd7cddfSDavid du Colombier 	case 'a':
2157dd7cddfSDavid du Colombier 		preauth = 1;
2167dd7cddfSDavid du Colombier 		break;
21780ee5cbfSDavid du Colombier 	case 'd':
21880ee5cbfSDavid du Colombier 		site = ARGF();
21980ee5cbfSDavid du Colombier 		break;
220d9306527SDavid du Colombier 	case 'c':
221d9306527SDavid du Colombier 		allowCR = 1;
222d9306527SDavid du Colombier 		break;
2237dd7cddfSDavid du Colombier 	case 'p':
2247dd7cddfSDavid du Colombier 		allowPass = 1;
2257dd7cddfSDavid du Colombier 		break;
22680ee5cbfSDavid du Colombier 	case 'r':
22780ee5cbfSDavid du Colombier 		remote = ARGF();
2287dd7cddfSDavid du Colombier 		break;
2297dd7cddfSDavid du Colombier 	case 's':
2307dd7cddfSDavid du Colombier 		servername = ARGF();
2317dd7cddfSDavid du Colombier 		break;
2329a747e4fSDavid du Colombier 	case 'v':
2339a747e4fSDavid du Colombier 		debug = 1;
2349a747e4fSDavid du Colombier 		debuglog("imap4d debugging enabled\n");
2359a747e4fSDavid du Colombier 		break;
2367dd7cddfSDavid du Colombier 	default:
237d9306527SDavid du Colombier 		fprint(2, "usage: ip/imap4d [-acpv] [-d site] [-r remotehost] [-s servername]\n");
2387dd7cddfSDavid du Colombier 		bye("usage");
2397dd7cddfSDavid du Colombier 		break;
2407dd7cddfSDavid du Colombier 	}ARGEND
2417dd7cddfSDavid du Colombier 
242d9306527SDavid du Colombier 	if(allowPass && allowCR){
243d9306527SDavid du Colombier 		fprint(2, "%s: -c and -p are mutually exclusive\n", argv0);
244d9306527SDavid du Colombier 		bye("usage");
245d9306527SDavid du Colombier 	}
246d9306527SDavid du Colombier 
2479a747e4fSDavid du Colombier 	if(preauth)
2489a747e4fSDavid du Colombier 		setupuser(nil);
2497dd7cddfSDavid du Colombier 
2507dd7cddfSDavid du Colombier 	if(servername == nil){
2517dd7cddfSDavid du Colombier 		servername = csquery("sys", sysname(), "dom");
2527dd7cddfSDavid du Colombier 		if(servername == nil)
2537dd7cddfSDavid du Colombier 			servername = sysname();
25480ee5cbfSDavid du Colombier 		if(servername == nil){
25580ee5cbfSDavid du Colombier 			fprint(2, "ip/imap4d can't find server name: %r\n");
2567dd7cddfSDavid du Colombier 			bye("can't find system name");
2577dd7cddfSDavid du Colombier 		}
25880ee5cbfSDavid du Colombier 	}
2597dd7cddfSDavid du Colombier 	if(site == nil){
2607dd7cddfSDavid du Colombier 		t = getenv("site");
2617dd7cddfSDavid du Colombier 		if(t == nil)
2627dd7cddfSDavid du Colombier 			site = servername;
2637dd7cddfSDavid du Colombier 		else{
2647dd7cddfSDavid du Colombier 			n = strlen(t);
2657dd7cddfSDavid du Colombier 			s = strchr(servername, '.');
2667dd7cddfSDavid du Colombier 			if(s == nil)
2677dd7cddfSDavid du Colombier 				s = servername;
2687dd7cddfSDavid du Colombier 			else
2697dd7cddfSDavid du Colombier 				s++;
2707dd7cddfSDavid du Colombier 			n += strlen(s) + 2;
2717dd7cddfSDavid du Colombier 			site = emalloc(n);
2727dd7cddfSDavid du Colombier 			snprint(site, n, "%s.%s", t, s);
2737dd7cddfSDavid du Colombier 		}
2747dd7cddfSDavid du Colombier 	}
2757dd7cddfSDavid du Colombier 
2767dd7cddfSDavid du Colombier 	rfork(RFNOTEG|RFREND);
2777dd7cddfSDavid du Colombier 
27859cc4ca5SDavid du Colombier 	atnotify(catcher, 1);
27959cc4ca5SDavid du Colombier 	qlock(&imaplock);
2807dd7cddfSDavid du Colombier 	atexit(cleaner);
2817dd7cddfSDavid du Colombier 	imap4(preauth);
2827dd7cddfSDavid du Colombier }
2837dd7cddfSDavid du Colombier 
2847dd7cddfSDavid du Colombier static void
imap4(int preauth)2857dd7cddfSDavid du Colombier imap4(int preauth)
2867dd7cddfSDavid du Colombier {
2877dd7cddfSDavid du Colombier 	char *volatile tg;
2887dd7cddfSDavid du Colombier 	char *volatile cmd;
2897dd7cddfSDavid du Colombier 	ParseCmd *st;
2907dd7cddfSDavid du Colombier 
2917dd7cddfSDavid du Colombier 	if(preauth){
2927dd7cddfSDavid du Colombier 		Bprint(&bout, "* preauth %s IMAP4rev1 server ready user %s authenticated\r\n", servername, username);
2937dd7cddfSDavid du Colombier 		imapState = SAuthed;
2947dd7cddfSDavid du Colombier 	}else{
295d9306527SDavid du Colombier 		Bprint(&bout, "* OK %s IMAP4rev1 server ready\r\n", servername);
2967dd7cddfSDavid du Colombier 		imapState = SNonAuthed;
2977dd7cddfSDavid du Colombier 	}
2987dd7cddfSDavid du Colombier 	if(Bflush(&bout) < 0)
2997dd7cddfSDavid du Colombier 		writeErr();
3007dd7cddfSDavid du Colombier 
3017dd7cddfSDavid du Colombier 	chaled = 0;
3027dd7cddfSDavid du Colombier 
3037dd7cddfSDavid du Colombier 	tg = nil;
3047dd7cddfSDavid du Colombier 	cmd = nil;
3057dd7cddfSDavid du Colombier 	if(setjmp(parseJmp)){
3067dd7cddfSDavid du Colombier 		if(tg == nil)
3077dd7cddfSDavid du Colombier 			Bprint(&bout, "* bad empty command line: %s\r\n", parseMsg);
3087dd7cddfSDavid du Colombier 		else if(cmd == nil)
309d9306527SDavid du Colombier 			Bprint(&bout, "%s BAD no command: %s\r\n", tg, parseMsg);
3107dd7cddfSDavid du Colombier 		else
311d9306527SDavid du Colombier 			Bprint(&bout, "%s BAD %s %s\r\n", tg, cmd, parseMsg);
3127dd7cddfSDavid du Colombier 		clearcmd();
3137dd7cddfSDavid du Colombier 		if(Bflush(&bout) < 0)
3147dd7cddfSDavid du Colombier 			writeErr();
31580ee5cbfSDavid du Colombier 		binfree(&parseBin);
3167dd7cddfSDavid du Colombier 	}
3177dd7cddfSDavid du Colombier 	for(;;){
3187dd7cddfSDavid du Colombier 		if(mbLocked())
3197dd7cddfSDavid du Colombier 			bye("internal error: mailbox lock held");
3207dd7cddfSDavid du Colombier 		tg = nil;
3217dd7cddfSDavid du Colombier 		cmd = nil;
3227dd7cddfSDavid du Colombier 		tg = tag();
3237dd7cddfSDavid du Colombier 		mustBe(' ');
3247dd7cddfSDavid du Colombier 		cmd = atom();
3257dd7cddfSDavid du Colombier 
3267dd7cddfSDavid du Colombier 		/*
3277dd7cddfSDavid du Colombier 		 * note: outlook express is broken: it requires echoing the
3287dd7cddfSDavid du Colombier 		 * command as part of matching response
3297dd7cddfSDavid du Colombier 		 */
3307dd7cddfSDavid du Colombier 		for(st = imapState; st->name != nil; st++){
3317dd7cddfSDavid du Colombier 			if(cistrcmp(cmd, st->name) == 0){
3327dd7cddfSDavid du Colombier 				(*st->f)(tg, cmd);
3337dd7cddfSDavid du Colombier 				break;
3347dd7cddfSDavid du Colombier 			}
3357dd7cddfSDavid du Colombier 		}
3367dd7cddfSDavid du Colombier 		if(st->name == nil){
3377dd7cddfSDavid du Colombier 			clearcmd();
338d9306527SDavid du Colombier 			Bprint(&bout, "%s BAD %s illegal command\r\n", tg, cmd);
3397dd7cddfSDavid du Colombier 		}
3407dd7cddfSDavid du Colombier 
3417dd7cddfSDavid du Colombier 		if(Bflush(&bout) < 0)
3427dd7cddfSDavid du Colombier 			writeErr();
34380ee5cbfSDavid du Colombier 		binfree(&parseBin);
3447dd7cddfSDavid du Colombier 	}
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier void
bye(char * fmt,...)3487dd7cddfSDavid du Colombier bye(char *fmt, ...)
3497dd7cddfSDavid du Colombier {
3507dd7cddfSDavid du Colombier 	va_list arg;
3517dd7cddfSDavid du Colombier 
3527dd7cddfSDavid du Colombier 	va_start(arg, fmt);
3539a747e4fSDavid du Colombier 	Bprint(&bout, "* bye ");
3549a747e4fSDavid du Colombier 	Bvprint(&bout, fmt, arg);
3559a747e4fSDavid du Colombier 	Bprint(&bout, "\r\n");
3569a747e4fSDavid du Colombier 	Bflush(&bout);
3579a747e4fSDavid du Colombier exits("rob2");
3587dd7cddfSDavid du Colombier 	exits(0);
3597dd7cddfSDavid du Colombier }
3607dd7cddfSDavid du Colombier 
3617dd7cddfSDavid du Colombier void
parseErr(char * msg)3627dd7cddfSDavid du Colombier parseErr(char *msg)
3637dd7cddfSDavid du Colombier {
3647dd7cddfSDavid du Colombier 	parseMsg = msg;
3657dd7cddfSDavid du Colombier 	longjmp(parseJmp, 1);
3667dd7cddfSDavid du Colombier }
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier /*
3697dd7cddfSDavid du Colombier  * an error occured while writing to the client
3707dd7cddfSDavid du Colombier  */
3717dd7cddfSDavid du Colombier void
writeErr(void)3727dd7cddfSDavid du Colombier writeErr(void)
3737dd7cddfSDavid du Colombier {
3747dd7cddfSDavid du Colombier 	cleaner();
3757dd7cddfSDavid du Colombier 	_exits("connection closed");
3767dd7cddfSDavid du Colombier }
3777dd7cddfSDavid du Colombier 
37859cc4ca5SDavid du Colombier static int
catcher(void * v,char * msg)37959cc4ca5SDavid du Colombier catcher(void *v, char *msg)
38059cc4ca5SDavid du Colombier {
38159cc4ca5SDavid du Colombier 	USED(v);
38259cc4ca5SDavid du Colombier 	if(strstr(msg, "closed pipe") != nil)
38359cc4ca5SDavid du Colombier 		return 1;
38459cc4ca5SDavid du Colombier 	return 0;
38559cc4ca5SDavid du Colombier }
38659cc4ca5SDavid du Colombier 
3877dd7cddfSDavid du Colombier /*
38859cc4ca5SDavid du Colombier  * wipes out the idleCmd backgroung process if it is around.
38959cc4ca5SDavid du Colombier  * this can only be called if the current proc has qlocked imaplock.
39059cc4ca5SDavid du Colombier  * it must be the last piece of imap4d code executed.
3917dd7cddfSDavid du Colombier  */
3927dd7cddfSDavid du Colombier static void
cleaner(void)3937dd7cddfSDavid du Colombier cleaner(void)
3947dd7cddfSDavid du Colombier {
3957dd7cddfSDavid du Colombier 	int i;
3967dd7cddfSDavid du Colombier 
39759cc4ca5SDavid du Colombier 	if(idlepid < 0)
39859cc4ca5SDavid du Colombier 		return;
39959cc4ca5SDavid du Colombier 	exiting = 1;
40059cc4ca5SDavid du Colombier 	close(0);
40159cc4ca5SDavid du Colombier 	close(1);
40259cc4ca5SDavid du Colombier 	close(2);
40359cc4ca5SDavid du Colombier 
40459cc4ca5SDavid du Colombier 	/*
40559cc4ca5SDavid du Colombier 	 * the other proc is either stuck in a read, a sleep,
40659cc4ca5SDavid du Colombier 	 * or is trying to lock imap4lock.
40759cc4ca5SDavid du Colombier 	 * get him out of it so he can exit cleanly
40859cc4ca5SDavid du Colombier 	 */
40959cc4ca5SDavid du Colombier 	qunlock(&imaplock);
4107dd7cddfSDavid du Colombier 	for(i = 0; i < 4; i++)
4117dd7cddfSDavid du Colombier 		postnote(PNGROUP, getpid(), "die");
4127dd7cddfSDavid du Colombier }
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier /*
4157dd7cddfSDavid du Colombier  * send any pending status updates to the client
4167dd7cddfSDavid du Colombier  * careful: shouldn't exit, because called by idle polling proc
4177dd7cddfSDavid du Colombier  *
4187dd7cddfSDavid du Colombier  * can't always send pending info
4197dd7cddfSDavid du Colombier  * in particular, can't send expunge info
4207dd7cddfSDavid du Colombier  * in response to a fetch, store, or search command.
4217dd7cddfSDavid du Colombier  *
4227dd7cddfSDavid du Colombier  * rfc2060 5.2:	server must send mailbox size updates
4237dd7cddfSDavid du Colombier  * rfc2060 5.2:	server may send flag updates
4247dd7cddfSDavid du Colombier  * rfc2060 5.5:	servers prohibited from sending expunge while fetch, store, search in progress
4257dd7cddfSDavid du Colombier  * rfc2060 7:	in selected state, server checks mailbox for new messages as part of every command
4267dd7cddfSDavid du Colombier  * 		sends untagged EXISTS and RECENT respsonses reflecting new size of the mailbox
4277dd7cddfSDavid du Colombier  * 		should also send appropriate untagged FETCH and EXPUNGE messages if another agent
4287dd7cddfSDavid du Colombier  * 		changes the state of any message flags or expunges any messages
4297dd7cddfSDavid du Colombier  * rfc2060 7.4.1	expunge server response must not be sent when no command is in progress,
4307dd7cddfSDavid du Colombier  * 		nor while responding to a fetch, stort, or search command (uid versions are ok)
4317dd7cddfSDavid du Colombier  * 		command only "in progress" after entirely parsed.
4327dd7cddfSDavid du Colombier  *
4337dd7cddfSDavid du Colombier  * strategy for third party deletion of messages or of a mailbox
4347dd7cddfSDavid du Colombier  *
4357dd7cddfSDavid du Colombier  * deletion of a selected mailbox => act like all message are expunged
4367dd7cddfSDavid du Colombier  *	not strictly allowed by rfc2180, but close to method 3.2.
4377dd7cddfSDavid du Colombier  *
4387dd7cddfSDavid du Colombier  * renaming same as deletion
4397dd7cddfSDavid du Colombier  *
4407dd7cddfSDavid du Colombier  * copy
4417dd7cddfSDavid du Colombier  *	reject iff a deleted message is in the request
4427dd7cddfSDavid du Colombier  *
4437dd7cddfSDavid du Colombier  * search, store, fetch operations on expunged messages
4447dd7cddfSDavid du Colombier  *	ignore the expunged messages
4457dd7cddfSDavid du Colombier  *	return tagged no if referenced
4467dd7cddfSDavid du Colombier  */
4477dd7cddfSDavid du Colombier static void
status(int expungeable,int uids)4487dd7cddfSDavid du Colombier status(int expungeable, int uids)
4497dd7cddfSDavid du Colombier {
4507dd7cddfSDavid du Colombier 	int tell;
4517dd7cddfSDavid du Colombier 
4527dd7cddfSDavid du Colombier 	if(!selected)
4537dd7cddfSDavid du Colombier 		return;
4547dd7cddfSDavid du Colombier 	tell = 0;
4557dd7cddfSDavid du Colombier 	if(expungeable)
4567dd7cddfSDavid du Colombier 		tell = expungeMsgs(selected, 1);
4577dd7cddfSDavid du Colombier 	if(selected->sendFlags)
4587dd7cddfSDavid du Colombier 		sendFlags(selected, uids);
4597dd7cddfSDavid du Colombier 	if(tell || selected->toldMax != selected->max){
46040fe8d0dSDavid du Colombier 		Bprint(&bout, "* %lud EXISTS\r\n", selected->max);
4617dd7cddfSDavid du Colombier 		selected->toldMax = selected->max;
4627dd7cddfSDavid du Colombier 	}
4637dd7cddfSDavid du Colombier 	if(tell || selected->toldRecent != selected->recent){
46440fe8d0dSDavid du Colombier 		Bprint(&bout, "* %lud RECENT\r\n", selected->recent);
4657dd7cddfSDavid du Colombier 		selected->toldRecent = selected->recent;
4667dd7cddfSDavid du Colombier 	}
46759cc4ca5SDavid du Colombier 	if(tell)
46859cc4ca5SDavid du Colombier 		closeImp(selected, checkBox(selected, 1));
4697dd7cddfSDavid du Colombier }
4707dd7cddfSDavid du Colombier 
4717dd7cddfSDavid du Colombier /*
4727dd7cddfSDavid du Colombier  * careful: can't exit, because called by idle polling proc
4737dd7cddfSDavid du Colombier  */
4747dd7cddfSDavid du Colombier static void
check(void)4757dd7cddfSDavid du Colombier check(void)
4767dd7cddfSDavid du Colombier {
4777dd7cddfSDavid du Colombier 	if(!selected)
4787dd7cddfSDavid du Colombier 		return;
4797dd7cddfSDavid du Colombier 	checkBox(selected, 0);
4807dd7cddfSDavid du Colombier 	status(1, 0);
4817dd7cddfSDavid du Colombier }
4827dd7cddfSDavid du Colombier 
4837dd7cddfSDavid du Colombier static void
appendCmd(char * tg,char * cmd)4847dd7cddfSDavid du Colombier appendCmd(char *tg, char *cmd)
4857dd7cddfSDavid du Colombier {
4867dd7cddfSDavid du Colombier 	char *mbox, head[128];
4877dd7cddfSDavid du Colombier 	ulong t, n, now;
4887dd7cddfSDavid du Colombier 	int flags, ok;
4897dd7cddfSDavid du Colombier 
4907dd7cddfSDavid du Colombier 	mustBe(' ');
4917dd7cddfSDavid du Colombier 	mbox = astring();
4927dd7cddfSDavid du Colombier 	mustBe(' ');
4937dd7cddfSDavid du Colombier 	flags = 0;
4947dd7cddfSDavid du Colombier 	if(peekc() == '('){
4957dd7cddfSDavid du Colombier 		flags = flagList();
4967dd7cddfSDavid du Colombier 		mustBe(' ');
4977dd7cddfSDavid du Colombier 	}
4987dd7cddfSDavid du Colombier 	now = time(nil);
4997dd7cddfSDavid du Colombier 	if(peekc() == '"'){
5007dd7cddfSDavid du Colombier 		t = imap4DateTime(quoted());
5017dd7cddfSDavid du Colombier 		if(t == ~0)
5027dd7cddfSDavid du Colombier 			parseErr("illegal date format");
5037dd7cddfSDavid du Colombier 		mustBe(' ');
5047dd7cddfSDavid du Colombier 		if(t > now)
5057dd7cddfSDavid du Colombier 			t = now;
5067dd7cddfSDavid du Colombier 	}else
5077dd7cddfSDavid du Colombier 		t = now;
5087dd7cddfSDavid du Colombier 	n = litlen();
5097dd7cddfSDavid du Colombier 
5107dd7cddfSDavid du Colombier 	mbox = mboxName(mbox);
5117dd7cddfSDavid du Colombier 	if(mbox == nil || !okMbox(mbox)){
5127dd7cddfSDavid du Colombier 		check();
513d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
5147dd7cddfSDavid du Colombier 		return;
5157dd7cddfSDavid du Colombier 	}
5169a747e4fSDavid du Colombier 	if(!cdExists(mboxDir, mbox)){
5177dd7cddfSDavid du Colombier 		check();
518d9306527SDavid du Colombier 		Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);
5197dd7cddfSDavid du Colombier 		return;
5207dd7cddfSDavid du Colombier 	}
5217dd7cddfSDavid du Colombier 
5227dd7cddfSDavid du Colombier 	snprint(head, sizeof(head), "From %s %s", username, ctime(t));
5237dd7cddfSDavid du Colombier 	ok = appendSave(mbox, flags, head, &bin, n);
5247dd7cddfSDavid du Colombier 	crnl();
52580ee5cbfSDavid du Colombier 	check();
52680ee5cbfSDavid du Colombier 	if(ok)
527d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
52880ee5cbfSDavid du Colombier 	else
529d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s message save failed\r\n", tg, cmd);
5307dd7cddfSDavid du Colombier }
5317dd7cddfSDavid du Colombier 
5327dd7cddfSDavid du Colombier static void
authenticateCmd(char * tg,char * cmd)5337dd7cddfSDavid du Colombier authenticateCmd(char *tg, char *cmd)
5347dd7cddfSDavid du Colombier {
5357dd7cddfSDavid du Colombier 	char *s, *t;
5367dd7cddfSDavid du Colombier 
5377dd7cddfSDavid du Colombier 	mustBe(' ');
5387dd7cddfSDavid du Colombier 	s = atom();
5397dd7cddfSDavid du Colombier 	crnl();
5409a747e4fSDavid du Colombier 	auth_freechal(chal);
5419a747e4fSDavid du Colombier 	chal = nil;
5427dd7cddfSDavid du Colombier 	if(cistrcmp(s, "cram-md5") == 0){
5437dd7cddfSDavid du Colombier 		t = cramauth();
5447dd7cddfSDavid du Colombier 		if(t == nil){
545d9306527SDavid du Colombier 			Bprint(&bout, "%s OK %s\r\n", tg, cmd);
5467dd7cddfSDavid du Colombier 			imapState = SAuthed;
5477dd7cddfSDavid du Colombier 		}else
548d9306527SDavid du Colombier 			Bprint(&bout, "%s NO %s failed %s\r\n", tg, cmd, t);
5497dd7cddfSDavid du Colombier 	}else
550d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s unsupported authentication protocol\r\n", tg, cmd);
5517dd7cddfSDavid du Colombier }
5527dd7cddfSDavid du Colombier 
5537dd7cddfSDavid du Colombier static void
capabilityCmd(char * tg,char * cmd)5547dd7cddfSDavid du Colombier capabilityCmd(char *tg, char *cmd)
5557dd7cddfSDavid du Colombier {
5567dd7cddfSDavid du Colombier 	crnl();
5577dd7cddfSDavid du Colombier 	check();
5586b6b9ac8SDavid du Colombier // nslocum's capabilities
5596b6b9ac8SDavid du Colombier //	Bprint(&bout, "* CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT\r\n");
560d9306527SDavid du Colombier 	Bprint(&bout, "* CAPABILITY IMAP4REV1 IDLE NAMESPACE AUTH=CRAM-MD5\r\n");
561d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s\r\n", tg, cmd);
5627dd7cddfSDavid du Colombier }
5637dd7cddfSDavid du Colombier 
5647dd7cddfSDavid du Colombier static void
closeCmd(char * tg,char * cmd)5657dd7cddfSDavid du Colombier closeCmd(char *tg, char *cmd)
5667dd7cddfSDavid du Colombier {
5677dd7cddfSDavid du Colombier 	crnl();
5687dd7cddfSDavid du Colombier 	imapState = SAuthed;
5697dd7cddfSDavid du Colombier 	closeBox(selected, 1);
5707dd7cddfSDavid du Colombier 	selected = nil;
571d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s mailbox closed, now in authenticated state\r\n", tg, cmd);
5727dd7cddfSDavid du Colombier }
5737dd7cddfSDavid du Colombier 
5747dd7cddfSDavid du Colombier /*
5757dd7cddfSDavid du Colombier  * note: message id's are before any pending expunges
5767dd7cddfSDavid du Colombier  */
5777dd7cddfSDavid du Colombier static void
copyCmd(char * tg,char * cmd)5787dd7cddfSDavid du Colombier copyCmd(char *tg, char *cmd)
5797dd7cddfSDavid du Colombier {
5807dd7cddfSDavid du Colombier 	copyUCmd(tg, cmd, 0);
5817dd7cddfSDavid du Colombier }
5827dd7cddfSDavid du Colombier 
5837dd7cddfSDavid du Colombier static void
copyUCmd(char * tg,char * cmd,int uids)5847dd7cddfSDavid du Colombier copyUCmd(char *tg, char *cmd, int uids)
5857dd7cddfSDavid du Colombier {
5867dd7cddfSDavid du Colombier 	MsgSet *ms;
5877dd7cddfSDavid du Colombier 	char *uid, *mbox;
5887dd7cddfSDavid du Colombier 	ulong max;
5897dd7cddfSDavid du Colombier 	int ok;
5907dd7cddfSDavid du Colombier 
5917dd7cddfSDavid du Colombier 	mustBe(' ');
592becaf2abSDavid du Colombier 	ms = msgSet(uids);
5937dd7cddfSDavid du Colombier 	mustBe(' ');
5947dd7cddfSDavid du Colombier 	mbox = astring();
5957dd7cddfSDavid du Colombier 	crnl();
5967dd7cddfSDavid du Colombier 
5977dd7cddfSDavid du Colombier 	uid = "";
5987dd7cddfSDavid du Colombier 	if(uids)
5997dd7cddfSDavid du Colombier 		uid = "uid ";
6007dd7cddfSDavid du Colombier 
6017dd7cddfSDavid du Colombier 	mbox = mboxName(mbox);
6027dd7cddfSDavid du Colombier 	if(mbox == nil || !okMbox(mbox)){
6037dd7cddfSDavid du Colombier 		status(1, uids);
604d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s%s bad mailbox\r\n", tg, uid, cmd);
6057dd7cddfSDavid du Colombier 		return;
6067dd7cddfSDavid du Colombier 	}
60748207d97SDavid du Colombier 	if(cistrcmp(mbox, "inbox") == 0)
60848207d97SDavid du Colombier 		mbox = "mbox";
6099a747e4fSDavid du Colombier 	if(!cdExists(mboxDir, mbox)){
6107dd7cddfSDavid du Colombier 		check();
611d9306527SDavid du Colombier 		Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);
6127dd7cddfSDavid du Colombier 		return;
6137dd7cddfSDavid du Colombier 	}
6147dd7cddfSDavid du Colombier 
6157dd7cddfSDavid du Colombier 	max = selected->max;
6167dd7cddfSDavid du Colombier 	checkBox(selected, 0);
6177dd7cddfSDavid du Colombier 	ok = forMsgs(selected, ms, max, uids, copyCheck, nil);
6187dd7cddfSDavid du Colombier 	if(ok)
6197dd7cddfSDavid du Colombier 		ok = forMsgs(selected, ms, max, uids, copySave, mbox);
6207dd7cddfSDavid du Colombier 
6217dd7cddfSDavid du Colombier 	status(1, uids);
6227dd7cddfSDavid du Colombier 	if(ok)
623d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
6247dd7cddfSDavid du Colombier 	else
625d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
6267dd7cddfSDavid du Colombier }
6277dd7cddfSDavid du Colombier 
6287dd7cddfSDavid du Colombier static void
createCmd(char * tg,char * cmd)6297dd7cddfSDavid du Colombier createCmd(char *tg, char *cmd)
6307dd7cddfSDavid du Colombier {
6317dd7cddfSDavid du Colombier 	char *mbox, *m;
6327dd7cddfSDavid du Colombier 	int fd, slash;
6337dd7cddfSDavid du Colombier 
6347dd7cddfSDavid du Colombier 	mustBe(' ');
6357dd7cddfSDavid du Colombier 	mbox = astring();
6367dd7cddfSDavid du Colombier 	crnl();
6377dd7cddfSDavid du Colombier 	check();
6387dd7cddfSDavid du Colombier 
6397dd7cddfSDavid du Colombier 	m = strchr(mbox, '\0');
6407dd7cddfSDavid du Colombier 	slash = m != mbox && m[-1] == '/';
6417dd7cddfSDavid du Colombier 	mbox = mboxName(mbox);
6427dd7cddfSDavid du Colombier 	if(mbox == nil || !okMbox(mbox)){
643d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
6447dd7cddfSDavid du Colombier 		return;
6457dd7cddfSDavid du Colombier 	}
6467dd7cddfSDavid du Colombier 	if(cistrcmp(mbox, "inbox") == 0){
647d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s cannot remotely create INBOX\r\n", tg, cmd);
6487dd7cddfSDavid du Colombier 		return;
6497dd7cddfSDavid du Colombier 	}
6507dd7cddfSDavid du Colombier 	if(access(mbox, AEXIST) >= 0){
651d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);
6527dd7cddfSDavid du Colombier 		return;
6537dd7cddfSDavid du Colombier 	}
6547dd7cddfSDavid du Colombier 
6557dd7cddfSDavid du Colombier 	fd = createBox(mbox, slash);
6567dd7cddfSDavid du Colombier 	close(fd);
6577dd7cddfSDavid du Colombier 	if(fd < 0)
658d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s cannot create mailbox %s\r\n", tg, cmd, mbox);
6597dd7cddfSDavid du Colombier 	else
660d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);
6617dd7cddfSDavid du Colombier }
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier static void
deleteCmd(char * tg,char * cmd)6647dd7cddfSDavid du Colombier deleteCmd(char *tg, char *cmd)
6657dd7cddfSDavid du Colombier {
6667dd7cddfSDavid du Colombier 	char *mbox, *imp;
6677dd7cddfSDavid du Colombier 
6687dd7cddfSDavid du Colombier 	mustBe(' ');
6697dd7cddfSDavid du Colombier 	mbox = astring();
6707dd7cddfSDavid du Colombier 	crnl();
6717dd7cddfSDavid du Colombier 	check();
6727dd7cddfSDavid du Colombier 
6737dd7cddfSDavid du Colombier 	mbox = mboxName(mbox);
6747dd7cddfSDavid du Colombier 	if(mbox == nil || !okMbox(mbox)){
675d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
6767dd7cddfSDavid du Colombier 		return;
6777dd7cddfSDavid du Colombier 	}
6787dd7cddfSDavid du Colombier 
6797dd7cddfSDavid du Colombier 	imp = impName(mbox);
6807dd7cddfSDavid du Colombier 	if(cistrcmp(mbox, "inbox") == 0
6819a747e4fSDavid du Colombier 	|| imp != nil && cdRemove(mboxDir, imp) < 0 && cdExists(mboxDir, imp)
6827dd7cddfSDavid du Colombier 	|| cdRemove(mboxDir, mbox) < 0)
683d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s cannot delete mailbox %s\r\n", tg, cmd, mbox);
6847dd7cddfSDavid du Colombier 	else
685d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);
6867dd7cddfSDavid du Colombier }
6877dd7cddfSDavid du Colombier 
6887dd7cddfSDavid du Colombier static void
expungeCmd(char * tg,char * cmd)6897dd7cddfSDavid du Colombier expungeCmd(char *tg, char *cmd)
6907dd7cddfSDavid du Colombier {
6917dd7cddfSDavid du Colombier 	int ok;
6927dd7cddfSDavid du Colombier 
6937dd7cddfSDavid du Colombier 	crnl();
6947dd7cddfSDavid du Colombier 	ok = deleteMsgs(selected);
6957dd7cddfSDavid du Colombier 	check();
6967dd7cddfSDavid du Colombier 	if(ok)
697d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s messages erased\r\n", tg, cmd);
6987dd7cddfSDavid du Colombier 	else
699d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s some messages not expunged\r\n", tg, cmd);
7007dd7cddfSDavid du Colombier }
7017dd7cddfSDavid du Colombier 
7027dd7cddfSDavid du Colombier static void
fetchCmd(char * tg,char * cmd)7037dd7cddfSDavid du Colombier fetchCmd(char *tg, char *cmd)
7047dd7cddfSDavid du Colombier {
7057dd7cddfSDavid du Colombier 	fetchUCmd(tg, cmd, 0);
7067dd7cddfSDavid du Colombier }
7077dd7cddfSDavid du Colombier 
7087dd7cddfSDavid du Colombier static void
fetchUCmd(char * tg,char * cmd,int uids)7097dd7cddfSDavid du Colombier fetchUCmd(char *tg, char *cmd, int uids)
7107dd7cddfSDavid du Colombier {
7117dd7cddfSDavid du Colombier 	Fetch *f;
7127dd7cddfSDavid du Colombier 	MsgSet *ms;
7137dd7cddfSDavid du Colombier 	MbLock *ml;
7147dd7cddfSDavid du Colombier 	char *uid;
7157dd7cddfSDavid du Colombier 	ulong max;
7167dd7cddfSDavid du Colombier 	int ok;
7177dd7cddfSDavid du Colombier 
7187dd7cddfSDavid du Colombier 	mustBe(' ');
719becaf2abSDavid du Colombier 	ms = msgSet(uids);
7207dd7cddfSDavid du Colombier 	mustBe(' ');
7217dd7cddfSDavid du Colombier 	f = fetchWhat();
7227dd7cddfSDavid du Colombier 	crnl();
7237dd7cddfSDavid du Colombier 	uid = "";
7247dd7cddfSDavid du Colombier 	if(uids)
7257dd7cddfSDavid du Colombier 		uid = "uid ";
7267dd7cddfSDavid du Colombier 	max = selected->max;
7277dd7cddfSDavid du Colombier 	ml = checkBox(selected, 1);
7287dd7cddfSDavid du Colombier 	if(ml != nil)
7297dd7cddfSDavid du Colombier 		forMsgs(selected, ms, max, uids, fetchSeen, f);
7307dd7cddfSDavid du Colombier 	closeImp(selected, ml);
7317dd7cddfSDavid du Colombier 	ok = ml != nil && forMsgs(selected, ms, max, uids, fetchMsg, f);
7327dd7cddfSDavid du Colombier 	status(uids, uids);
7337dd7cddfSDavid du Colombier 	if(ok)
734d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
7357dd7cddfSDavid du Colombier 	else
736d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
7377dd7cddfSDavid du Colombier }
7387dd7cddfSDavid du Colombier 
7397dd7cddfSDavid du Colombier static void
idleCmd(char * tg,char * cmd)7407dd7cddfSDavid du Colombier idleCmd(char *tg, char *cmd)
7417dd7cddfSDavid du Colombier {
74259cc4ca5SDavid du Colombier 	int c, pid;
7437dd7cddfSDavid du Colombier 
7447dd7cddfSDavid du Colombier 	crnl();
7457dd7cddfSDavid du Colombier 	Bprint(&bout, "+ idling, waiting for done\r\n");
7467dd7cddfSDavid du Colombier 	if(Bflush(&bout) < 0)
7477dd7cddfSDavid du Colombier 		writeErr();
7487dd7cddfSDavid du Colombier 
74959cc4ca5SDavid du Colombier 	if(idlepid < 0){
7507dd7cddfSDavid du Colombier 		pid = rfork(RFPROC|RFMEM|RFNOWAIT);
7517dd7cddfSDavid du Colombier 		if(pid == 0){
7527dd7cddfSDavid du Colombier 			for(;;){
7537dd7cddfSDavid du Colombier 				qlock(&imaplock);
75459cc4ca5SDavid du Colombier 				if(exiting)
75559cc4ca5SDavid du Colombier 					break;
75659cc4ca5SDavid du Colombier 
75759cc4ca5SDavid du Colombier 				/*
75859cc4ca5SDavid du Colombier 				 * parent may have changed curDir, but it doesn't change our .
75959cc4ca5SDavid du Colombier 				 */
76059cc4ca5SDavid du Colombier 				resetCurDir();
76159cc4ca5SDavid du Colombier 
7627dd7cddfSDavid du Colombier 				check();
7637dd7cddfSDavid du Colombier 				if(Bflush(&bout) < 0)
7647dd7cddfSDavid du Colombier 					writeErr();
7657dd7cddfSDavid du Colombier 				qunlock(&imaplock);
7667dd7cddfSDavid du Colombier 				sleep(15*1000);
76780ee5cbfSDavid du Colombier 				enableForwarding();
7687dd7cddfSDavid du Colombier 			}
7699a747e4fSDavid du Colombier _exits("rob3");
7707dd7cddfSDavid du Colombier 			_exits(0);
7717dd7cddfSDavid du Colombier 		}
77259cc4ca5SDavid du Colombier 		idlepid = pid;
77359cc4ca5SDavid du Colombier 	}
7747dd7cddfSDavid du Colombier 
7757dd7cddfSDavid du Colombier 	qunlock(&imaplock);
7767dd7cddfSDavid du Colombier 
77759cc4ca5SDavid du Colombier 	/*
77859cc4ca5SDavid du Colombier 	 * clear out the next line, which is supposed to contain (case-insensitive)
77959cc4ca5SDavid du Colombier 	 * done\n
78059cc4ca5SDavid du Colombier 	 * this is special code since it has to dance with the idle polling proc
78159cc4ca5SDavid du Colombier 	 * and handle exiting correctly.
78259cc4ca5SDavid du Colombier 	 */
78359cc4ca5SDavid du Colombier 	for(;;){
78459cc4ca5SDavid du Colombier 		c = getc();
78559cc4ca5SDavid du Colombier 		if(c < 0){
78659cc4ca5SDavid du Colombier 			qlock(&imaplock);
78759cc4ca5SDavid du Colombier 			if(!exiting)
78859cc4ca5SDavid du Colombier 				cleaner();
7899a747e4fSDavid du Colombier _exits("rob4");
79059cc4ca5SDavid du Colombier 			_exits(0);
79159cc4ca5SDavid du Colombier 		}
79259cc4ca5SDavid du Colombier 		if(c == '\n')
79359cc4ca5SDavid du Colombier 			break;
79459cc4ca5SDavid du Colombier 	}
79559cc4ca5SDavid du Colombier 
79659cc4ca5SDavid du Colombier 	qlock(&imaplock);
79759cc4ca5SDavid du Colombier 	if(exiting)
7989a747e4fSDavid du Colombier {_exits("rob5");
79959cc4ca5SDavid du Colombier 		_exits(0);
8009a747e4fSDavid du Colombier }
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier 	/*
8037dd7cddfSDavid du Colombier 	 * child may have changed curDir, but it doesn't change our .
8047dd7cddfSDavid du Colombier 	 */
8057dd7cddfSDavid du Colombier 	resetCurDir();
8067dd7cddfSDavid du Colombier 
8077dd7cddfSDavid du Colombier 	check();
808d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s terminated\r\n", tg, cmd);
8097dd7cddfSDavid du Colombier }
8107dd7cddfSDavid du Colombier 
8117dd7cddfSDavid du Colombier static void
listCmd(char * tg,char * cmd)8127dd7cddfSDavid du Colombier listCmd(char *tg, char *cmd)
8137dd7cddfSDavid du Colombier {
8147dd7cddfSDavid du Colombier 	char *s, *t, *ss, *ref, *mbox;
8157dd7cddfSDavid du Colombier 	int n;
8167dd7cddfSDavid du Colombier 
8177dd7cddfSDavid du Colombier 	mustBe(' ');
8187dd7cddfSDavid du Colombier 	s = astring();
8197dd7cddfSDavid du Colombier 	mustBe(' ');
8207dd7cddfSDavid du Colombier 	t = listmbox();
8217dd7cddfSDavid du Colombier 	crnl();
8227dd7cddfSDavid du Colombier 	check();
8237dd7cddfSDavid du Colombier 	ref = mutf7str(s);
8247dd7cddfSDavid du Colombier 	mbox = mutf7str(t);
8257dd7cddfSDavid du Colombier 	if(ref == nil || mbox == nil){
826d9306527SDavid du Colombier 		Bprint(&bout, "%s BAD %s mailbox name not in modified utf-7\r\n", tg, cmd);
8277dd7cddfSDavid du Colombier 		return;
8287dd7cddfSDavid du Colombier 	}
8297dd7cddfSDavid du Colombier 
8307dd7cddfSDavid du Colombier 	/*
8317dd7cddfSDavid du Colombier 	 * special request for hierarchy delimiter and root name
8327dd7cddfSDavid du Colombier 	 * root name appears to be name up to and including any delimiter,
8337dd7cddfSDavid du Colombier 	 * or the empty string, if there is no delimiter.
8347dd7cddfSDavid du Colombier 	 *
8357dd7cddfSDavid du Colombier 	 * this must change if the # namespace convention is supported.
8367dd7cddfSDavid du Colombier 	 */
8377dd7cddfSDavid du Colombier 	if(*mbox == '\0'){
8387dd7cddfSDavid du Colombier 		s = strchr(ref, '/');
8397dd7cddfSDavid du Colombier 		if(s == nil)
8407dd7cddfSDavid du Colombier 			ref = "";
8417dd7cddfSDavid du Colombier 		else
8427dd7cddfSDavid du Colombier 			s[1] = '\0';
8437dd7cddfSDavid du Colombier 		Bprint(&bout, "* %s (\\Noselect) \"/\" \"%s\"\r\n", cmd, ref);
844d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s\r\n", tg, cmd);
8457dd7cddfSDavid du Colombier 		return;
8467dd7cddfSDavid du Colombier 	}
8477dd7cddfSDavid du Colombier 
8487dd7cddfSDavid du Colombier 
8497dd7cddfSDavid du Colombier 	/*
8507dd7cddfSDavid du Colombier 	 * massage the listing name:
8517dd7cddfSDavid du Colombier 	 * clean up the components individually,
8527dd7cddfSDavid du Colombier 	 * then rip off componenets from the ref to
8537dd7cddfSDavid du Colombier 	 * take care of leading ..'s in the mbox.
8547dd7cddfSDavid du Colombier 	 *
8557dd7cddfSDavid du Colombier 	 * the cleanup can wipe out * followed by a ..
8567dd7cddfSDavid du Colombier 	 * tough luck if such a stupid pattern is given.
8577dd7cddfSDavid du Colombier 	 */
8587dd7cddfSDavid du Colombier 	cleanname(mbox);
8597dd7cddfSDavid du Colombier 	if(strcmp(mbox, ".") == 0)
8607dd7cddfSDavid du Colombier 		*mbox = '\0';
8617dd7cddfSDavid du Colombier 	if(mbox[0] == '/')
8627dd7cddfSDavid du Colombier 		*ref = '\0';
8637dd7cddfSDavid du Colombier 	else if(*ref != '\0'){
8647dd7cddfSDavid du Colombier 		cleanname(ref);
8657dd7cddfSDavid du Colombier 		if(strcmp(ref, ".") == 0)
8667dd7cddfSDavid du Colombier 			*ref = '\0';
8677dd7cddfSDavid du Colombier 	}else
8687dd7cddfSDavid du Colombier 		*ref = '\0';
8697dd7cddfSDavid du Colombier 	while(*ref && isdotdot(mbox)){
8707dd7cddfSDavid du Colombier 		s = strrchr(ref, '/');
8717dd7cddfSDavid du Colombier 		if(s == nil)
8727dd7cddfSDavid du Colombier 			s = ref;
8737dd7cddfSDavid du Colombier 		if(isdotdot(s))
8747dd7cddfSDavid du Colombier 			break;
8757dd7cddfSDavid du Colombier 		*s = '\0';
8767dd7cddfSDavid du Colombier 		mbox += 2;
8777dd7cddfSDavid du Colombier 		if(*mbox == '/')
8787dd7cddfSDavid du Colombier 			mbox++;
8797dd7cddfSDavid du Colombier 	}
8807dd7cddfSDavid du Colombier 	if(*ref == '\0'){
8817dd7cddfSDavid du Colombier 		s = mbox;
8827dd7cddfSDavid du Colombier 		ss = s;
8837dd7cddfSDavid du Colombier 	}else{
8847dd7cddfSDavid du Colombier 		n = strlen(ref) + strlen(mbox) + 2;
88580ee5cbfSDavid du Colombier 		t = binalloc(&parseBin, n, 0);
8867dd7cddfSDavid du Colombier 		if(t == nil)
8877dd7cddfSDavid du Colombier 			parseErr("out of memory");
8887dd7cddfSDavid du Colombier 		snprint(t, n, "%s/%s", ref, mbox);
8897dd7cddfSDavid du Colombier 		s = t;
8907dd7cddfSDavid du Colombier 		ss = s + strlen(ref);
8917dd7cddfSDavid du Colombier 	}
8927dd7cddfSDavid du Colombier 
8937dd7cddfSDavid du Colombier 	/*
8947dd7cddfSDavid du Colombier 	 * only allow activity in /mail/box
8957dd7cddfSDavid du Colombier 	 */
8967dd7cddfSDavid du Colombier 	if(s[0] == '/' || isdotdot(s)){
897d9306527SDavid du Colombier 		Bprint(&bout, "%s NO illegal mailbox pattern\r\n", tg);
8987dd7cddfSDavid du Colombier 		return;
8997dd7cddfSDavid du Colombier 	}
9007dd7cddfSDavid du Colombier 
9017dd7cddfSDavid du Colombier 	if(cistrcmp(cmd, "lsub") == 0)
9027dd7cddfSDavid du Colombier 		lsubBoxes(cmd, s, ss);
9037dd7cddfSDavid du Colombier 	else
9047dd7cddfSDavid du Colombier 		listBoxes(cmd, s, ss);
905d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
906d9306527SDavid du Colombier }
907d9306527SDavid du Colombier 
908d9306527SDavid du Colombier static char*
passCR(char * u,char * p)909d9306527SDavid du Colombier passCR(char*u, char*p)
910d9306527SDavid du Colombier {
911d9306527SDavid du Colombier 	static char Ebadch[] = "can't get challenge";
912d9306527SDavid du Colombier 	static char nchall[64];
913d9306527SDavid du Colombier 	static char response[64];
914d9306527SDavid du Colombier 	static Chalstate *ch = nil;
915d9306527SDavid du Colombier 	AuthInfo *ai;
916d9306527SDavid du Colombier 
917d9306527SDavid du Colombier again:
918d9306527SDavid du Colombier 	if (ch == nil){
919d9306527SDavid du Colombier 		if(!(ch = auth_challenge("proto=p9cr role=server user=%q", u)))
920d9306527SDavid du Colombier 			return Ebadch;
921d9306527SDavid du Colombier 		snprint(nchall, 64, " encrypt challenge: %s", ch->chal);
922d9306527SDavid du Colombier 		return nchall;
923d9306527SDavid du Colombier 	} else {
924d9306527SDavid du Colombier 		strncpy(response, p, 64);
925d9306527SDavid du Colombier 		ch->resp = response;
926d9306527SDavid du Colombier 		ch->nresp = strlen(response);
927d9306527SDavid du Colombier 		ai = auth_response(ch);
928d9306527SDavid du Colombier 		auth_freechal(ch);
929d9306527SDavid du Colombier 		ch = nil;
930d9306527SDavid du Colombier 		if (ai == nil)
931d9306527SDavid du Colombier 			goto again;
932d9306527SDavid du Colombier 		setupuser(ai);
933d9306527SDavid du Colombier 		return nil;
934d9306527SDavid du Colombier 	}
935d9306527SDavid du Colombier 
9367dd7cddfSDavid du Colombier }
9377dd7cddfSDavid du Colombier 
9387dd7cddfSDavid du Colombier static void
loginCmd(char * tg,char * cmd)9397dd7cddfSDavid du Colombier loginCmd(char *tg, char *cmd)
9407dd7cddfSDavid du Colombier {
9417dd7cddfSDavid du Colombier 	char *s, *t;
9429a747e4fSDavid du Colombier 	AuthInfo *ai;
943d9306527SDavid du Colombier 	char*r;
9447dd7cddfSDavid du Colombier 	mustBe(' ');
9457dd7cddfSDavid du Colombier 	s = astring();	/* uid */
9467dd7cddfSDavid du Colombier 	mustBe(' ');
9477dd7cddfSDavid du Colombier 	t = astring();	/* password */
9487dd7cddfSDavid du Colombier 	crnl();
949d9306527SDavid du Colombier 	if(allowCR){
950d9306527SDavid du Colombier 		if ((r = passCR(s, t)) == nil){
951d9306527SDavid du Colombier 			Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd);
9527dd7cddfSDavid du Colombier 			imapState = SAuthed;
953d9306527SDavid du Colombier 		} else {
954d9306527SDavid du Colombier 			Bprint(&bout, "* NO [ALERT] %s\r\n", r);
955d9306527SDavid du Colombier 			Bprint(&bout, "%s NO %s succeeded\r\n", tg, cmd);
956d9306527SDavid du Colombier 		}
9577dd7cddfSDavid du Colombier 		return;
9587dd7cddfSDavid du Colombier 	}
959d9306527SDavid du Colombier 	else if(allowPass){
960d9306527SDavid du Colombier 		if(ai = passLogin(s, t)){
961d9306527SDavid du Colombier 			setupuser(ai);
962d9306527SDavid du Colombier 			Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd);
963d9306527SDavid du Colombier 			imapState = SAuthed;
964d9306527SDavid du Colombier 		}else
965d9306527SDavid du Colombier 			Bprint(&bout, "%s NO %s failed check\r\n", tg, cmd);
966d9306527SDavid du Colombier 		return;
967d9306527SDavid du Colombier 	}
968d9306527SDavid du Colombier 	Bprint(&bout, "%s NO %s plaintext passwords disallowed\r\n", tg, cmd);
9697dd7cddfSDavid du Colombier }
9707dd7cddfSDavid du Colombier 
9717dd7cddfSDavid du Colombier /*
9727dd7cddfSDavid du Colombier  * logout or x-exit, which doesn't expunge the mailbox
9737dd7cddfSDavid du Colombier  */
9747dd7cddfSDavid du Colombier static void
logoutCmd(char * tg,char * cmd)9757dd7cddfSDavid du Colombier logoutCmd(char *tg, char *cmd)
9767dd7cddfSDavid du Colombier {
9777dd7cddfSDavid du Colombier 	crnl();
9787dd7cddfSDavid du Colombier 
9797dd7cddfSDavid du Colombier 	if(cmd[0] != 'x' && selected){
9807dd7cddfSDavid du Colombier 		closeBox(selected, 1);
9817dd7cddfSDavid du Colombier 		selected = nil;
9827dd7cddfSDavid du Colombier 	}
9837dd7cddfSDavid du Colombier 	Bprint(&bout, "* bye\r\n");
984d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
9859a747e4fSDavid du Colombier exits("rob6");
9867dd7cddfSDavid du Colombier 	exits(0);
9877dd7cddfSDavid du Colombier }
9887dd7cddfSDavid du Colombier 
9897dd7cddfSDavid du Colombier static void
namespaceCmd(char * tg,char * cmd)9907dd7cddfSDavid du Colombier namespaceCmd(char *tg, char *cmd)
9917dd7cddfSDavid du Colombier {
9927dd7cddfSDavid du Colombier 	crnl();
9937dd7cddfSDavid du Colombier 	check();
9947dd7cddfSDavid du Colombier 
9957dd7cddfSDavid du Colombier 	/*
9967dd7cddfSDavid du Colombier 	 * personal, other users, shared namespaces
9977dd7cddfSDavid du Colombier 	 * send back nil or descriptions of (prefix heirarchy-delim) for each case
9987dd7cddfSDavid du Colombier 	 */
99940fe8d0dSDavid du Colombier 	Bprint(&bout, "* NAMESPACE ((\"\" \"/\")) nil nil\r\n");
1000d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
10017dd7cddfSDavid du Colombier }
10027dd7cddfSDavid du Colombier 
10037dd7cddfSDavid du Colombier static void
noopCmd(char * tg,char * cmd)10047dd7cddfSDavid du Colombier noopCmd(char *tg, char *cmd)
10057dd7cddfSDavid du Colombier {
10067dd7cddfSDavid du Colombier 	crnl();
10077dd7cddfSDavid du Colombier 	check();
1008d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
100980ee5cbfSDavid du Colombier 	enableForwarding();
10107dd7cddfSDavid du Colombier }
10117dd7cddfSDavid du Colombier 
10127dd7cddfSDavid du Colombier /*
10137dd7cddfSDavid du Colombier  * this is only a partial implementation
10147dd7cddfSDavid du Colombier  * should copy files to other directories,
10157dd7cddfSDavid du Colombier  * and copy & truncate inbox
10167dd7cddfSDavid du Colombier  */
10177dd7cddfSDavid du Colombier static void
renameCmd(char * tg,char * cmd)10187dd7cddfSDavid du Colombier renameCmd(char *tg, char *cmd)
10197dd7cddfSDavid du Colombier {
10207dd7cddfSDavid du Colombier 	char *from, *to;
10217dd7cddfSDavid du Colombier 	int ok;
10227dd7cddfSDavid du Colombier 
10237dd7cddfSDavid du Colombier 	mustBe(' ');
10247dd7cddfSDavid du Colombier 	from = astring();
10257dd7cddfSDavid du Colombier 	mustBe(' ');
10267dd7cddfSDavid du Colombier 	to = astring();
10277dd7cddfSDavid du Colombier 	crnl();
10287dd7cddfSDavid du Colombier 	check();
10297dd7cddfSDavid du Colombier 
10307dd7cddfSDavid du Colombier 	to = mboxName(to);
10317dd7cddfSDavid du Colombier 	if(to == nil || !okMbox(to) || cistrcmp(to, "inbox") == 0){
1032d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd);
10337dd7cddfSDavid du Colombier 		return;
10347dd7cddfSDavid du Colombier 	}
10357dd7cddfSDavid du Colombier 	if(access(to, AEXIST) >= 0){
1036d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);
10377dd7cddfSDavid du Colombier 		return;
10387dd7cddfSDavid du Colombier 	}
10397dd7cddfSDavid du Colombier 	from = mboxName(from);
10407dd7cddfSDavid du Colombier 	if(from == nil || !okMbox(from)){
1041d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd);
10427dd7cddfSDavid du Colombier 		return;
10437dd7cddfSDavid du Colombier 	}
10447dd7cddfSDavid du Colombier 	if(cistrcmp(from, "inbox") == 0)
10457dd7cddfSDavid du Colombier 		ok = copyBox(from, to, 0);
10467dd7cddfSDavid du Colombier 	else
10477dd7cddfSDavid du Colombier 		ok = moveBox(from, to);
10487dd7cddfSDavid du Colombier 
10497dd7cddfSDavid du Colombier 	if(ok)
1050d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
10517dd7cddfSDavid du Colombier 	else
1052d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s failed\r\n", tg, cmd);
10537dd7cddfSDavid du Colombier }
10547dd7cddfSDavid du Colombier 
10557dd7cddfSDavid du Colombier static void
searchCmd(char * tg,char * cmd)10567dd7cddfSDavid du Colombier searchCmd(char *tg, char *cmd)
10577dd7cddfSDavid du Colombier {
10587dd7cddfSDavid du Colombier 	searchUCmd(tg, cmd, 0);
10597dd7cddfSDavid du Colombier }
10607dd7cddfSDavid du Colombier 
10617dd7cddfSDavid du Colombier static void
searchUCmd(char * tg,char * cmd,int uids)10627dd7cddfSDavid du Colombier searchUCmd(char *tg, char *cmd, int uids)
10637dd7cddfSDavid du Colombier {
10647dd7cddfSDavid du Colombier 	Search rock;
10657dd7cddfSDavid du Colombier 	Msg *m;
10667dd7cddfSDavid du Colombier 	char *uid;
10677dd7cddfSDavid du Colombier 	ulong id;
10687dd7cddfSDavid du Colombier 
10697dd7cddfSDavid du Colombier 	mustBe(' ');
10707dd7cddfSDavid du Colombier 	rock.next = nil;
10717dd7cddfSDavid du Colombier 	searchKeys(1, &rock);
10727dd7cddfSDavid du Colombier 	crnl();
10737dd7cddfSDavid du Colombier 	uid = "";
10747dd7cddfSDavid du Colombier 	if(uids)
10757dd7cddfSDavid du Colombier 		uid = "uid ";
10767dd7cddfSDavid du Colombier 	if(rock.next != nil && rock.next->key == SKCharset){
1077*58da3067SDavid du Colombier 		if(cistrcmp(rock.next->s, "utf-8") != 0
10787dd7cddfSDavid du Colombier 		&& cistrcmp(rock.next->s, "us-ascii") != 0){
1079*58da3067SDavid du Colombier 			Bprint(&bout, "%s NO [BADCHARSET] (\"US-ASCII\" \"UTF-8\") %s%s failed\r\n",
1080*58da3067SDavid du Colombier 				tg, uid, cmd);
10817dd7cddfSDavid du Colombier 			checkBox(selected, 0);
10827dd7cddfSDavid du Colombier 			status(uids, uids);
10837dd7cddfSDavid du Colombier 			return;
10847dd7cddfSDavid du Colombier 		}
10857dd7cddfSDavid du Colombier 		rock.next = rock.next->next;
10867dd7cddfSDavid du Colombier 	}
10877dd7cddfSDavid du Colombier 	Bprint(&bout, "* search");
10887dd7cddfSDavid du Colombier 	for(m = selected->msgs; m != nil; m = m->next)
10897dd7cddfSDavid du Colombier 		m->matched = searchMsg(m, rock.next);
10907dd7cddfSDavid du Colombier 	for(m = selected->msgs; m != nil; m = m->next){
10917dd7cddfSDavid du Colombier 		if(m->matched){
10927dd7cddfSDavid du Colombier 			if(uids)
10937dd7cddfSDavid du Colombier 				id = m->uid;
10947dd7cddfSDavid du Colombier 			else
10957dd7cddfSDavid du Colombier 				id = m->seq;
10967dd7cddfSDavid du Colombier 			Bprint(&bout, " %lud", id);
10977dd7cddfSDavid du Colombier 		}
10987dd7cddfSDavid du Colombier 	}
10997dd7cddfSDavid du Colombier 	Bprint(&bout, "\r\n");
11007dd7cddfSDavid du Colombier 	checkBox(selected, 0);
11017dd7cddfSDavid du Colombier 	status(uids, uids);
1102d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
11037dd7cddfSDavid du Colombier }
11047dd7cddfSDavid du Colombier 
11057dd7cddfSDavid du Colombier static void
selectCmd(char * tg,char * cmd)11067dd7cddfSDavid du Colombier selectCmd(char *tg, char *cmd)
11077dd7cddfSDavid du Colombier {
11087dd7cddfSDavid du Colombier 	Msg *m;
11097dd7cddfSDavid du Colombier 	char *s, *mbox;
11107dd7cddfSDavid du Colombier 
11117dd7cddfSDavid du Colombier 	mustBe(' ');
11127dd7cddfSDavid du Colombier 	mbox = astring();
11137dd7cddfSDavid du Colombier 	crnl();
11147dd7cddfSDavid du Colombier 
11157dd7cddfSDavid du Colombier 	if(selected){
11167dd7cddfSDavid du Colombier 		imapState = SAuthed;
11177dd7cddfSDavid du Colombier 		closeBox(selected, 1);
11187dd7cddfSDavid du Colombier 		selected = nil;
11197dd7cddfSDavid du Colombier 	}
11207dd7cddfSDavid du Colombier 
11217dd7cddfSDavid du Colombier 	mbox = mboxName(mbox);
11227dd7cddfSDavid du Colombier 	if(mbox == nil || !okMbox(mbox)){
1123d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
11247dd7cddfSDavid du Colombier 		return;
11257dd7cddfSDavid du Colombier 	}
11267dd7cddfSDavid du Colombier 
11277dd7cddfSDavid du Colombier 	selected = openBox(mbox, "imap", cistrcmp(cmd, "select") == 0);
11287dd7cddfSDavid du Colombier 	if(selected == nil){
1129d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s can't open mailbox %s: %r\r\n", tg, cmd, mbox);
11307dd7cddfSDavid du Colombier 		return;
11317dd7cddfSDavid du Colombier 	}
11327dd7cddfSDavid du Colombier 
11337dd7cddfSDavid du Colombier 	imapState = SSelected;
11347dd7cddfSDavid du Colombier 
1135d9306527SDavid du Colombier 	Bprint(&bout, "* FLAGS (\\Seen \\Answered \\Flagged \\Deleted \\Draft)\r\n");
113640fe8d0dSDavid du Colombier 	Bprint(&bout, "* %lud EXISTS\r\n", selected->max);
11377dd7cddfSDavid du Colombier 	selected->toldMax = selected->max;
113840fe8d0dSDavid du Colombier 	Bprint(&bout, "* %lud RECENT\r\n", selected->recent);
11397dd7cddfSDavid du Colombier 	selected->toldRecent = selected->recent;
11407dd7cddfSDavid du Colombier 	for(m = selected->msgs; m != nil; m = m->next){
11417dd7cddfSDavid du Colombier 		if(!m->expunged && (m->flags & MSeen) != MSeen){
1142d9306527SDavid du Colombier 			Bprint(&bout, "* OK [UNSEEN %ld]\r\n", m->seq);
11437dd7cddfSDavid du Colombier 			break;
11447dd7cddfSDavid du Colombier 		}
11457dd7cddfSDavid du Colombier 	}
11466b6b9ac8SDavid du Colombier 	Bprint(&bout, "* OK [PERMANENTFLAGS (\\Seen \\Answered \\Flagged \\Draft \\Deleted)]\r\n");
1147d9306527SDavid du Colombier 	Bprint(&bout, "* OK [UIDNEXT %ld]\r\n", selected->uidnext);
1148d9306527SDavid du Colombier 	Bprint(&bout, "* OK [UIDVALIDITY %ld]\r\n", selected->uidvalidity);
11497dd7cddfSDavid du Colombier 	s = "READ-ONLY";
11507dd7cddfSDavid du Colombier 	if(selected->writable)
11517dd7cddfSDavid du Colombier 		s = "READ-WRITE";
1152d9306527SDavid du Colombier 	Bprint(&bout, "%s OK [%s] %s %s completed\r\n", tg, s, cmd, mbox);
11537dd7cddfSDavid du Colombier }
11547dd7cddfSDavid du Colombier 
11557dd7cddfSDavid du Colombier static NamedInt	statusItems[] =
11567dd7cddfSDavid du Colombier {
11577dd7cddfSDavid du Colombier 	{"MESSAGES",	SMessages},
11587dd7cddfSDavid du Colombier 	{"RECENT",	SRecent},
11597dd7cddfSDavid du Colombier 	{"UIDNEXT",	SUidNext},
11607dd7cddfSDavid du Colombier 	{"UIDVALIDITY",	SUidValidity},
11617dd7cddfSDavid du Colombier 	{"UNSEEN",	SUnseen},
11627dd7cddfSDavid du Colombier 	{nil,		0}
11637dd7cddfSDavid du Colombier };
11647dd7cddfSDavid du Colombier 
11657dd7cddfSDavid du Colombier static void
statusCmd(char * tg,char * cmd)11667dd7cddfSDavid du Colombier statusCmd(char *tg, char *cmd)
11677dd7cddfSDavid du Colombier {
11687dd7cddfSDavid du Colombier 	Box *box;
11697dd7cddfSDavid du Colombier 	Msg *m;
11707dd7cddfSDavid du Colombier 	char *s, *mbox;
11717dd7cddfSDavid du Colombier 	ulong v;
11727dd7cddfSDavid du Colombier 	int si, i;
11737dd7cddfSDavid du Colombier 
11747dd7cddfSDavid du Colombier 	mustBe(' ');
11757dd7cddfSDavid du Colombier 	mbox = astring();
11767dd7cddfSDavid du Colombier 	mustBe(' ');
11777dd7cddfSDavid du Colombier 	mustBe('(');
11787dd7cddfSDavid du Colombier 	si = 0;
11797dd7cddfSDavid du Colombier 	for(;;){
11807dd7cddfSDavid du Colombier 		s = atom();
11817dd7cddfSDavid du Colombier 		i = mapInt(statusItems, s);
11827dd7cddfSDavid du Colombier 		if(i == 0)
11837dd7cddfSDavid du Colombier 			parseErr("illegal status item");
11847dd7cddfSDavid du Colombier 		si |= i;
11857dd7cddfSDavid du Colombier 		if(peekc() == ')')
11867dd7cddfSDavid du Colombier 			break;
11877dd7cddfSDavid du Colombier 		mustBe(' ');
11887dd7cddfSDavid du Colombier 	}
11897dd7cddfSDavid du Colombier 	mustBe(')');
11907dd7cddfSDavid du Colombier 	crnl();
11917dd7cddfSDavid du Colombier 
11927dd7cddfSDavid du Colombier 	mbox = mboxName(mbox);
11937dd7cddfSDavid du Colombier 	if(mbox == nil || !okMbox(mbox)){
11947dd7cddfSDavid du Colombier 		check();
1195d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
11967dd7cddfSDavid du Colombier 		return;
11977dd7cddfSDavid du Colombier 	}
11987dd7cddfSDavid du Colombier 
11997dd7cddfSDavid du Colombier 	box = openBox(mbox, "status", 1);
12007dd7cddfSDavid du Colombier 	if(box == nil){
12017dd7cddfSDavid du Colombier 		check();
1202d9306527SDavid du Colombier 		Bprint(&bout, "%s NO [TRYCREATE] %s can't open mailbox %s: %r\r\n", tg, cmd, mbox);
12037dd7cddfSDavid du Colombier 		return;
12047dd7cddfSDavid du Colombier 	}
12057dd7cddfSDavid du Colombier 
12068cf6001eSDavid du Colombier 	Bprint(&bout, "* STATUS %s (", mbox);
12077dd7cddfSDavid du Colombier 	s = "";
12087dd7cddfSDavid du Colombier 	for(i = 0; statusItems[i].name != nil; i++){
12097dd7cddfSDavid du Colombier 		if(si & statusItems[i].v){
12107dd7cddfSDavid du Colombier 			v = 0;
12117dd7cddfSDavid du Colombier 			switch(statusItems[i].v){
12127dd7cddfSDavid du Colombier 			case SMessages:
12137dd7cddfSDavid du Colombier 				v = box->max;
12147dd7cddfSDavid du Colombier 				break;
12157dd7cddfSDavid du Colombier 			case SRecent:
12167dd7cddfSDavid du Colombier 				v = box->recent;
12177dd7cddfSDavid du Colombier 				break;
12187dd7cddfSDavid du Colombier 			case SUidNext:
12197dd7cddfSDavid du Colombier 				v = box->uidnext;
12207dd7cddfSDavid du Colombier 				break;
12217dd7cddfSDavid du Colombier 			case SUidValidity:
12227dd7cddfSDavid du Colombier 				v = box->uidvalidity;
12237dd7cddfSDavid du Colombier 				break;
12247dd7cddfSDavid du Colombier 			case SUnseen:
12257dd7cddfSDavid du Colombier 				v = 0;
12267dd7cddfSDavid du Colombier 				for(m = box->msgs; m != nil; m = m->next)
12277dd7cddfSDavid du Colombier 					if((m->flags & MSeen) != MSeen)
12287dd7cddfSDavid du Colombier 						v++;
12297dd7cddfSDavid du Colombier 				break;
12307dd7cddfSDavid du Colombier 			default:
12317dd7cddfSDavid du Colombier 				Bprint(&bout, ")");
12327dd7cddfSDavid du Colombier 				bye("internal error: status item not implemented");
12337dd7cddfSDavid du Colombier 				break;
12347dd7cddfSDavid du Colombier 			}
12357dd7cddfSDavid du Colombier 			Bprint(&bout, "%s%s %lud", s, statusItems[i].name, v);
12367dd7cddfSDavid du Colombier 			s = " ";
12377dd7cddfSDavid du Colombier 		}
12387dd7cddfSDavid du Colombier 	}
12397dd7cddfSDavid du Colombier 	Bprint(&bout, ")\r\n");
12407dd7cddfSDavid du Colombier 	closeBox(box, 1);
12417dd7cddfSDavid du Colombier 
12427dd7cddfSDavid du Colombier 	check();
1243d9306527SDavid du Colombier 	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
12447dd7cddfSDavid du Colombier }
12457dd7cddfSDavid du Colombier 
12467dd7cddfSDavid du Colombier static void
storeCmd(char * tg,char * cmd)12477dd7cddfSDavid du Colombier storeCmd(char *tg, char *cmd)
12487dd7cddfSDavid du Colombier {
12497dd7cddfSDavid du Colombier 	storeUCmd(tg, cmd, 0);
12507dd7cddfSDavid du Colombier }
12517dd7cddfSDavid du Colombier 
12527dd7cddfSDavid du Colombier static void
storeUCmd(char * tg,char * cmd,int uids)12537dd7cddfSDavid du Colombier storeUCmd(char *tg, char *cmd, int uids)
12547dd7cddfSDavid du Colombier {
12557dd7cddfSDavid du Colombier 	Store *st;
12567dd7cddfSDavid du Colombier 	MsgSet *ms;
12577dd7cddfSDavid du Colombier 	MbLock *ml;
12587dd7cddfSDavid du Colombier 	char *uid;
12597dd7cddfSDavid du Colombier 	ulong max;
12607dd7cddfSDavid du Colombier 	int ok;
12617dd7cddfSDavid du Colombier 
12627dd7cddfSDavid du Colombier 	mustBe(' ');
1263becaf2abSDavid du Colombier 	ms = msgSet(uids);
12647dd7cddfSDavid du Colombier 	mustBe(' ');
12657dd7cddfSDavid du Colombier 	st = storeWhat();
12667dd7cddfSDavid du Colombier 	crnl();
12677dd7cddfSDavid du Colombier 	uid = "";
12687dd7cddfSDavid du Colombier 	if(uids)
12697dd7cddfSDavid du Colombier 		uid = "uid ";
12707dd7cddfSDavid du Colombier 	max = selected->max;
12717dd7cddfSDavid du Colombier 	ml = checkBox(selected, 1);
12727dd7cddfSDavid du Colombier 	ok = ml != nil && forMsgs(selected, ms, max, uids, storeMsg, st);
12737dd7cddfSDavid du Colombier 	closeImp(selected, ml);
12747dd7cddfSDavid du Colombier 	status(uids, uids);
12757dd7cddfSDavid du Colombier 	if(ok)
1276d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
12777dd7cddfSDavid du Colombier 	else
1278d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
12797dd7cddfSDavid du Colombier }
12807dd7cddfSDavid du Colombier 
12817dd7cddfSDavid du Colombier /*
12827dd7cddfSDavid du Colombier  * minimal implementation of subscribe
12837dd7cddfSDavid du Colombier  * all folders are automatically subscribed,
12847dd7cddfSDavid du Colombier  * and can't be unsubscribed
12857dd7cddfSDavid du Colombier  */
12867dd7cddfSDavid du Colombier static void
subscribeCmd(char * tg,char * cmd)12877dd7cddfSDavid du Colombier subscribeCmd(char *tg, char *cmd)
12887dd7cddfSDavid du Colombier {
12897dd7cddfSDavid du Colombier 	Box *box;
12907dd7cddfSDavid du Colombier 	char *mbox;
12917dd7cddfSDavid du Colombier 	int ok;
12927dd7cddfSDavid du Colombier 
12937dd7cddfSDavid du Colombier 	mustBe(' ');
12947dd7cddfSDavid du Colombier 	mbox = astring();
12957dd7cddfSDavid du Colombier 	crnl();
12967dd7cddfSDavid du Colombier 	check();
12977dd7cddfSDavid du Colombier 	mbox = mboxName(mbox);
12987dd7cddfSDavid du Colombier 	ok = 0;
12997dd7cddfSDavid du Colombier 	if(mbox != nil && okMbox(mbox)){
13007dd7cddfSDavid du Colombier 		box = openBox(mbox, "subscribe", 0);
13017dd7cddfSDavid du Colombier 		if(box != nil){
13027dd7cddfSDavid du Colombier 			ok = subscribe(mbox, 's');
13037dd7cddfSDavid du Colombier 			closeBox(box, 1);
13047dd7cddfSDavid du Colombier 		}
13057dd7cddfSDavid du Colombier 	}
13067dd7cddfSDavid du Colombier 	if(!ok)
1307d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
13087dd7cddfSDavid du Colombier 	else
1309d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
13107dd7cddfSDavid du Colombier }
13117dd7cddfSDavid du Colombier 
13127dd7cddfSDavid du Colombier static void
uidCmd(char * tg,char * cmd)13137dd7cddfSDavid du Colombier uidCmd(char *tg, char *cmd)
13147dd7cddfSDavid du Colombier {
13157dd7cddfSDavid du Colombier 	char *sub;
13167dd7cddfSDavid du Colombier 
13177dd7cddfSDavid du Colombier 	mustBe(' ');
13187dd7cddfSDavid du Colombier 	sub = atom();
13197dd7cddfSDavid du Colombier 	if(cistrcmp(sub, "copy") == 0)
13207dd7cddfSDavid du Colombier 		copyUCmd(tg, sub, 1);
13217dd7cddfSDavid du Colombier 	else if(cistrcmp(sub, "fetch") == 0)
13227dd7cddfSDavid du Colombier 		fetchUCmd(tg, sub, 1);
13237dd7cddfSDavid du Colombier 	else if(cistrcmp(sub, "search") == 0)
13247dd7cddfSDavid du Colombier 		searchUCmd(tg, sub, 1);
13257dd7cddfSDavid du Colombier 	else if(cistrcmp(sub, "store") == 0)
13267dd7cddfSDavid du Colombier 		storeUCmd(tg, sub, 1);
13277dd7cddfSDavid du Colombier 	else{
13287dd7cddfSDavid du Colombier 		clearcmd();
1329d9306527SDavid du Colombier 		Bprint(&bout, "%s BAD %s illegal uid command %s\r\n", tg, cmd, sub);
13307dd7cddfSDavid du Colombier 	}
13317dd7cddfSDavid du Colombier }
13327dd7cddfSDavid du Colombier 
13337dd7cddfSDavid du Colombier static void
unsubscribeCmd(char * tg,char * cmd)13347dd7cddfSDavid du Colombier unsubscribeCmd(char *tg, char *cmd)
13357dd7cddfSDavid du Colombier {
13367dd7cddfSDavid du Colombier 	char *mbox;
13377dd7cddfSDavid du Colombier 
13387dd7cddfSDavid du Colombier 	mustBe(' ');
13397dd7cddfSDavid du Colombier 	mbox = astring();
13407dd7cddfSDavid du Colombier 	crnl();
13417dd7cddfSDavid du Colombier 	check();
13427dd7cddfSDavid du Colombier 	mbox = mboxName(mbox);
13437dd7cddfSDavid du Colombier 	if(mbox == nil || !okMbox(mbox) || !subscribe(mbox, 'u'))
1344d9306527SDavid du Colombier 		Bprint(&bout, "%s NO %s can't unsubscribe\r\n", tg, cmd);
13457dd7cddfSDavid du Colombier 	else
1346d9306527SDavid du Colombier 		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
13477dd7cddfSDavid du Colombier }
13487dd7cddfSDavid du Colombier 
13497dd7cddfSDavid du Colombier static void
badsyn(void)13507dd7cddfSDavid du Colombier badsyn(void)
13517dd7cddfSDavid du Colombier {
13527dd7cddfSDavid du Colombier 	parseErr("bad syntax");
13537dd7cddfSDavid du Colombier }
13547dd7cddfSDavid du Colombier 
13557dd7cddfSDavid du Colombier static void
clearcmd(void)13567dd7cddfSDavid du Colombier clearcmd(void)
13577dd7cddfSDavid du Colombier {
13587dd7cddfSDavid du Colombier 	int c;
13597dd7cddfSDavid du Colombier 
13607dd7cddfSDavid du Colombier 	for(;;){
13617dd7cddfSDavid du Colombier 		c = getc();
13627dd7cddfSDavid du Colombier 		if(c < 0)
13637dd7cddfSDavid du Colombier 			bye("end of input");
13647dd7cddfSDavid du Colombier 		if(c == '\n')
13657dd7cddfSDavid du Colombier 			return;
13667dd7cddfSDavid du Colombier 	}
13677dd7cddfSDavid du Colombier }
13687dd7cddfSDavid du Colombier 
13697dd7cddfSDavid du Colombier static void
crnl(void)13707dd7cddfSDavid du Colombier crnl(void)
13717dd7cddfSDavid du Colombier {
13727dd7cddfSDavid du Colombier 	int c;
13737dd7cddfSDavid du Colombier 
13747dd7cddfSDavid du Colombier 	c = getc();
13757dd7cddfSDavid du Colombier 	if(c == '\n')
13767dd7cddfSDavid du Colombier 		return;
13777dd7cddfSDavid du Colombier 	if(c != '\r' || getc() != '\n')
13787dd7cddfSDavid du Colombier 		badsyn();
13797dd7cddfSDavid du Colombier }
13807dd7cddfSDavid du Colombier 
13817dd7cddfSDavid du Colombier static void
mustBe(int c)13827dd7cddfSDavid du Colombier mustBe(int c)
13837dd7cddfSDavid du Colombier {
13847dd7cddfSDavid du Colombier 	if(getc() != c){
13857dd7cddfSDavid du Colombier 		ungetc();
13867dd7cddfSDavid du Colombier 		badsyn();
13877dd7cddfSDavid du Colombier 	}
13887dd7cddfSDavid du Colombier }
13897dd7cddfSDavid du Colombier 
13907dd7cddfSDavid du Colombier /*
13917dd7cddfSDavid du Colombier  * flaglist	: '(' ')' | '(' flags ')'
13927dd7cddfSDavid du Colombier  */
13937dd7cddfSDavid du Colombier static int
flagList(void)13947dd7cddfSDavid du Colombier flagList(void)
13957dd7cddfSDavid du Colombier {
13967dd7cddfSDavid du Colombier 	int f;
13977dd7cddfSDavid du Colombier 
13987dd7cddfSDavid du Colombier 	mustBe('(');
13997dd7cddfSDavid du Colombier 	f = 0;
14007dd7cddfSDavid du Colombier 	if(peekc() != ')')
14017dd7cddfSDavid du Colombier 		f = flags();
14027dd7cddfSDavid du Colombier 
14037dd7cddfSDavid du Colombier 	mustBe(')');
14047dd7cddfSDavid du Colombier 	return f;
14057dd7cddfSDavid du Colombier }
14067dd7cddfSDavid du Colombier 
14077dd7cddfSDavid du Colombier /*
14087dd7cddfSDavid du Colombier  * flags	: flag | flags ' ' flag
14097dd7cddfSDavid du Colombier  * flag		: '\' atom | atom
14107dd7cddfSDavid du Colombier  */
14117dd7cddfSDavid du Colombier static int
flags(void)14127dd7cddfSDavid du Colombier flags(void)
14137dd7cddfSDavid du Colombier {
14147dd7cddfSDavid du Colombier 	int ff, flags;
14157dd7cddfSDavid du Colombier 	char *s;
14167dd7cddfSDavid du Colombier 	int c;
14177dd7cddfSDavid du Colombier 
14187dd7cddfSDavid du Colombier 	flags = 0;
14197dd7cddfSDavid du Colombier 	for(;;){
14207dd7cddfSDavid du Colombier 		c = peekc();
14217dd7cddfSDavid du Colombier 		if(c == '\\'){
14227dd7cddfSDavid du Colombier 			mustBe('\\');
14237dd7cddfSDavid du Colombier 			s = atomString(atomStop, "\\");
14247dd7cddfSDavid du Colombier 		}else if(strchr(atomStop, c) != nil)
14257dd7cddfSDavid du Colombier 			s = atom();
14267dd7cddfSDavid du Colombier 		else
14277dd7cddfSDavid du Colombier 			break;
14287dd7cddfSDavid du Colombier 		ff = mapFlag(s);
14297dd7cddfSDavid du Colombier 		if(ff == 0)
14307dd7cddfSDavid du Colombier 			parseErr("flag not supported");
14317dd7cddfSDavid du Colombier 		flags |= ff;
14327dd7cddfSDavid du Colombier 		if(peekc() != ' ')
14337dd7cddfSDavid du Colombier 			break;
14347dd7cddfSDavid du Colombier 		mustBe(' ');
14357dd7cddfSDavid du Colombier 	}
14367dd7cddfSDavid du Colombier 	if(flags == 0)
14377dd7cddfSDavid du Colombier 		parseErr("no flags given");
14387dd7cddfSDavid du Colombier 	return flags;
14397dd7cddfSDavid du Colombier }
14407dd7cddfSDavid du Colombier 
14417dd7cddfSDavid du Colombier /*
14427dd7cddfSDavid du Colombier  * storeWhat	: osign 'FLAGS' ' ' storeflags
14437dd7cddfSDavid du Colombier  *		| osign 'FLAGS.SILENT' ' ' storeflags
14447dd7cddfSDavid du Colombier  * osign	:
14457dd7cddfSDavid du Colombier  *		| '+' | '-'
14467dd7cddfSDavid du Colombier  * storeflags	: flagList | flags
14477dd7cddfSDavid du Colombier  */
14487dd7cddfSDavid du Colombier static Store*
storeWhat(void)14497dd7cddfSDavid du Colombier storeWhat(void)
14507dd7cddfSDavid du Colombier {
14517dd7cddfSDavid du Colombier 	int f;
14527dd7cddfSDavid du Colombier 	char *s;
14537dd7cddfSDavid du Colombier 	int c, w;
14547dd7cddfSDavid du Colombier 
14557dd7cddfSDavid du Colombier 	c = peekc();
14567dd7cddfSDavid du Colombier 	if(c == '+' || c == '-')
14577dd7cddfSDavid du Colombier 		mustBe(c);
14587dd7cddfSDavid du Colombier 	else
14597dd7cddfSDavid du Colombier 		c = 0;
14607dd7cddfSDavid du Colombier 	s = atom();
14617dd7cddfSDavid du Colombier 	w = 0;
14627dd7cddfSDavid du Colombier 	if(cistrcmp(s, "flags") == 0)
14637dd7cddfSDavid du Colombier 		w = STFlags;
14647dd7cddfSDavid du Colombier 	else if(cistrcmp(s, "flags.silent") == 0)
14657dd7cddfSDavid du Colombier 		w = STFlagsSilent;
14667dd7cddfSDavid du Colombier 	else
14677dd7cddfSDavid du Colombier 		parseErr("illegal store attribute");
14687dd7cddfSDavid du Colombier 	mustBe(' ');
14697dd7cddfSDavid du Colombier 	if(peekc() == '(')
14707dd7cddfSDavid du Colombier 		f = flagList();
14717dd7cddfSDavid du Colombier 	else
14727dd7cddfSDavid du Colombier 		f = flags();
14737dd7cddfSDavid du Colombier 	return mkStore(c, w, f);
14747dd7cddfSDavid du Colombier }
14757dd7cddfSDavid du Colombier 
14767dd7cddfSDavid du Colombier /*
14777dd7cddfSDavid du Colombier  * fetchWhat	: "ALL" | "FULL" | "FAST" | fetchAtt | '(' fetchAtts ')'
14787dd7cddfSDavid du Colombier  * fetchAtts	: fetchAtt | fetchAtts ' ' fetchAtt
14797dd7cddfSDavid du Colombier  */
14807dd7cddfSDavid du Colombier static char *fetchAtom	= "(){}%*\"\\[]";
14817dd7cddfSDavid du Colombier static Fetch*
fetchWhat(void)14827dd7cddfSDavid du Colombier fetchWhat(void)
14837dd7cddfSDavid du Colombier {
14847dd7cddfSDavid du Colombier 	Fetch *f;
14857dd7cddfSDavid du Colombier 	char *s;
14867dd7cddfSDavid du Colombier 
14877dd7cddfSDavid du Colombier 	if(peekc() == '('){
14887dd7cddfSDavid du Colombier 		getc();
14897dd7cddfSDavid du Colombier 		f = nil;
14907dd7cddfSDavid du Colombier 		for(;;){
14917dd7cddfSDavid du Colombier 			s = atomString(fetchAtom, "");
14927dd7cddfSDavid du Colombier 			f = fetchAtt(s, f);
14937dd7cddfSDavid du Colombier 			if(peekc() == ')')
14947dd7cddfSDavid du Colombier 				break;
14957dd7cddfSDavid du Colombier 			mustBe(' ');
14967dd7cddfSDavid du Colombier 		}
14977dd7cddfSDavid du Colombier 		getc();
14987dd7cddfSDavid du Colombier 		return revFetch(f);
14997dd7cddfSDavid du Colombier 	}
15007dd7cddfSDavid du Colombier 
15017dd7cddfSDavid du Colombier 	s = atomString(fetchAtom, "");
15027dd7cddfSDavid du Colombier 	if(cistrcmp(s, "all") == 0)
15037dd7cddfSDavid du Colombier 		f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, nil))));
15047dd7cddfSDavid du Colombier 	else if(cistrcmp(s, "fast") == 0)
15057dd7cddfSDavid du Colombier 		f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, nil)));
15067dd7cddfSDavid du Colombier 	else if(cistrcmp(s, "full") == 0)
15077dd7cddfSDavid du Colombier 		f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, mkFetch(FBody, nil)))));
15087dd7cddfSDavid du Colombier 	else
15097dd7cddfSDavid du Colombier 		f = fetchAtt(s, nil);
15107dd7cddfSDavid du Colombier 	return f;
15117dd7cddfSDavid du Colombier }
15127dd7cddfSDavid du Colombier 
15137dd7cddfSDavid du Colombier /*
15147dd7cddfSDavid du Colombier  * fetchAtt	: "ENVELOPE" | "FLAGS" | "INTERNALDATE"
15157dd7cddfSDavid du Colombier  *		| "RFC822" | "RFC822.HEADER" | "RFC822.SIZE" | "RFC822.TEXT"
15167dd7cddfSDavid du Colombier  *		| "BODYSTRUCTURE"
15177dd7cddfSDavid du Colombier  *		| "UID"
15187dd7cddfSDavid du Colombier  *		| "BODY"
15197dd7cddfSDavid du Colombier  *		| "BODY" bodysubs
15207dd7cddfSDavid du Colombier  *		| "BODY.PEEK" bodysubs
15217dd7cddfSDavid du Colombier  * bodysubs	: sect
15227dd7cddfSDavid du Colombier  *		| sect '<' number '.' nz-number '>'
15237dd7cddfSDavid du Colombier  * sect		: '[' sectSpec ']'
15247dd7cddfSDavid du Colombier  * sectSpec	: sectMsgText
15257dd7cddfSDavid du Colombier  *		| sectPart
15267dd7cddfSDavid du Colombier  *		| sectPart '.' sectText
15277dd7cddfSDavid du Colombier  * sectPart	: nz-number
15287dd7cddfSDavid du Colombier  *		| sectPart '.' nz-number
15297dd7cddfSDavid du Colombier  */
15307dd7cddfSDavid du Colombier static Fetch*
fetchAtt(char * s,Fetch * f)15317dd7cddfSDavid du Colombier fetchAtt(char *s, Fetch *f)
15327dd7cddfSDavid du Colombier {
15337dd7cddfSDavid du Colombier 	NList *sect;
15347dd7cddfSDavid du Colombier 	int c;
15357dd7cddfSDavid du Colombier 
15367dd7cddfSDavid du Colombier 	if(cistrcmp(s, "envelope") == 0)
15377dd7cddfSDavid du Colombier 		return mkFetch(FEnvelope, f);
15387dd7cddfSDavid du Colombier 	if(cistrcmp(s, "flags") == 0)
15397dd7cddfSDavid du Colombier 		return mkFetch(FFlags, f);
15407dd7cddfSDavid du Colombier 	if(cistrcmp(s, "internaldate") == 0)
15417dd7cddfSDavid du Colombier 		return mkFetch(FInternalDate, f);
1542d9306527SDavid du Colombier 	if(cistrcmp(s, "RFC822") == 0)
15437dd7cddfSDavid du Colombier 		return mkFetch(FRfc822, f);
1544d9306527SDavid du Colombier 	if(cistrcmp(s, "RFC822.header") == 0)
15457dd7cddfSDavid du Colombier 		return mkFetch(FRfc822Head, f);
1546d9306527SDavid du Colombier 	if(cistrcmp(s, "RFC822.size") == 0)
15477dd7cddfSDavid du Colombier 		return mkFetch(FRfc822Size, f);
1548d9306527SDavid du Colombier 	if(cistrcmp(s, "RFC822.text") == 0)
15497dd7cddfSDavid du Colombier 		return mkFetch(FRfc822Text, f);
15507dd7cddfSDavid du Colombier 	if(cistrcmp(s, "bodystructure") == 0)
15517dd7cddfSDavid du Colombier 		return mkFetch(FBodyStruct, f);
15527dd7cddfSDavid du Colombier 	if(cistrcmp(s, "uid") == 0)
15537dd7cddfSDavid du Colombier 		return mkFetch(FUid, f);
15547dd7cddfSDavid du Colombier 
15557dd7cddfSDavid du Colombier 	if(cistrcmp(s, "body") == 0){
15567dd7cddfSDavid du Colombier 		if(peekc() != '[')
15577dd7cddfSDavid du Colombier 			return mkFetch(FBody, f);
15587dd7cddfSDavid du Colombier 		f = mkFetch(FBodySect, f);
15597dd7cddfSDavid du Colombier 	}else if(cistrcmp(s, "body.peek") == 0)
15607dd7cddfSDavid du Colombier 		f = mkFetch(FBodyPeek, f);
15617dd7cddfSDavid du Colombier 	else
15627dd7cddfSDavid du Colombier 		parseErr("illegal fetch attribute");
15637dd7cddfSDavid du Colombier 
15647dd7cddfSDavid du Colombier 	mustBe('[');
15657dd7cddfSDavid du Colombier 	c = peekc();
15667dd7cddfSDavid du Colombier 	if(c >= '1' && c <= '9'){
15677dd7cddfSDavid du Colombier 		sect = mkNList(number(1), nil);
15687dd7cddfSDavid du Colombier 		while(peekc() == '.'){
15697dd7cddfSDavid du Colombier 			getc();
15707dd7cddfSDavid du Colombier 			c = peekc();
15717dd7cddfSDavid du Colombier 			if(c >= '1' && c <= '9'){
15727dd7cddfSDavid du Colombier 				sect = mkNList(number(1), sect);
15737dd7cddfSDavid du Colombier 			}else{
15747dd7cddfSDavid du Colombier 				break;
15757dd7cddfSDavid du Colombier 			}
15767dd7cddfSDavid du Colombier 		}
15777dd7cddfSDavid du Colombier 		f->sect = revNList(sect);
15787dd7cddfSDavid du Colombier 	}
15797dd7cddfSDavid du Colombier 	if(peekc() != ']')
15807dd7cddfSDavid du Colombier 		sectText(f, f->sect != nil);
15817dd7cddfSDavid du Colombier 	mustBe(']');
15827dd7cddfSDavid du Colombier 
15837dd7cddfSDavid du Colombier 	if(peekc() != '<')
15847dd7cddfSDavid du Colombier 		return f;
15857dd7cddfSDavid du Colombier 
15867dd7cddfSDavid du Colombier 	f->partial = 1;
15877dd7cddfSDavid du Colombier 	mustBe('<');
15887dd7cddfSDavid du Colombier 	f->start = number(0);
15897dd7cddfSDavid du Colombier 	mustBe('.');
15907dd7cddfSDavid du Colombier 	f->size = number(1);
15917dd7cddfSDavid du Colombier 	mustBe('>');
15927dd7cddfSDavid du Colombier 	return f;
15937dd7cddfSDavid du Colombier }
15947dd7cddfSDavid du Colombier 
15957dd7cddfSDavid du Colombier /*
15967dd7cddfSDavid du Colombier  * sectText	: sectMsgText | "MIME"
15977dd7cddfSDavid du Colombier  * sectMsgText	: "HEADER"
15987dd7cddfSDavid du Colombier  *		| "TEXT"
15997dd7cddfSDavid du Colombier  *		| "HEADER.FIELDS" ' ' hdrList
16007dd7cddfSDavid du Colombier  *		| "HEADER.FIELDS.NOT" ' ' hdrList
16017dd7cddfSDavid du Colombier  * hdrList	: '(' hdrs ')'
16027dd7cddfSDavid du Colombier  * hdrs:	: astring
16037dd7cddfSDavid du Colombier  *		| hdrs ' ' astring
16047dd7cddfSDavid du Colombier  */
16057dd7cddfSDavid du Colombier static void
sectText(Fetch * f,int mimeOk)16067dd7cddfSDavid du Colombier sectText(Fetch *f, int mimeOk)
16077dd7cddfSDavid du Colombier {
16087dd7cddfSDavid du Colombier 	SList *h;
16097dd7cddfSDavid du Colombier 	char *s;
16107dd7cddfSDavid du Colombier 
16117dd7cddfSDavid du Colombier 	s = atomString(fetchAtom, "");
16127dd7cddfSDavid du Colombier 	if(cistrcmp(s, "header") == 0){
16137dd7cddfSDavid du Colombier 		f->part = FPHead;
16147dd7cddfSDavid du Colombier 		return;
16157dd7cddfSDavid du Colombier 	}
16167dd7cddfSDavid du Colombier 	if(cistrcmp(s, "text") == 0){
16177dd7cddfSDavid du Colombier 		f->part = FPText;
16187dd7cddfSDavid du Colombier 		return;
16197dd7cddfSDavid du Colombier 	}
16207dd7cddfSDavid du Colombier 	if(mimeOk && cistrcmp(s, "mime") == 0){
16217dd7cddfSDavid du Colombier 		f->part = FPMime;
16227dd7cddfSDavid du Colombier 		return;
16237dd7cddfSDavid du Colombier 	}
16247dd7cddfSDavid du Colombier 	if(cistrcmp(s, "header.fields") == 0)
16257dd7cddfSDavid du Colombier 		f->part = FPHeadFields;
16267dd7cddfSDavid du Colombier 	else if(cistrcmp(s, "header.fields.not") == 0)
16277dd7cddfSDavid du Colombier 		f->part = FPHeadFieldsNot;
16287dd7cddfSDavid du Colombier 	else
16297dd7cddfSDavid du Colombier 		parseErr("illegal fetch section text");
16307dd7cddfSDavid du Colombier 	mustBe(' ');
16317dd7cddfSDavid du Colombier 	mustBe('(');
16327dd7cddfSDavid du Colombier 	h = nil;
16337dd7cddfSDavid du Colombier 	for(;;){
16347dd7cddfSDavid du Colombier 		h = mkSList(astring(), h);
16357dd7cddfSDavid du Colombier 		if(peekc() == ')')
16367dd7cddfSDavid du Colombier 			break;
16377dd7cddfSDavid du Colombier 		mustBe(' ');
16387dd7cddfSDavid du Colombier 	}
16397dd7cddfSDavid du Colombier 	mustBe(')');
16407dd7cddfSDavid du Colombier 	f->hdrs = revSList(h);
16417dd7cddfSDavid du Colombier }
16427dd7cddfSDavid du Colombier 
16437dd7cddfSDavid du Colombier /*
16447dd7cddfSDavid du Colombier  * searchWhat	: "CHARSET" ' ' astring searchkeys | searchkeys
16457dd7cddfSDavid du Colombier  * searchkeys	: searchkey | searchkeys ' ' searchkey
16467dd7cddfSDavid du Colombier  * searchkey	: "ALL" | "ANSWERED" | "DELETED" | "FLAGGED" | "NEW" | "OLD" | "RECENT"
16477dd7cddfSDavid du Colombier  *		| "SEEN" | "UNANSWERED" | "UNDELETED" | "UNFLAGGED" | "DRAFT" | "UNDRAFT"
16487dd7cddfSDavid du Colombier  *		| astrkey ' ' astring
16497dd7cddfSDavid du Colombier  *		| datekey ' ' date
16507dd7cddfSDavid du Colombier  *		| "KEYWORD" ' ' flag | "UNKEYWORD" flag
16517dd7cddfSDavid du Colombier  *		| "LARGER" ' ' number | "SMALLER" ' ' number
16527dd7cddfSDavid du Colombier  * 		| "HEADER" astring ' ' astring
16537dd7cddfSDavid du Colombier  *		| set | "UID" ' ' set
16547dd7cddfSDavid du Colombier  *		| "NOT" ' ' searchkey
16557dd7cddfSDavid du Colombier  *		| "OR" ' ' searchkey ' ' searchkey
16567dd7cddfSDavid du Colombier  *		| '(' searchkeys ')'
16577dd7cddfSDavid du Colombier  * astrkey	: "BCC" | "BODY" | "CC" | "FROM" | "SUBJECT" | "TEXT" | "TO"
16587dd7cddfSDavid du Colombier  * datekey	: "BEFORE" | "ON" | "SINCE" | "SENTBEFORE" | "SENTON" | "SENTSINCE"
16597dd7cddfSDavid du Colombier  */
16607dd7cddfSDavid du Colombier static NamedInt searchMap[] =
16617dd7cddfSDavid du Colombier {
16627dd7cddfSDavid du Colombier 	{"ALL",		SKAll},
16637dd7cddfSDavid du Colombier 	{"ANSWERED",	SKAnswered},
16647dd7cddfSDavid du Colombier 	{"DELETED",	SKDeleted},
16657dd7cddfSDavid du Colombier 	{"FLAGGED",	SKFlagged},
16667dd7cddfSDavid du Colombier 	{"NEW",		SKNew},
16677dd7cddfSDavid du Colombier 	{"OLD",		SKOld},
16687dd7cddfSDavid du Colombier 	{"RECENT",	SKRecent},
16697dd7cddfSDavid du Colombier 	{"SEEN",	SKSeen},
16707dd7cddfSDavid du Colombier 	{"UNANSWERED",	SKUnanswered},
16717dd7cddfSDavid du Colombier 	{"UNDELETED",	SKUndeleted},
16727dd7cddfSDavid du Colombier 	{"UNFLAGGED",	SKUnflagged},
16737dd7cddfSDavid du Colombier 	{"DRAFT",	SKDraft},
16747dd7cddfSDavid du Colombier 	{"UNDRAFT",	SKUndraft},
16757dd7cddfSDavid du Colombier 	{"UNSEEN",	SKUnseen},
16767dd7cddfSDavid du Colombier 	{nil,		0}
16777dd7cddfSDavid du Colombier };
16787dd7cddfSDavid du Colombier 
16797dd7cddfSDavid du Colombier static NamedInt searchMapStr[] =
16807dd7cddfSDavid du Colombier {
16817dd7cddfSDavid du Colombier 	{"CHARSET",	SKCharset},
16827dd7cddfSDavid du Colombier 	{"BCC",		SKBcc},
16837dd7cddfSDavid du Colombier 	{"BODY",	SKBody},
16847dd7cddfSDavid du Colombier 	{"CC",		SKCc},
16857dd7cddfSDavid du Colombier 	{"FROM",	SKFrom},
16867dd7cddfSDavid du Colombier 	{"SUBJECT",	SKSubject},
16877dd7cddfSDavid du Colombier 	{"TEXT",	SKText},
16887dd7cddfSDavid du Colombier 	{"TO",		SKTo},
16897dd7cddfSDavid du Colombier 	{nil,		0}
16907dd7cddfSDavid du Colombier };
16917dd7cddfSDavid du Colombier 
16927dd7cddfSDavid du Colombier static NamedInt searchMapDate[] =
16937dd7cddfSDavid du Colombier {
16947dd7cddfSDavid du Colombier 	{"BEFORE",	SKBefore},
16957dd7cddfSDavid du Colombier 	{"ON",		SKOn},
16967dd7cddfSDavid du Colombier 	{"SINCE",	SKSince},
16977dd7cddfSDavid du Colombier 	{"SENTBEFORE",	SKSentBefore},
16987dd7cddfSDavid du Colombier 	{"SENTON",	SKSentOn},
16997dd7cddfSDavid du Colombier 	{"SENTSINCE",	SKSentSince},
17007dd7cddfSDavid du Colombier 	{nil,		0}
17017dd7cddfSDavid du Colombier };
17027dd7cddfSDavid du Colombier 
17037dd7cddfSDavid du Colombier static NamedInt searchMapFlag[] =
17047dd7cddfSDavid du Colombier {
17057dd7cddfSDavid du Colombier 	{"KEYWORD",	SKKeyword},
17067dd7cddfSDavid du Colombier 	{"UNKEYWORD",	SKUnkeyword},
17077dd7cddfSDavid du Colombier 	{nil,		0}
17087dd7cddfSDavid du Colombier };
17097dd7cddfSDavid du Colombier 
17107dd7cddfSDavid du Colombier static NamedInt searchMapNum[] =
17117dd7cddfSDavid du Colombier {
17127dd7cddfSDavid du Colombier 	{"SMALLER",	SKSmaller},
17137dd7cddfSDavid du Colombier 	{"LARGER",	SKLarger},
17147dd7cddfSDavid du Colombier 	{nil,		0}
17157dd7cddfSDavid du Colombier };
17167dd7cddfSDavid du Colombier 
17177dd7cddfSDavid du Colombier static Search*
searchKeys(int first,Search * tail)17187dd7cddfSDavid du Colombier searchKeys(int first, Search *tail)
17197dd7cddfSDavid du Colombier {
17207dd7cddfSDavid du Colombier 	Search *s;
17217dd7cddfSDavid du Colombier 
17227dd7cddfSDavid du Colombier 	for(;;){
17237dd7cddfSDavid du Colombier 		if(peekc() == '('){
17247dd7cddfSDavid du Colombier 			getc();
17257dd7cddfSDavid du Colombier 			tail = searchKeys(0, tail);
17267dd7cddfSDavid du Colombier 			mustBe(')');
17277dd7cddfSDavid du Colombier 		}else{
17287dd7cddfSDavid du Colombier 			s = searchKey(first);
17297dd7cddfSDavid du Colombier 			tail->next = s;
17307dd7cddfSDavid du Colombier 			tail = s;
17317dd7cddfSDavid du Colombier 		}
17327dd7cddfSDavid du Colombier 		first = 0;
17337dd7cddfSDavid du Colombier 		if(peekc() != ' ')
17347dd7cddfSDavid du Colombier 			break;
17357dd7cddfSDavid du Colombier 		getc();
17367dd7cddfSDavid du Colombier 	}
17377dd7cddfSDavid du Colombier 	return tail;
17387dd7cddfSDavid du Colombier }
17397dd7cddfSDavid du Colombier 
17407dd7cddfSDavid du Colombier static Search*
searchKey(int first)17417dd7cddfSDavid du Colombier searchKey(int first)
17427dd7cddfSDavid du Colombier {
17437dd7cddfSDavid du Colombier 	Search *sr, rock;
17447dd7cddfSDavid du Colombier 	Tm tm;
17457dd7cddfSDavid du Colombier 	char *a;
17467dd7cddfSDavid du Colombier 	int i, c;
17477dd7cddfSDavid du Colombier 
174880ee5cbfSDavid du Colombier 	sr = binalloc(&parseBin, sizeof(Search), 1);
17497dd7cddfSDavid du Colombier 	if(sr == nil)
17507dd7cddfSDavid du Colombier 		parseErr("out of memory");
17517dd7cddfSDavid du Colombier 
17527dd7cddfSDavid du Colombier 	c = peekc();
17537dd7cddfSDavid du Colombier 	if(c >= '0' && c <= '9'){
17547dd7cddfSDavid du Colombier 		sr->key = SKSet;
1755becaf2abSDavid du Colombier 		sr->set = msgSet(0);
17567dd7cddfSDavid du Colombier 		return sr;
17577dd7cddfSDavid du Colombier 	}
17587dd7cddfSDavid du Colombier 
17597dd7cddfSDavid du Colombier 	a = atom();
17607dd7cddfSDavid du Colombier 	if(i = mapInt(searchMap, a))
17617dd7cddfSDavid du Colombier 		sr->key = i;
17627dd7cddfSDavid du Colombier 	else if(i = mapInt(searchMapStr, a)){
17637dd7cddfSDavid du Colombier 		if(!first && i == SKCharset)
17647dd7cddfSDavid du Colombier 			parseErr("illegal search key");
17657dd7cddfSDavid du Colombier 		sr->key = i;
17667dd7cddfSDavid du Colombier 		mustBe(' ');
17677dd7cddfSDavid du Colombier 		sr->s = astring();
17687dd7cddfSDavid du Colombier 	}else if(i = mapInt(searchMapDate, a)){
17697dd7cddfSDavid du Colombier 		sr->key = i;
17707dd7cddfSDavid du Colombier 		mustBe(' ');
17717dd7cddfSDavid du Colombier 		c = peekc();
17727dd7cddfSDavid du Colombier 		if(c == '"')
17737dd7cddfSDavid du Colombier 			getc();
17747dd7cddfSDavid du Colombier 		a = atom();
17757dd7cddfSDavid du Colombier 		if(!imap4Date(&tm, a))
17767dd7cddfSDavid du Colombier 			parseErr("bad date format");
17777dd7cddfSDavid du Colombier 		sr->year = tm.year;
17787dd7cddfSDavid du Colombier 		sr->mon = tm.mon;
17797dd7cddfSDavid du Colombier 		sr->mday = tm.mday;
17807dd7cddfSDavid du Colombier 		if(c == '"')
17817dd7cddfSDavid du Colombier 			mustBe('"');
17827dd7cddfSDavid du Colombier 	}else if(i = mapInt(searchMapFlag, a)){
17837dd7cddfSDavid du Colombier 		sr->key = i;
17847dd7cddfSDavid du Colombier 		mustBe(' ');
17857dd7cddfSDavid du Colombier 		c = peekc();
17867dd7cddfSDavid du Colombier 		if(c == '\\'){
17877dd7cddfSDavid du Colombier 			mustBe('\\');
17887dd7cddfSDavid du Colombier 			a = atomString(atomStop, "\\");
17897dd7cddfSDavid du Colombier 		}else
17907dd7cddfSDavid du Colombier 			a = atom();
17917dd7cddfSDavid du Colombier 		i = mapFlag(a);
17927dd7cddfSDavid du Colombier 		if(i == 0)
17937dd7cddfSDavid du Colombier 			parseErr("flag not supported");
17947dd7cddfSDavid du Colombier 		sr->num = i;
17957dd7cddfSDavid du Colombier 	}else if(i = mapInt(searchMapNum, a)){
17967dd7cddfSDavid du Colombier 		sr->key = i;
17977dd7cddfSDavid du Colombier 		mustBe(' ');
17987dd7cddfSDavid du Colombier 		sr->num = number(0);
17997dd7cddfSDavid du Colombier 	}else if(cistrcmp(a, "HEADER") == 0){
18007dd7cddfSDavid du Colombier 		sr->key = SKHeader;
18017dd7cddfSDavid du Colombier 		mustBe(' ');
18027dd7cddfSDavid du Colombier 		sr->hdr = astring();
18037dd7cddfSDavid du Colombier 		mustBe(' ');
18047dd7cddfSDavid du Colombier 		sr->s = astring();
18057dd7cddfSDavid du Colombier 	}else if(cistrcmp(a, "UID") == 0){
18067dd7cddfSDavid du Colombier 		sr->key = SKUid;
18077dd7cddfSDavid du Colombier 		mustBe(' ');
1808becaf2abSDavid du Colombier 		sr->set = msgSet(0);
18097dd7cddfSDavid du Colombier 	}else if(cistrcmp(a, "NOT") == 0){
18107dd7cddfSDavid du Colombier 		sr->key = SKNot;
18117dd7cddfSDavid du Colombier 		mustBe(' ');
18127dd7cddfSDavid du Colombier 		rock.next = nil;
18137dd7cddfSDavid du Colombier 		searchKeys(0, &rock);
18147dd7cddfSDavid du Colombier 		sr->left = rock.next;
18157dd7cddfSDavid du Colombier 	}else if(cistrcmp(a, "OR") == 0){
18167dd7cddfSDavid du Colombier 		sr->key = SKOr;
18177dd7cddfSDavid du Colombier 		mustBe(' ');
18187dd7cddfSDavid du Colombier 		rock.next = nil;
18197dd7cddfSDavid du Colombier 		searchKeys(0, &rock);
18207dd7cddfSDavid du Colombier 		sr->left = rock.next;
18217dd7cddfSDavid du Colombier 		mustBe(' ');
18227dd7cddfSDavid du Colombier 		rock.next = nil;
18237dd7cddfSDavid du Colombier 		searchKeys(0, &rock);
18247dd7cddfSDavid du Colombier 		sr->right = rock.next;
18257dd7cddfSDavid du Colombier 	}else
18267dd7cddfSDavid du Colombier 		parseErr("illegal search key");
18277dd7cddfSDavid du Colombier 	return sr;
18287dd7cddfSDavid du Colombier }
18297dd7cddfSDavid du Colombier 
18307dd7cddfSDavid du Colombier /*
18317dd7cddfSDavid du Colombier  * set	: seqno
18327dd7cddfSDavid du Colombier  *	| seqno ':' seqno
18337dd7cddfSDavid du Colombier  *	| set ',' set
18347dd7cddfSDavid du Colombier  * seqno: nz-number
18357dd7cddfSDavid du Colombier  *	| '*'
18367dd7cddfSDavid du Colombier  *
18377dd7cddfSDavid du Colombier  */
18387dd7cddfSDavid du Colombier static MsgSet*
msgSet(int uids)1839becaf2abSDavid du Colombier msgSet(int uids)
18407dd7cddfSDavid du Colombier {
18417dd7cddfSDavid du Colombier 	MsgSet head, *last, *ms;
18427dd7cddfSDavid du Colombier 	ulong from, to;
18437dd7cddfSDavid du Colombier 
18447dd7cddfSDavid du Colombier 	last = &head;
18457dd7cddfSDavid du Colombier 	head.next = nil;
18467dd7cddfSDavid du Colombier 	for(;;){
1847becaf2abSDavid du Colombier 		from = uids ? uidNo() : seqNo();
18487dd7cddfSDavid du Colombier 		to = from;
18497dd7cddfSDavid du Colombier 		if(peekc() == ':'){
18507dd7cddfSDavid du Colombier 			getc();
1851becaf2abSDavid du Colombier 			to = uids ? uidNo() : seqNo();
18527dd7cddfSDavid du Colombier 		}
185380ee5cbfSDavid du Colombier 		ms = binalloc(&parseBin, sizeof(MsgSet), 0);
18547dd7cddfSDavid du Colombier 		if(ms == nil)
18557dd7cddfSDavid du Colombier 			parseErr("out of memory");
18567dd7cddfSDavid du Colombier 		ms->from = from;
18577dd7cddfSDavid du Colombier 		ms->to = to;
18587dd7cddfSDavid du Colombier 		ms->next = nil;
18597dd7cddfSDavid du Colombier 		last->next = ms;
18607dd7cddfSDavid du Colombier 		last = ms;
18617dd7cddfSDavid du Colombier 		if(peekc() != ',')
18627dd7cddfSDavid du Colombier 			break;
18637dd7cddfSDavid du Colombier 		getc();
18647dd7cddfSDavid du Colombier 	}
18657dd7cddfSDavid du Colombier 	return head.next;
18667dd7cddfSDavid du Colombier }
18677dd7cddfSDavid du Colombier 
18687dd7cddfSDavid du Colombier static ulong
seqNo(void)18697dd7cddfSDavid du Colombier seqNo(void)
18707dd7cddfSDavid du Colombier {
18717dd7cddfSDavid du Colombier 	if(peekc() == '*'){
18727dd7cddfSDavid du Colombier 		getc();
18737dd7cddfSDavid du Colombier 		return ~0UL;
18747dd7cddfSDavid du Colombier 	}
18757dd7cddfSDavid du Colombier 	return number(1);
18767dd7cddfSDavid du Colombier }
18777dd7cddfSDavid du Colombier 
1878becaf2abSDavid du Colombier static ulong
uidNo(void)1879becaf2abSDavid du Colombier uidNo(void)
1880becaf2abSDavid du Colombier {
1881becaf2abSDavid du Colombier 	if(peekc() == '*'){
1882becaf2abSDavid du Colombier 		getc();
1883becaf2abSDavid du Colombier 		return ~0UL;
1884becaf2abSDavid du Colombier 	}
1885becaf2abSDavid du Colombier 	return number(0);
1886becaf2abSDavid du Colombier }
1887becaf2abSDavid du Colombier 
18887dd7cddfSDavid du Colombier /*
18897dd7cddfSDavid du Colombier  * 7 bit, non-ctl chars, no (){%*"\
18907dd7cddfSDavid du Colombier  * NIL is special case for nstring or parenlist
18917dd7cddfSDavid du Colombier  */
18927dd7cddfSDavid du Colombier static char *
atom(void)18937dd7cddfSDavid du Colombier atom(void)
18947dd7cddfSDavid du Colombier {
18957dd7cddfSDavid du Colombier 	return atomString(atomStop, "");
18967dd7cddfSDavid du Colombier }
18977dd7cddfSDavid du Colombier 
18987dd7cddfSDavid du Colombier /*
18997dd7cddfSDavid du Colombier  * like an atom, but no +
19007dd7cddfSDavid du Colombier  */
19017dd7cddfSDavid du Colombier static char *
tag(void)19027dd7cddfSDavid du Colombier tag(void)
19037dd7cddfSDavid du Colombier {
19047dd7cddfSDavid du Colombier 	return atomString("+(){%*\"\\", "");
19057dd7cddfSDavid du Colombier }
19067dd7cddfSDavid du Colombier 
19077dd7cddfSDavid du Colombier /*
19087dd7cddfSDavid du Colombier  * string or atom allowing %*
19097dd7cddfSDavid du Colombier  */
19107dd7cddfSDavid du Colombier static char *
listmbox(void)19117dd7cddfSDavid du Colombier listmbox(void)
19127dd7cddfSDavid du Colombier {
19137dd7cddfSDavid du Colombier 	int c;
19147dd7cddfSDavid du Colombier 
19157dd7cddfSDavid du Colombier 	c = peekc();
19167dd7cddfSDavid du Colombier 	if(c == '{')
19177dd7cddfSDavid du Colombier 		return literal();
19187dd7cddfSDavid du Colombier 	if(c == '"')
19197dd7cddfSDavid du Colombier 		return quoted();
19207dd7cddfSDavid du Colombier 	return atomString("(){\"\\", "");
19217dd7cddfSDavid du Colombier }
19227dd7cddfSDavid du Colombier 
19237dd7cddfSDavid du Colombier /*
19247dd7cddfSDavid du Colombier  * string or atom
19257dd7cddfSDavid du Colombier  */
19267dd7cddfSDavid du Colombier static char *
astring(void)19277dd7cddfSDavid du Colombier astring(void)
19287dd7cddfSDavid du Colombier {
19297dd7cddfSDavid du Colombier 	int c;
19307dd7cddfSDavid du Colombier 
19317dd7cddfSDavid du Colombier 	c = peekc();
19327dd7cddfSDavid du Colombier 	if(c == '{')
19337dd7cddfSDavid du Colombier 		return literal();
19347dd7cddfSDavid du Colombier 	if(c == '"')
19357dd7cddfSDavid du Colombier 		return quoted();
19367dd7cddfSDavid du Colombier 	return atom();
19377dd7cddfSDavid du Colombier }
19387dd7cddfSDavid du Colombier 
19397dd7cddfSDavid du Colombier /*
19407dd7cddfSDavid du Colombier  * 7 bit, non-ctl chars, none from exception list
19417dd7cddfSDavid du Colombier  */
19427dd7cddfSDavid du Colombier static char *
atomString(char * disallowed,char * initial)19437dd7cddfSDavid du Colombier atomString(char *disallowed, char *initial)
19447dd7cddfSDavid du Colombier {
19457dd7cddfSDavid du Colombier 	char *s;
19467dd7cddfSDavid du Colombier 	int c, ns, as;
19477dd7cddfSDavid du Colombier 
19487dd7cddfSDavid du Colombier 	ns = strlen(initial);
194980ee5cbfSDavid du Colombier 	s = binalloc(&parseBin, ns + StrAlloc, 0);
19507dd7cddfSDavid du Colombier 	if(s == nil)
19517dd7cddfSDavid du Colombier 		parseErr("out of memory");
19527dd7cddfSDavid du Colombier 	strcpy(s, initial);
19537dd7cddfSDavid du Colombier 	as = ns + StrAlloc;
19547dd7cddfSDavid du Colombier 	for(;;){
19557dd7cddfSDavid du Colombier 		c = getc();
19567dd7cddfSDavid du Colombier 		if(c <= ' ' || c >= 0x7f || strchr(disallowed, c) != nil){
19577dd7cddfSDavid du Colombier 			ungetc();
19587dd7cddfSDavid du Colombier 			break;
19597dd7cddfSDavid du Colombier 		}
19607dd7cddfSDavid du Colombier 		s[ns++] = c;
19617dd7cddfSDavid du Colombier 		if(ns >= as){
196280ee5cbfSDavid du Colombier 			s = bingrow(&parseBin, s, as, as + StrAlloc, 0);
19637dd7cddfSDavid du Colombier 			if(s == nil)
19647dd7cddfSDavid du Colombier 				parseErr("out of memory");
19657dd7cddfSDavid du Colombier 			as += StrAlloc;
19667dd7cddfSDavid du Colombier 		}
19677dd7cddfSDavid du Colombier 	}
19687dd7cddfSDavid du Colombier 	if(ns == 0)
19697dd7cddfSDavid du Colombier 		badsyn();
19707dd7cddfSDavid du Colombier 	s[ns] = '\0';
19717dd7cddfSDavid du Colombier 	return s;
19727dd7cddfSDavid du Colombier }
19737dd7cddfSDavid du Colombier 
19747dd7cddfSDavid du Colombier /*
19757dd7cddfSDavid du Colombier  * quoted: '"' chars* '"'
19767dd7cddfSDavid du Colombier  * chars:	1-128 except \r and \n
19777dd7cddfSDavid du Colombier  */
19787dd7cddfSDavid du Colombier static char *
quoted(void)19797dd7cddfSDavid du Colombier quoted(void)
19807dd7cddfSDavid du Colombier {
19817dd7cddfSDavid du Colombier 	char *s;
19827dd7cddfSDavid du Colombier 	int c, ns, as;
19837dd7cddfSDavid du Colombier 
19847dd7cddfSDavid du Colombier 	mustBe('"');
198580ee5cbfSDavid du Colombier 	s = binalloc(&parseBin, StrAlloc, 0);
19867dd7cddfSDavid du Colombier 	if(s == nil)
19877dd7cddfSDavid du Colombier 		parseErr("out of memory");
19887dd7cddfSDavid du Colombier 	as = StrAlloc;
19897dd7cddfSDavid du Colombier 	ns = 0;
19907dd7cddfSDavid du Colombier 	for(;;){
19917dd7cddfSDavid du Colombier 		c = getc();
19927dd7cddfSDavid du Colombier 		if(c == '"')
19937dd7cddfSDavid du Colombier 			break;
19947dd7cddfSDavid du Colombier 		if(c < 1 || c > 0x7f || c == '\r' || c == '\n')
19957dd7cddfSDavid du Colombier 			badsyn();
19967dd7cddfSDavid du Colombier 		if(c == '\\'){
19977dd7cddfSDavid du Colombier 			c = getc();
19987dd7cddfSDavid du Colombier 			if(c != '\\' && c != '"')
19997dd7cddfSDavid du Colombier 				badsyn();
20007dd7cddfSDavid du Colombier 		}
20017dd7cddfSDavid du Colombier 		s[ns++] = c;
20027dd7cddfSDavid du Colombier 		if(ns >= as){
200380ee5cbfSDavid du Colombier 			s = bingrow(&parseBin, s, as, as + StrAlloc, 0);
20047dd7cddfSDavid du Colombier 			if(s == nil)
20057dd7cddfSDavid du Colombier 				parseErr("out of memory");
20067dd7cddfSDavid du Colombier 			as += StrAlloc;
20077dd7cddfSDavid du Colombier 		}
20087dd7cddfSDavid du Colombier 	}
20097dd7cddfSDavid du Colombier 	s[ns] = '\0';
20107dd7cddfSDavid du Colombier 	return s;
20117dd7cddfSDavid du Colombier }
20127dd7cddfSDavid du Colombier 
20137dd7cddfSDavid du Colombier /*
20147dd7cddfSDavid du Colombier  * litlen: {number}\r\n
20157dd7cddfSDavid du Colombier  */
20167dd7cddfSDavid du Colombier static ulong
litlen(void)20177dd7cddfSDavid du Colombier litlen(void)
20187dd7cddfSDavid du Colombier {
20197dd7cddfSDavid du Colombier 	ulong v;
20207dd7cddfSDavid du Colombier 
20217dd7cddfSDavid du Colombier 	mustBe('{');
20227dd7cddfSDavid du Colombier 	v = number(0);
20237dd7cddfSDavid du Colombier 	mustBe('}');
20247dd7cddfSDavid du Colombier 	crnl();
20257dd7cddfSDavid du Colombier 	return v;
20267dd7cddfSDavid du Colombier }
20277dd7cddfSDavid du Colombier 
20287dd7cddfSDavid du Colombier /*
20297dd7cddfSDavid du Colombier  * literal: litlen data<0:litlen>
20307dd7cddfSDavid du Colombier  */
20317dd7cddfSDavid du Colombier static char *
literal(void)20327dd7cddfSDavid du Colombier literal(void)
20337dd7cddfSDavid du Colombier {
20347dd7cddfSDavid du Colombier 	char *s;
20357dd7cddfSDavid du Colombier 	ulong v;
20367dd7cddfSDavid du Colombier 
20377dd7cddfSDavid du Colombier 	v = litlen();
203880ee5cbfSDavid du Colombier 	s = binalloc(&parseBin, v+1, 0);
20397dd7cddfSDavid du Colombier 	if(s == nil)
20407dd7cddfSDavid du Colombier 		parseErr("out of memory");
20417dd7cddfSDavid du Colombier 	Bprint(&bout, "+ Ready for literal data\r\n");
20427dd7cddfSDavid du Colombier 	if(Bflush(&bout) < 0)
20437dd7cddfSDavid du Colombier 		writeErr();
20447dd7cddfSDavid du Colombier 	if(v != 0 && Bread(&bin, s, v) != v)
20457dd7cddfSDavid du Colombier 		badsyn();
20467dd7cddfSDavid du Colombier 	s[v] = '\0';
20477dd7cddfSDavid du Colombier 	return s;
20487dd7cddfSDavid du Colombier }
20497dd7cddfSDavid du Colombier 
20507dd7cddfSDavid du Colombier /*
20517dd7cddfSDavid du Colombier  * digits; number is 32 bits
20527dd7cddfSDavid du Colombier  */
20537dd7cddfSDavid du Colombier static ulong
number(int nonzero)20547dd7cddfSDavid du Colombier number(int nonzero)
20557dd7cddfSDavid du Colombier {
20567dd7cddfSDavid du Colombier 	ulong v;
20577dd7cddfSDavid du Colombier 	int c, first;
20587dd7cddfSDavid du Colombier 
20597dd7cddfSDavid du Colombier 	v = 0;
20607dd7cddfSDavid du Colombier 	first = 1;
20617dd7cddfSDavid du Colombier 	for(;;){
20627dd7cddfSDavid du Colombier 		c = getc();
20637dd7cddfSDavid du Colombier 		if(c < '0' || c > '9'){
20647dd7cddfSDavid du Colombier 			ungetc();
20657dd7cddfSDavid du Colombier 			if(first)
20667dd7cddfSDavid du Colombier 				badsyn();
20677dd7cddfSDavid du Colombier 			break;
20687dd7cddfSDavid du Colombier 		}
20697dd7cddfSDavid du Colombier 		if(nonzero && first && c == '0')
20707dd7cddfSDavid du Colombier 			badsyn();
20717dd7cddfSDavid du Colombier 		c -= '0';
20727dd7cddfSDavid du Colombier 		first = 0;
207359cc4ca5SDavid du Colombier 		if(v > UlongMax/10 || v == UlongMax/10 && c > UlongMax%10)
20747dd7cddfSDavid du Colombier 			parseErr("number out of range\r\n");
20757dd7cddfSDavid du Colombier 		v = v * 10 + c;
20767dd7cddfSDavid du Colombier 	}
20777dd7cddfSDavid du Colombier 	return v;
20787dd7cddfSDavid du Colombier }
20797dd7cddfSDavid du Colombier 
20807dd7cddfSDavid du Colombier static int
getc(void)20817dd7cddfSDavid du Colombier getc(void)
20827dd7cddfSDavid du Colombier {
20837dd7cddfSDavid du Colombier 	return Bgetc(&bin);
20847dd7cddfSDavid du Colombier }
20857dd7cddfSDavid du Colombier 
20867dd7cddfSDavid du Colombier static void
ungetc(void)20877dd7cddfSDavid du Colombier ungetc(void)
20887dd7cddfSDavid du Colombier {
20897dd7cddfSDavid du Colombier 	Bungetc(&bin);
20907dd7cddfSDavid du Colombier }
20917dd7cddfSDavid du Colombier 
20927dd7cddfSDavid du Colombier static int
peekc(void)20937dd7cddfSDavid du Colombier peekc(void)
20947dd7cddfSDavid du Colombier {
20957dd7cddfSDavid du Colombier 	int c;
20967dd7cddfSDavid du Colombier 
20977dd7cddfSDavid du Colombier 	c = Bgetc(&bin);
20987dd7cddfSDavid du Colombier 	Bungetc(&bin);
20997dd7cddfSDavid du Colombier 	return c;
21007dd7cddfSDavid du Colombier }
2101d9306527SDavid du Colombier 
2102