421 lines · 8.4 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <bio.h>#include <auth.h>#include "imap4d.h" #define SUBSCRIBED	"imap.subscribed" static int	matches(char *ref, char *pat, char *name);static int	mayMatch(char *pat, char *name, int star);static int	checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir);static int	listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime);static int	listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm);static int	mkSubscribed(void); static longlistMtime(char *file){	Dir *d;	long mtime; 	d = cdDirstat(mboxDir, file);	if(d == nil)		return 0;	mtime = d->mtime;	free(d);	return mtime;} /* * check for subscribed mailboxes * each line is either a comment starting with # * or is a subscribed mailbox name */intlsubBoxes(char *cmd, char *ref, char *pat){	MbLock *mb;	Dir *d;	Biobuf bin;	char *s;	long mtime;	int fd, ok, isdir; 	mb = mbLock();	if(mb == nil)		return 0;	fd = cdOpen(mboxDir, SUBSCRIBED, OREAD);	if(fd < 0)		fd = mkSubscribed();	if(fd < 0){		mbUnlock(mb);		return 0;	}	ok = 0;	Binit(&bin, fd, OREAD);	while(s = Brdline(&bin, '\n')){		s[Blinelen(&bin) - 1] = '\0';		if(s[0] == '#')			continue;		isdir = 1;		if(cistrcmp(s, "INBOX") == 0){			if(access("msgs", AEXIST) == 0)				mtime = listMtime("msgs");			else				mtime = listMtime("mbox");			isdir = 0;		}else{			d = cdDirstat(mboxDir, s);			if(d != nil){				mtime = d->mtime;				if(!(d->mode & DMDIR))					isdir = 0;				free(d);			}else				mtime = 0;		}		ok |= checkMatch(cmd, ref, pat, s, mtime, isdir);	}	Bterm(&bin);	close(fd);	mbUnlock(mb);	return ok;} static intmkSubscribed(void){	int fd; 	fd = cdCreate(mboxDir, SUBSCRIBED, ORDWR, 0664);	if(fd < 0)		return -1;	fprint(fd, "#imap4 subscription list\nINBOX\n");	seek(fd, 0, 0);	return fd;} /* * either subscribe or unsubscribe to a mailbox */intsubscribe(char *mbox, int how){	MbLock *mb;	char *s, *in, *ein;	int fd, tfd, ok, nmbox; 	if(cistrcmp(mbox, "inbox") == 0)		mbox = "INBOX";	mb = mbLock();	if(mb == nil)		return 0;	fd = cdOpen(mboxDir, SUBSCRIBED, ORDWR);	if(fd < 0)		fd = mkSubscribed();	if(fd < 0){		mbUnlock(mb);		return 0;	}	in = readFile(fd);	if(in == nil){		mbUnlock(mb);		return 0;	}	nmbox = strlen(mbox);	s = strstr(in, mbox);	while(s != nil && (s != in && s[-1] != '\n' || s[nmbox] != '\n'))		s = strstr(s+1, mbox);	ok = 0;	if(how == 's' && s == nil){		if(fprint(fd, "%s\n", mbox) > 0)			ok = 1;	}else if(how == 'u' && s != nil){		ein = strchr(s, '\0');		memmove(s, &s[nmbox+1], ein - &s[nmbox+1]);		ein -= nmbox+1;		tfd = cdOpen(mboxDir, SUBSCRIBED, OWRITE|OTRUNC);		if(tfd >= 0 && seek(fd, 0, 0) >= 0 && write(fd, in, ein-in) == ein-in)			ok = 1;		if(tfd > 0)			close(tfd);	}else		ok = 1;	close(fd);	mbUnlock(mb);	return ok;} /* * stupidly complicated so that % doesn't read entire directory structure * yet * works * note: in most places, inbox is case-insensitive, * but here INBOX is checked for a case-sensitve match. */intlistBoxes(char *cmd, char *ref, char *pat){	int ok; 	ok = checkMatch(cmd, ref, pat, "INBOX", listMtime("mbox"), 0);	return ok | listMatch(cmd, ref, pat, ref, pat);} /* * look for all messages which may match the pattern * punt when a * is reached */static intlistMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm){	Dir *dir, *dirs;	char *mdir, *m, *mb, *wc;	long mode;	int c, i, nmb, nmdir, nd, ok, fd; 	mdir = nil;	for(m = mm; c = *m; m++){		if(c == '%' || c == '*'){			if(mdir == nil){				fd = cdOpen(mboxDir, ".", OREAD);				if(fd < 0)					return 0;				mbox = "";				nmdir = 0;			}else{				*mdir = '\0';				fd = cdOpen(mboxDir, mbox, OREAD);				*mdir = '/';				nmdir = mdir - mbox + 1;				if(fd < 0)					return 0;				dir = dirfstat(fd);				if(dir == nil){					close(fd);					return 0;				}				mode = dir->mode;				free(dir);				if(!(mode & DMDIR))					break;			}			wc = m;			for(; c = *m; m++)				if(c == '/')					break;			nmb = nmdir + strlen(m) + MboxNameLen + 3;			mb = emalloc(nmb);			strncpy(mb, mbox, nmdir);			ok = 0;			while((nd = dirread(fd, &dirs)) > 0){				for(i = 0; i < nd; i++){					if(strcmp(mbox, "") == 0 &&					    !okMbox(dirs[i].name))						continue;					/* Safety: ignore message dirs */					if(strstr(dirs[i].name, "mails") != 0 ||					   strcmp(dirs[i].name, "out") == 0 ||					   strcmp(dirs[i].name, "obox") == 0 ||					   strcmp(dirs[i].name, "ombox") == 0)						continue;					if(strcmp(dirs[i].name, "msgs") == 0)						dirs[i].mode &= ~DMDIR;					if(*wc == '*' && dirs[i].mode & DMDIR &&					    mayMatch(mm, dirs[i].name, 1)){						snprint(mb+nmdir, nmb-nmdir,							"%s", dirs[i].name);						ok |= listAll(cmd, ref, pat, mb,							dirs[i].mtime);					}else if(mayMatch(mm, dirs[i].name, 0)){						snprint(mb+nmdir, nmb-nmdir,							"%s%s", dirs[i].name, m);						if(*m == '\0')							ok |= checkMatch(cmd,								ref, pat, mb,								dirs[i].mtime,								dirs[i].mode &								DMDIR);						else if(dirs[i].mode & DMDIR)							ok |= listMatch(cmd,								ref, pat, mb, mb								+ nmdir + strlen(								dirs[i].name));					}				}				free(dirs);			}			close(fd);			free(mb);			return ok;		}		if(c == '/'){			mdir = m;			mm = m + 1;		}	}	m = mbox;	if(*mbox == '\0')		m = ".";	dir = cdDirstat(mboxDir, m);	if(dir == nil)		return 0;	ok = checkMatch(cmd, ref, pat, mbox, dir->mtime, (dir->mode & DMDIR) == DMDIR);	free(dir);	return ok;} /* * too hard: recursively list all files rooted at mbox, * and list checkMatch figure it out */static intlistAll(char *cmd, char *ref, char *pat, char *mbox, long mtime){	Dir *dirs;	char *mb;	int i, nmb, nd, ok, fd; 	ok = checkMatch(cmd, ref, pat, mbox, mtime, 1);	fd = cdOpen(mboxDir, mbox, OREAD);	if(fd < 0)		return ok; 	nmb = strlen(mbox) + MboxNameLen + 2;	mb = emalloc(nmb);	while((nd = dirread(fd, &dirs)) > 0){		for(i = 0; i < nd; i++){			snprint(mb, nmb, "%s/%s", mbox, dirs[i].name);			/* safety: do not recurr */			if(0 && dirs[i].mode & DMDIR)				ok |= listAll(cmd, ref, pat, mb, dirs[i].mtime);			else				ok |= checkMatch(cmd, ref, pat, mb, dirs[i].mtime, 0);		}		free(dirs);	}	close(fd);	free(mb);	return ok;} static intmayMatch(char *pat, char *name, int star){	Rune r;	int i, n; 	for(; *pat && *pat != '/'; pat += n){		r = *(uchar*)pat;		if(r < Runeself)			n = 1;		else			n = chartorune(&r, pat); 		if(r == '*' || r == '%'){			pat += n;			if(r == '*' && star || *pat == '\0' || *pat == '/')				return 1;			while(*name){				if(mayMatch(pat, name, star))					return 1;				name += chartorune(&r, name);			}			return 0;		}		for(i = 0; i < n; i++)			if(name[i] != pat[i])				return 0;		name += n;	}	if(*name == '\0')		return 1;	return 0;} /* * mbox is a mailbox name which might match pat. * verify the match * generates response */static intcheckMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir){	char *s, *flags; 	if(!matches(ref, pat, mbox) || !okMbox(mbox))		return 0;	if(strcmp(mbox, ".") == 0)		mbox = ""; 	if(isdir)		flags = "(\\Noselect)";	else{		s = impName(mbox);		if(s != nil && listMtime(s) < mtime)			flags = "(\\Noinferiors \\Marked)";		else			flags = "(\\Noinferiors)";	} 	s = strmutf7(mbox);	if(s != nil)		Bprint(&bout, "* %s %s \"/\" \"%s\"\r\n", cmd, flags, s);	return 1;} static intmatches(char *ref, char *pat, char *name){	Rune r;	int i, n; 	while(ref != pat)		if(*name++ != *ref++)			return 0;	for(; *pat; pat += n){		r = *(uchar*)pat;		if(r < Runeself)			n = 1;		else			n = chartorune(&r, pat); 		if(r == '*'){			pat += n;			if(*pat == '\0')				return 1;			while(*name){				if(matches(pat, pat, name))					return 1;				name += chartorune(&r, name);			}			return 0;		}		if(r == '%'){			pat += n;			while(*name && *name != '/'){				if(matches(pat, pat, name))					return 1;				name += chartorune(&r, name);			}			pat -= n;			continue;		}		for(i = 0; i < n; i++)			if(name[i] != pat[i])				return 0;		name += n;	}	if(*name == '\0')		return 1;	return 0;}