| #include <u.h> | 
 | #include <libc.h> | 
 | #include <bio.h> | 
 | #include <thread.h> | 
 | #include <9pclient.h> | 
 | #include <plumb.h> | 
 | #include <ctype.h> | 
 | #include "dat.h" | 
 |  | 
 | char	*maildir = "Mail/";			/* mountpoint of mail file system */ | 
 | char *mboxname = "mbox";			/* mailboxdir/mboxname is mail spool file */ | 
 | char	*mailboxdir = nil;				/* nil == /mail/box/$user */ | 
 | char *fsname;						/* filesystem for mailboxdir/mboxname is at maildir/fsname */ | 
 | char	*user; | 
 | char	*outgoing; | 
 | char *srvname; | 
 |  | 
 | Window	*wbox; | 
 | Message	mbox; | 
 | Message	replies; | 
 | char		*home; | 
 | CFid		*plumbsendfd; | 
 | CFid		*plumbseemailfd; | 
 | CFid		*plumbshowmailfd; | 
 | CFid		*plumbsendmailfd; | 
 | Channel	*cplumb; | 
 | Channel	*cplumbshow; | 
 | Channel	*cplumbsend; | 
 | int		wctlfd; | 
 | void		mainctl(void*); | 
 | void		plumbproc(void*); | 
 | void		plumbshowproc(void*); | 
 | void		plumbsendproc(void*); | 
 | void		plumbthread(void); | 
 | void		plumbshowthread(void*); | 
 | void		plumbsendthread(void*); | 
 |  | 
 | int			shortmenu; | 
 |  | 
 | CFsys *mailfs; | 
 | CFsys *acmefs; | 
 |  | 
 | void | 
 | usage(void) | 
 | { | 
 | 	fprint(2, "usage: Mail [-sS] [-n srvname] [-o outgoing] [mailboxname [directoryname]]\n"); | 
 | 	threadexitsall("usage"); | 
 | } | 
 |  | 
 | void | 
 | removeupasfs(void) | 
 | { | 
 | 	char buf[256]; | 
 |  | 
 | 	if(strcmp(mboxname, "mbox") == 0) | 
 | 		return; | 
 | 	snprint(buf, sizeof buf, "close %s", mboxname); | 
 | 	fswrite(mbox.ctlfd, buf, strlen(buf)); | 
 | } | 
 |  | 
 | int | 
 | ismaildir(char *s) | 
 | { | 
 | 	Dir *d; | 
 | 	int ret; | 
 |  | 
 | 	d = fsdirstat(mailfs, s); | 
 | 	if(d == nil) | 
 | 		return 0; | 
 | 	ret = d->qid.type & QTDIR; | 
 | 	free(d); | 
 | 	return ret; | 
 | } | 
 |  | 
 | void | 
 | threadmain(int argc, char *argv[]) | 
 | { | 
 | 	char *s, *name; | 
 | 	char err[ERRMAX], *cmd; | 
 | 	int i, newdir; | 
 | 	Fmt fmt; | 
 |  | 
 | 	doquote = needsrcquote; | 
 | 	quotefmtinstall(); | 
 |  | 
 | 	/* open these early so we won't miss notification of new mail messages while we read mbox */ | 
 | 	if((plumbsendfd = plumbopenfid("send", OWRITE|OCEXEC)) == nil) | 
 | 		fprint(2, "warning: open plumb/send: %r\n"); | 
 | 	if((plumbseemailfd = plumbopenfid("seemail", OREAD|OCEXEC)) == nil) | 
 | 		fprint(2, "warning: open plumb/seemail: %r\n"); | 
 | 	if((plumbshowmailfd = plumbopenfid("showmail", OREAD|OCEXEC)) == nil) | 
 | 		fprint(2, "warning: open plumb/showmail: %r\n"); | 
 |  | 
 | 	shortmenu = 0; | 
 | 	srvname = "mail"; | 
 | 	ARGBEGIN{ | 
 | 	case 's': | 
 | 		shortmenu = 1; | 
 | 		break; | 
 | 	case 'S': | 
 | 		shortmenu = 2; | 
 | 		break; | 
 | 	case 'o': | 
 | 		outgoing = EARGF(usage()); | 
 | 		break; | 
 | 	case 'm': | 
 | 		smprint(maildir, "%s/", EARGF(usage())); | 
 | 		break; | 
 | 	case 'n': | 
 | 		srvname = EARGF(usage()); | 
 | 		break; | 
 | 	default: | 
 | 		usage(); | 
 | 	}ARGEND | 
 |  | 
 | 	acmefs = nsmount("acme",nil); | 
 | 	if(acmefs == nil) | 
 | 		error("cannot mount acme: %r"); | 
 | 	mailfs = nsmount(srvname, nil); | 
 | 	if(mailfs == nil) | 
 | 		error("cannot mount %s: %r", srvname); | 
 |  | 
 | 	name = "mbox"; | 
 |  | 
 | 	newdir = 1; | 
 | 	if(argc > 0){ | 
 | 		i = strlen(argv[0]); | 
 | 		if(argc>2 || i==0) | 
 | 			usage(); | 
 | 		/* see if the name is that of an existing /mail/fs directory */ | 
 | 		if(argc==1 && argv[0][0] != '/' && ismaildir(argv[0])){ | 
 | 			name = argv[0]; | 
 | 			mboxname = estrdup(name); | 
 | 			newdir = 0; | 
 | 		}else{ | 
 | 			if(argv[0][i-1] == '/') | 
 | 				argv[0][i-1] = '\0'; | 
 | 			s = strrchr(argv[0], '/'); | 
 | 			if(s == nil) | 
 | 				mboxname = estrdup(argv[0]); | 
 | 			else{ | 
 | 				*s++ = '\0'; | 
 | 				if(*s == '\0') | 
 | 					usage(); | 
 | 				mailboxdir = argv[0]; | 
 | 				mboxname = estrdup(s); | 
 | 			} | 
 | 			if(argc > 1) | 
 | 				name = argv[1]; | 
 | 			else | 
 | 				name = mboxname; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	user = getenv("user"); | 
 | 	if(user == nil) | 
 | 		user = "none"; | 
 | 	home = getenv("home"); | 
 | 	if(home == nil) | 
 | 		home = getenv("HOME"); | 
 | 	if(home == nil) | 
 | 		error("can't find $home"); | 
 | 	if(mailboxdir == nil) | 
 | 		mailboxdir = estrstrdup(home, "/mail"); | 
 | 	if(outgoing == nil) | 
 | 		outgoing = estrstrdup(mailboxdir, "/outgoing"); | 
 |  | 
 | 	mbox.ctlfd = fsopen(mailfs, estrstrdup(mboxname, "/ctl"), OWRITE); | 
 | 	if(mbox.ctlfd == nil) | 
 | 		error("can't open %s: %r", estrstrdup(mboxname, "/ctl")); | 
 |  | 
 | 	fsname = estrdup(name); | 
 | 	if(newdir && argc > 0){ | 
 | 		s = emalloc(5+strlen(mailboxdir)+strlen(mboxname)+strlen(name)+10+1); | 
 | 		for(i=0; i<10; i++){ | 
 | 			sprint(s, "open %s/%s %s", mailboxdir, mboxname, fsname); | 
 | 			if(fswrite(mbox.ctlfd, s, strlen(s)) >= 0) | 
 | 				break; | 
 | 			err[0] = '\0'; | 
 | 			errstr(err, sizeof err); | 
 | 			if(strstr(err, "mbox name in use") == nil) | 
 | 				error("can't create directory %s for mail: %s", name, err); | 
 | 			free(fsname); | 
 | 			fsname = emalloc(strlen(name)+10); | 
 | 			sprint(fsname, "%s-%d", name, i); | 
 | 		} | 
 | 		if(i == 10) | 
 | 			error("can't open %s/%s: %r", mailboxdir, mboxname); | 
 | 		free(s); | 
 | 	} | 
 |  | 
 | 	s = estrstrdup(fsname, "/"); | 
 | 	mbox.name = estrstrdup(maildir, s); | 
 | 	mbox.level= 0; | 
 | 	readmbox(&mbox, maildir, s); | 
 | 	home = getenv("home"); | 
 | 	if(home == nil) | 
 | 		home = "/"; | 
 |  | 
 | 	wbox = newwindow(); | 
 | 	winname(wbox, mbox.name); | 
 | 	wintagwrite(wbox, "Put Mail Delmesg ", 3+1+4+1+7+1); | 
 | 	threadcreate(mainctl, wbox, STACK); | 
 |  | 
 | 	fmtstrinit(&fmt); | 
 | 	fmtprint(&fmt, "Mail"); | 
 | 	if(shortmenu) | 
 | 		fmtprint(&fmt, " -%c", "sS"[shortmenu-1]); | 
 | 	if(outgoing) | 
 | 		fmtprint(&fmt, " -o %s", outgoing); | 
 | 	fmtprint(&fmt, " %s", name); | 
 | 	cmd = fmtstrflush(&fmt); | 
 | 	if(cmd == nil) | 
 | 		sysfatal("out of memory"); | 
 | 	winsetdump(wbox, "/acme/mail", cmd); | 
 | 	mbox.w = wbox; | 
 |  | 
 | 	mesgmenu(wbox, &mbox); | 
 | 	winclean(wbox); | 
 |  | 
 | /*	wctlfd = open("/dev/wctl", OWRITE|OCEXEC);	/* for acme window */ | 
 | 	wctlfd = -1; | 
 | 	cplumb = chancreate(sizeof(Plumbmsg*), 0); | 
 | 	cplumbshow = chancreate(sizeof(Plumbmsg*), 0); | 
 | 	if(strcmp(name, "mbox") == 0){ | 
 | 		/* | 
 | 		 * Avoid creating multiple windows to send mail by only accepting | 
 | 		 * sendmail plumb messages if we're reading the main mailbox. | 
 | 		 */ | 
 | 		plumbsendmailfd = plumbopenfid("sendmail", OREAD|OCEXEC); | 
 | 		cplumbsend = chancreate(sizeof(Plumbmsg*), 0); | 
 | 		proccreate(plumbsendproc, nil, STACK); | 
 | 		threadcreate(plumbsendthread, nil, STACK); | 
 | 	} | 
 | 	/* start plumb reader as separate proc ... */ | 
 | 	proccreate(plumbproc, nil, STACK); | 
 | 	proccreate(plumbshowproc, nil, STACK); | 
 | 	threadcreate(plumbshowthread, nil, STACK); | 
 | 	fswrite(mbox.ctlfd, "refresh", 7); | 
 | 	/* ... and use this thread to read the messages */ | 
 | 	plumbthread(); | 
 | } | 
 |  | 
 | void | 
 | plumbproc(void* v) | 
 | { | 
 | 	Plumbmsg *m; | 
 |  | 
 | 	threadsetname("plumbproc"); | 
 | 	for(;;){ | 
 | 		m = plumbrecvfid(plumbseemailfd); | 
 | 		sendp(cplumb, m); | 
 | 		if(m == nil) | 
 | 			threadexits(nil); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | plumbshowproc(void* v) | 
 | { | 
 | 	Plumbmsg *m; | 
 |  | 
 | 	threadsetname("plumbshowproc"); | 
 | 	for(;;){ | 
 | 		m = plumbrecvfid(plumbshowmailfd); | 
 | 		sendp(cplumbshow, m); | 
 | 		if(m == nil) | 
 | 			threadexits(nil); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | plumbsendproc(void* v) | 
 | { | 
 | 	Plumbmsg *m; | 
 |  | 
 | 	threadsetname("plumbsendproc"); | 
 | 	for(;;){ | 
 | 		m = plumbrecvfid(plumbsendmailfd); | 
 | 		sendp(cplumbsend, m); | 
 | 		if(m == nil) | 
 | 			threadexits(nil); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | newmesg(char *name, char *digest) | 
 | { | 
 | 	Dir *d; | 
 |  | 
 | 	if(strncmp(name, mbox.name, strlen(mbox.name)) != 0) | 
 | 		return;	/* message is about another mailbox */ | 
 | 	if(mesglookupfile(&mbox, name, digest) != nil) | 
 | 		return; | 
 | 	if(strncmp(name, "Mail/", 5) == 0) | 
 | 		name += 5; | 
 | 	d = fsdirstat(mailfs, name); | 
 | 	if(d == nil) | 
 | 		return; | 
 | 	if(mesgadd(&mbox, mbox.name, d, digest)) | 
 | 		mesgmenunew(wbox, &mbox); | 
 | 	free(d); | 
 | } | 
 |  | 
 | void | 
 | showmesg(char *name, char *digest) | 
 | { | 
 | 	char *n; | 
 | 	char *mb; | 
 |  | 
 | 	mb = mbox.name; | 
 | 	if(strncmp(name, mb, strlen(mb)) != 0) | 
 | 		return;	/* message is about another mailbox */ | 
 | 	n = estrdup(name+strlen(mb)); | 
 | 	if(n[strlen(n)-1] != '/') | 
 | 		n = egrow(n, "/", nil); | 
 | 	mesgopen(&mbox, mbox.name, name+strlen(mb), nil, 1, digest); | 
 | 	free(n); | 
 | } | 
 |  | 
 | void | 
 | delmesg(char *name, char *digest, int dodel, char *save) | 
 | { | 
 | 	Message *m; | 
 |  | 
 | 	m = mesglookupfile(&mbox, name, digest); | 
 | 	if(m != nil){ | 
 | 		if(save) | 
 | 			mesgcommand(m, estrstrdup("Save ", save)); | 
 | 		if(dodel) | 
 | 			mesgmenumarkdel(wbox, &mbox, m, 1); | 
 | 		else{ | 
 | 			/* notification came from plumber - message is gone */ | 
 | 			mesgmenudel(wbox, &mbox, m); | 
 | 			if(!m->opened) | 
 | 				mesgdel(&mbox, m); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | plumbthread(void) | 
 | { | 
 | 	Plumbmsg *m; | 
 | 	Plumbattr *a; | 
 | 	char *type, *digest; | 
 |  | 
 | 	threadsetname("plumbthread"); | 
 | 	while((m = recvp(cplumb)) != nil){ | 
 | 		a = m->attr; | 
 | 		digest = plumblookup(a, "digest"); | 
 | 		type = plumblookup(a, "mailtype"); | 
 | 		if(type == nil) | 
 | 			fprint(2, "Mail: plumb message with no mailtype attribute\n"); | 
 | 		else if(strcmp(type, "new") == 0) | 
 | 			newmesg(m->data, digest); | 
 | 		else if(strcmp(type, "delete") == 0) | 
 | 			delmesg(m->data, digest, 0, nil); | 
 | 		else | 
 | 			fprint(2, "Mail: unknown plumb attribute %s\n", type); | 
 | 		plumbfree(m); | 
 | 	} | 
 | 	threadexits(nil); | 
 | } | 
 |  | 
 | void | 
 | plumbshowthread(void *v) | 
 | { | 
 | 	Plumbmsg *m; | 
 |  | 
 | 	USED(v); | 
 | 	threadsetname("plumbshowthread"); | 
 | 	while((m = recvp(cplumbshow)) != nil){ | 
 | 		showmesg(m->data, plumblookup(m->attr, "digest")); | 
 | 		plumbfree(m); | 
 | 	} | 
 | 	threadexits(nil); | 
 | } | 
 |  | 
 | void | 
 | plumbsendthread(void *v) | 
 | { | 
 | 	Plumbmsg *m; | 
 |  | 
 | 	USED(v); | 
 | 	threadsetname("plumbsendthread"); | 
 | 	while((m = recvp(cplumbsend)) != nil){ | 
 | 		mkreply(nil, "Mail", m->data, m->attr, nil); | 
 | 		plumbfree(m); | 
 | 	} | 
 | 	threadexits(nil); | 
 | } | 
 |  | 
 | int | 
 | mboxcommand(Window *w, char *s) | 
 | { | 
 | 	char *args[10], **targs, *save; | 
 | 	Window *sbox; | 
 | 	Message *m, *next; | 
 | 	int ok, nargs, i, j; | 
 | 	CFid *searchfd; | 
 | 	char buf[128], *res; | 
 |  | 
 | 	nargs = tokenize(s, args, nelem(args)); | 
 | 	if(nargs == 0) | 
 | 		return 0; | 
 | 	if(strcmp(args[0], "Mail") == 0){ | 
 | 		if(nargs == 1) | 
 | 			mkreply(nil, "Mail", "", nil, nil); | 
 | 		else | 
 | 			mkreply(nil, "Mail", args[1], nil, nil); | 
 | 		return 1; | 
 | 	} | 
 | 	if(strcmp(s, "Del") == 0){ | 
 | 		if(mbox.dirty){ | 
 | 			mbox.dirty = 0; | 
 | 			fprint(2, "mail: mailbox not written\n"); | 
 | 			return 1; | 
 | 		} | 
 | 		if(w != mbox.w){ | 
 | 			windel(w, 1); | 
 | 			return 1; | 
 | 		} | 
 | 		ok = 1; | 
 | 		for(m=mbox.head; m!=nil; m=next){ | 
 | 			next = m->next; | 
 | 			if(m->w){ | 
 | 				if(windel(m->w, 0)) | 
 | 					m->w = nil; | 
 | 				else | 
 | 					ok = 0; | 
 | 			} | 
 | 		} | 
 | 		for(m=replies.head; m!=nil; m=next){ | 
 | 			next = m->next; | 
 | 			if(m->w){ | 
 | 				if(windel(m->w, 0)) | 
 | 					m->w = nil; | 
 | 				else | 
 | 					ok = 0; | 
 | 			} | 
 | 		} | 
 | 		if(ok){ | 
 | 			windel(w, 1); | 
 | 			removeupasfs(); | 
 | 			threadexitsall(nil); | 
 | 		} | 
 | 		return 1; | 
 | 	} | 
 | 	if(strcmp(s, "Put") == 0){ | 
 | 		rewritembox(wbox, &mbox); | 
 | 		return 1; | 
 | 	} | 
 | 	if(strcmp(s, "Get") == 0){ | 
 | 		fswrite(mbox.ctlfd, "refresh", 7); | 
 | 		return 1; | 
 | 	} | 
 | 	if(strcmp(s, "Delmesg") == 0){ | 
 | 		save = nil; | 
 | 		if(nargs > 1) | 
 | 			save = args[1]; | 
 | 		s = winselection(w); | 
 | 		if(s == nil) | 
 | 			return 1; | 
 | 		nargs = 1; | 
 | 		for(i=0; s[i]; i++) | 
 | 			if(s[i] == '\n') | 
 | 				nargs++; | 
 | 		targs = emalloc(nargs*sizeof(char*));	/* could be too many for a local array */ | 
 | 		nargs = getfields(s, targs, nargs, 1, "\n"); | 
 | 		for(i=0; i<nargs; i++){ | 
 | 			if(!isdigit(targs[i][0])) | 
 | 				continue; | 
 | 			j = atoi(targs[i]);	/* easy way to parse the number! */ | 
 | 			if(j == 0) | 
 | 				continue; | 
 | 			snprint(buf, sizeof buf, "%s%d", mbox.name, j); | 
 | 			delmesg(buf, nil, 1, save); | 
 | 		} | 
 | 		free(s); | 
 | 		free(targs); | 
 | 		return 1; | 
 | 	} | 
 | 	if(strcmp(s, "Search") == 0){ | 
 | 		if(nargs <= 1) | 
 | 			return 1; | 
 | 		s = estrstrdup(mboxname, "/search"); | 
 | 		searchfd = fsopen(mailfs, s, ORDWR); | 
 | 		if(searchfd == nil) | 
 | 			return 1; | 
 | 		save = estrdup(args[1]); | 
 | 		for(i=2; i<nargs; i++) | 
 | 			save = eappend(save, " ", args[i]); | 
 | 		fswrite(searchfd, save, strlen(save)); | 
 | 		fsseek(searchfd, 0, 0); | 
 | 		j = fsread(searchfd, buf, sizeof buf - 1); | 
 |  		if(j == 0){ | 
 | 			fprint(2, "[%s] search %s: no results found\n", mboxname, save); | 
 | 			fsclose(searchfd); | 
 | 			free(save); | 
 | 			return 1; | 
 | 		} | 
 | 		free(save); | 
 | 		buf[j] = '\0'; | 
 | 		res = estrdup(buf); | 
 | 		j = fsread(searchfd, buf, sizeof buf - 1); | 
 | 		for(; j != 0; j = fsread(searchfd, buf, sizeof buf - 1), buf[j] = '\0') | 
 | 			res = eappend(res, "", buf); | 
 | 		fsclose(searchfd); | 
 |  | 
 | 		sbox = newwindow(); | 
 | 		winname(sbox, s); | 
 | 		free(s); | 
 | 		threadcreate(mainctl, sbox, STACK); | 
 | 		winopenbody(sbox, OWRITE); | 
 |  | 
 | 		/* show results in reverse order */ | 
 | 		m = mbox.tail; | 
 | 		save = nil; | 
 | 		for(s=strrchr(res, ' '); s!=nil || save!=res; s=strrchr(res, ' ')){ | 
 | 			if(s != nil){ | 
 | 				save = s+1; | 
 | 				*s = '\0'; | 
 | 			} | 
 | 			else save = res; | 
 | 			save = estrstrdup(save, "/"); | 
 | 			for(; m && strcmp(save, m->name) != 0; m=m->prev); | 
 | 			free(save); | 
 | 			if(m == nil) | 
 | 				break; | 
 | 			fsprint(sbox->body, "%s%s\n", m->name, info(m, 0, 0)); | 
 | 			m = m->prev; | 
 | 		} | 
 | 		free(res); | 
 | 		winclean(sbox); | 
 | 		winclosebody(sbox); | 
 | 		return 1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | void | 
 | mainctl(void *v) | 
 | { | 
 | 	Window *w; | 
 | 	Event *e, *e2, *eq, *ea; | 
 | 	int na, nopen; | 
 | 	char *s, *t, *buf; | 
 |  | 
 | 	w = v; | 
 | 	winincref(w); | 
 | 	proccreate(wineventproc, w, STACK); | 
 |  | 
 | 	for(;;){ | 
 | 		e = recvp(w->cevent); | 
 | 		switch(e->c1){ | 
 | 		default: | 
 | 		Unknown: | 
 | 			print("unknown message %c%c\n", e->c1, e->c2); | 
 | 			break; | 
 | 	 | 
 | 		case 'E':	/* write to body; can't affect us */ | 
 | 			break; | 
 | 	 | 
 | 		case 'F':	/* generated by our actions; ignore */ | 
 | 			break; | 
 | 	 | 
 | 		case 'K':	/* type away; we don't care */ | 
 | 			break; | 
 | 	 | 
 | 		case 'M': | 
 | 			switch(e->c2){ | 
 | 			case 'x': | 
 | 			case 'X': | 
 | 				ea = nil; | 
 | 				e2 = nil; | 
 | 				if(e->flag & 2) | 
 | 					e2 = recvp(w->cevent); | 
 | 				if(e->flag & 8){ | 
 | 					ea = recvp(w->cevent); | 
 | 					na = ea->nb; | 
 | 					recvp(w->cevent); | 
 | 				}else | 
 | 					na = 0; | 
 | 				s = e->b; | 
 | 				/* if it's a known command, do it */ | 
 | 				if((e->flag&2) && e->nb==0) | 
 | 					s = e2->b; | 
 | 				if(na){ | 
 | 					t = emalloc(strlen(s)+1+na+1); | 
 | 					sprint(t, "%s %s", s, ea->b); | 
 | 					s = t; | 
 | 				} | 
 | 				/* if it's a long message, it can't be for us anyway */ | 
 | 				if(!mboxcommand(w, s))	/* send it back */ | 
 | 					winwriteevent(w, e); | 
 | 				if(na) | 
 | 					free(s); | 
 | 				break; | 
 | 	 | 
 | 			case 'l': | 
 | 			case 'L': | 
 | 				buf = nil; | 
 | 				eq = e; | 
 | 				if(e->flag & 2){ | 
 | 					e2 = recvp(w->cevent); | 
 | 					eq = e2; | 
 | 				} | 
 | 				s = eq->b; | 
 | 				if(eq->q1>eq->q0 && eq->nb==0){ | 
 | 					buf = emalloc((eq->q1-eq->q0)*UTFmax+1); | 
 | 					winread(w, eq->q0, eq->q1, buf); | 
 | 					s = buf; | 
 | 				} | 
 | 				nopen = 0; | 
 | 				do{ | 
 | 					/* skip 'deleted' string if present' */ | 
 | 					if(strncmp(s, deleted, strlen(deleted)) == 0) | 
 | 						s += strlen(deleted); | 
 | 					/* skip mail box name if present */ | 
 | 					if(strncmp(s, mbox.name, strlen(mbox.name)) == 0) | 
 | 						s += strlen(mbox.name); | 
 | 					nopen += mesgopen(&mbox, mbox.name, s, nil, 0, nil); | 
 | 					while(*s!='\0' && *s++!='\n') | 
 | 						; | 
 | 				}while(*s); | 
 | 				if(nopen == 0)	/* send it back */ | 
 | 					winwriteevent(w, e); | 
 | 				free(buf); | 
 | 				break; | 
 | 	 | 
 | 			case 'I':	/* modify away; we don't care */ | 
 | 			case 'D': | 
 | 			case 'd': | 
 | 			case 'i': | 
 | 				break; | 
 | 	 | 
 | 			default: | 
 | 				goto Unknown; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  |