xref: /plan9/sys/src/cmd/ip/imap4d/mbox.c (revision 8cf6001e50e647a07ccf484b8e2f9940411befb9)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <auth.h>
57dd7cddfSDavid du Colombier #include "imap4d.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier static NamedInt	flagChars[NFlags] =
87dd7cddfSDavid du Colombier {
97dd7cddfSDavid du Colombier 	{"s",	MSeen},
107dd7cddfSDavid du Colombier 	{"a",	MAnswered},
117dd7cddfSDavid du Colombier 	{"f",	MFlagged},
127dd7cddfSDavid du Colombier 	{"D",	MDeleted},
137dd7cddfSDavid du Colombier 	{"d",	MDraft},
147dd7cddfSDavid du Colombier 	{"r",	MRecent},
157dd7cddfSDavid du Colombier };
167dd7cddfSDavid du Colombier 
177dd7cddfSDavid du Colombier static	int	fsCtl = -1;
187dd7cddfSDavid du Colombier 
197dd7cddfSDavid du Colombier static	void	boxFlags(Box *box);
209a747e4fSDavid du Colombier static	int	createImp(Box *box, Qid *qid);
217dd7cddfSDavid du Colombier static	void	fsInit(void);
227dd7cddfSDavid du Colombier static	void	mboxGone(Box *box);
237dd7cddfSDavid du Colombier static	MbLock	*openImp(Box *box, int new);
247dd7cddfSDavid du Colombier static	int	parseImp(Biobuf *b, Box *box);
257dd7cddfSDavid du Colombier static	int	readBox(Box *box);
267dd7cddfSDavid du Colombier static	ulong	uidRenumber(Msg *m, ulong uid, int force);
277dd7cddfSDavid du Colombier static	int	impFlags(Box *box, Msg *m, char *flags);
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier /*
307dd7cddfSDavid du Colombier  * strategy:
317dd7cddfSDavid du Colombier  * every mailbox file has an associated .imp file
327dd7cddfSDavid du Colombier  * which maps upas/fs message digests to uids & message flags.
337dd7cddfSDavid du Colombier  *
347dd7cddfSDavid du Colombier  * the .imp files are locked by /mail/fs/usename/L.mbox.
357dd7cddfSDavid du Colombier  * whenever the flags can be modified, the lock file
367dd7cddfSDavid du Colombier  * should be opened, thereby locking the uid & flag state.
377dd7cddfSDavid du Colombier  * for example, whenever new uids are assigned to messages,
387dd7cddfSDavid du Colombier  * and whenever flags are changed internally, the lock file
397dd7cddfSDavid du Colombier  * should be open and locked.  this means the file must be
407dd7cddfSDavid du Colombier  * opened during store command, and when changing the \seen
417dd7cddfSDavid du Colombier  * flag for the fetch command.
427dd7cddfSDavid du Colombier  *
437dd7cddfSDavid du Colombier  * if no .imp file exists, a null one must be created before
447dd7cddfSDavid du Colombier  * assigning uids.
457dd7cddfSDavid du Colombier  *
467dd7cddfSDavid du Colombier  * the .imp file has the following format
477dd7cddfSDavid du Colombier  * imp		: "imap internal mailbox description\n"
487dd7cddfSDavid du Colombier  * 			uidvalidity " " uidnext "\n"
497dd7cddfSDavid du Colombier  *			messageLines
507dd7cddfSDavid du Colombier  *
517dd7cddfSDavid du Colombier  * messageLines	:
527dd7cddfSDavid du Colombier  *		| messageLines digest " " uid " " flags "\n"
537dd7cddfSDavid du Colombier  *
547dd7cddfSDavid du Colombier  * uid, uidnext, and uidvalidity are 32 bit decimal numbers
557dd7cddfSDavid du Colombier  * printed right justified in a field NUid characters long.
567dd7cddfSDavid du Colombier  * the 0 uid implies that no uid has been assigned to the message,
577dd7cddfSDavid du Colombier  * but the flags are valid. note that message lines are in mailbox
587dd7cddfSDavid du Colombier  * order, except possibly for 0 uid messages.
597dd7cddfSDavid du Colombier  *
607dd7cddfSDavid du Colombier  * digest is an ascii hex string NDigest characters long.
617dd7cddfSDavid du Colombier  *
627dd7cddfSDavid du Colombier  * flags has a character for each of NFlag flag fields.
637dd7cddfSDavid du Colombier  * if the flag is clear, it is represented by a "-".
647dd7cddfSDavid du Colombier  * set flags are represented as a unique single ascii character.
657dd7cddfSDavid du Colombier  * the currently assigned flags are, in order:
667dd7cddfSDavid du Colombier  *	MSeen		s
677dd7cddfSDavid du Colombier  *	MAnswered	a
687dd7cddfSDavid du Colombier  *	MFlagged	f
697dd7cddfSDavid du Colombier  *	MDeleted	D
707dd7cddfSDavid du Colombier  *	MDraft		d
717dd7cddfSDavid du Colombier  */
727dd7cddfSDavid du Colombier Box*
openBox(char * name,char * fsname,int writable)737dd7cddfSDavid du Colombier openBox(char *name, char *fsname, int writable)
747dd7cddfSDavid du Colombier {
757dd7cddfSDavid du Colombier 	Box *box;
767dd7cddfSDavid du Colombier 	MbLock *ml;
777dd7cddfSDavid du Colombier 	int n, new;
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier 	if(cistrcmp(name, "inbox") == 0)
8043aadf5eSDavid du Colombier 		if(access("msgs", AEXIST) == 0)
8143aadf5eSDavid du Colombier 			name = "msgs";
8243aadf5eSDavid du Colombier 		else
837dd7cddfSDavid du Colombier 			name = "mbox";
847dd7cddfSDavid du Colombier 	fsInit();
8543aadf5eSDavid du Colombier 	debuglog("imap4d open %s %s\n", name, fsname);
8643aadf5eSDavid du Colombier 
87*8cf6001eSDavid du Colombier 	if(fprint(fsCtl, "open '/mail/box/%s/%s' %s", username, name, fsname) < 0){
8880ee5cbfSDavid du Colombier //ZZZ
899a747e4fSDavid du Colombier 		char err[ERRMAX];
906e955fcaSDavid du Colombier 
91b8914692SDavid du Colombier 		rerrstr(err, sizeof err);
929a747e4fSDavid du Colombier 		if(strstr(err, "file does not exist") == nil)
936e955fcaSDavid du Colombier 			fprint(2,
946e955fcaSDavid du Colombier 		"imap4d at %lud: upas/fs open %s/%s as %s failed: '%s' %s",
956e955fcaSDavid du Colombier 			time(nil), username, name, fsname, err,
966e955fcaSDavid du Colombier 			ctime(time(nil)));  /* NB: ctime result ends with \n */
977dd7cddfSDavid du Colombier 		fprint(fsCtl, "close %s", fsname);
987dd7cddfSDavid du Colombier 		return nil;
997dd7cddfSDavid du Colombier 	}
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier 	/*
1027dd7cddfSDavid du Colombier 	 * read box to find all messages
1037dd7cddfSDavid du Colombier 	 * each one has a directory, and is in numerical order
1047dd7cddfSDavid du Colombier 	 */
1057dd7cddfSDavid du Colombier 	box = MKZ(Box);
1067dd7cddfSDavid du Colombier 	box->writable = writable;
1077dd7cddfSDavid du Colombier 
1087dd7cddfSDavid du Colombier 	n = strlen(name) + 1;
1097dd7cddfSDavid du Colombier 	box->name = emalloc(n);
1107dd7cddfSDavid du Colombier 	strcpy(box->name, name);
1117dd7cddfSDavid du Colombier 
1127dd7cddfSDavid du Colombier 	n += STRLEN(".imp");
1137dd7cddfSDavid du Colombier 	box->imp = emalloc(n);
1147dd7cddfSDavid du Colombier 	snprint(box->imp, n, "%s.imp", name);
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier 	n = strlen(fsname) + 1;
1177dd7cddfSDavid du Colombier 	box->fs = emalloc(n);
1187dd7cddfSDavid du Colombier 	strcpy(box->fs, fsname);
1197dd7cddfSDavid du Colombier 
1207dd7cddfSDavid du Colombier 	n = STRLEN("/mail/fs/") + strlen(fsname) + 1;
1217dd7cddfSDavid du Colombier 	box->fsDir = emalloc(n);
1227dd7cddfSDavid du Colombier 	snprint(box->fsDir, n, "/mail/fs/%s", fsname);
1237dd7cddfSDavid du Colombier 
1247dd7cddfSDavid du Colombier 	box->uidnext = 1;
1257dd7cddfSDavid du Colombier 	new = readBox(box);
1267dd7cddfSDavid du Colombier 	if(new >= 0){
1277dd7cddfSDavid du Colombier 		ml = openImp(box, new);
1287dd7cddfSDavid du Colombier 		if(ml != nil){
1297dd7cddfSDavid du Colombier 			closeImp(box, ml);
1307dd7cddfSDavid du Colombier 			return box;
1317dd7cddfSDavid du Colombier 		}
1327dd7cddfSDavid du Colombier 	}
1337dd7cddfSDavid du Colombier 	closeBox(box, 0);
1347dd7cddfSDavid du Colombier 	return nil;
1357dd7cddfSDavid du Colombier }
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier /*
1387dd7cddfSDavid du Colombier  * check mailbox
1397dd7cddfSDavid du Colombier  * returns fd of open .imp file if imped.
1407dd7cddfSDavid du Colombier  * otherwise, return value is insignificant
1417dd7cddfSDavid du Colombier  *
1427dd7cddfSDavid du Colombier  * careful: called by idle polling proc
1437dd7cddfSDavid du Colombier  */
1447dd7cddfSDavid du Colombier MbLock*
checkBox(Box * box,int imped)1457dd7cddfSDavid du Colombier checkBox(Box *box, int imped)
1467dd7cddfSDavid du Colombier {
1477dd7cddfSDavid du Colombier 	MbLock *ml;
1489a747e4fSDavid du Colombier 	Dir *d;
1497dd7cddfSDavid du Colombier 	int new;
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier 	if(box == nil)
1527dd7cddfSDavid du Colombier 		return nil;
1537dd7cddfSDavid du Colombier 
1547dd7cddfSDavid du Colombier 	/*
1557dd7cddfSDavid du Colombier 	 * if stat fails, mailbox must be gone
1567dd7cddfSDavid du Colombier 	 */
1579a747e4fSDavid du Colombier 	d = cdDirstat(box->fsDir, ".");
1589a747e4fSDavid du Colombier 	if(d == nil){
1597dd7cddfSDavid du Colombier 		mboxGone(box);
1607dd7cddfSDavid du Colombier 		return nil;
1617dd7cddfSDavid du Colombier 	}
1627dd7cddfSDavid du Colombier 	new = 0;
1639a747e4fSDavid du Colombier 	if(box->qid.path != d->qid.path || box->qid.vers != d->qid.vers
1649a747e4fSDavid du Colombier 	|| box->mtime != d->mtime){
1657dd7cddfSDavid du Colombier 		new = readBox(box);
1669a747e4fSDavid du Colombier 		if(new < 0){
1679a747e4fSDavid du Colombier 			free(d);
1687dd7cddfSDavid du Colombier 			return nil;
1697dd7cddfSDavid du Colombier 		}
1709a747e4fSDavid du Colombier 	}
1719a747e4fSDavid du Colombier 	free(d);
1727dd7cddfSDavid du Colombier 	ml = openImp(box, new);
1737dd7cddfSDavid du Colombier 	if(ml == nil)
1747dd7cddfSDavid du Colombier 		box->writable = 0;
1757dd7cddfSDavid du Colombier 	else if(!imped){
1767dd7cddfSDavid du Colombier 		closeImp(box, ml);
1777dd7cddfSDavid du Colombier 		ml = nil;
1787dd7cddfSDavid du Colombier 	}
1797dd7cddfSDavid du Colombier 	return ml;
1807dd7cddfSDavid du Colombier }
1817dd7cddfSDavid du Colombier 
1827dd7cddfSDavid du Colombier /*
1837dd7cddfSDavid du Colombier  * mailbox is unreachable, so mark all messages expunged
1847dd7cddfSDavid du Colombier  * clean up .imp files as well.
1857dd7cddfSDavid du Colombier  */
1867dd7cddfSDavid du Colombier static void
mboxGone(Box * box)1877dd7cddfSDavid du Colombier mboxGone(Box *box)
1887dd7cddfSDavid du Colombier {
1897dd7cddfSDavid du Colombier 	Msg *m;
1907dd7cddfSDavid du Colombier 
1919a747e4fSDavid du Colombier 	if(cdExists(mboxDir, box->name) < 0)
1927dd7cddfSDavid du Colombier 		cdRemove(mboxDir, box->imp);
1937dd7cddfSDavid du Colombier 	for(m = box->msgs; m != nil; m = m->next)
1947dd7cddfSDavid du Colombier 		m->expunged = 1;
1957dd7cddfSDavid du Colombier 	box->writable = 0;
1967dd7cddfSDavid du Colombier }
1977dd7cddfSDavid du Colombier 
1987dd7cddfSDavid du Colombier /*
1997dd7cddfSDavid du Colombier  * read messages in the mailbox
2007dd7cddfSDavid du Colombier  * mark message that no longer exist as expunged
2017dd7cddfSDavid du Colombier  * returns -1 for failure, 0 if no new messages, 1 if new messages.
2027dd7cddfSDavid du Colombier  */
2037dd7cddfSDavid du Colombier static int
readBox(Box * box)2047dd7cddfSDavid du Colombier readBox(Box *box)
2057dd7cddfSDavid du Colombier {
2067dd7cddfSDavid du Colombier 	Msg *msgs, *m, *last;
2079a747e4fSDavid du Colombier 	Dir *d;
2087dd7cddfSDavid du Colombier 	char *s;
2097dd7cddfSDavid du Colombier 	long max, id;
2109a747e4fSDavid du Colombier 	int i, nd, fd, new;
2117dd7cddfSDavid du Colombier 
2127dd7cddfSDavid du Colombier 	fd = cdOpen(box->fsDir, ".", OREAD);
2137dd7cddfSDavid du Colombier 	if(fd < 0){
2146e955fcaSDavid du Colombier 		syslog(0, "mail",
2156e955fcaSDavid du Colombier 		    "imap4d at %lud: upas/fs stat of %s/%s aka %s failed: %r",
2166e955fcaSDavid du Colombier 			time(nil), username, box->name, box->fsDir);
2177dd7cddfSDavid du Colombier 		mboxGone(box);
2187dd7cddfSDavid du Colombier 		return -1;
2197dd7cddfSDavid du Colombier 	}
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier 	/*
2227dd7cddfSDavid du Colombier 	 * read box to find all messages
2237dd7cddfSDavid du Colombier 	 * each one has a directory, and is in numerical order
2247dd7cddfSDavid du Colombier 	 */
2259a747e4fSDavid du Colombier 	d = dirfstat(fd);
2269a747e4fSDavid du Colombier 	if(d == nil){
2277dd7cddfSDavid du Colombier 		close(fd);
2287dd7cddfSDavid du Colombier 		return -1;
2297dd7cddfSDavid du Colombier 	}
2307dd7cddfSDavid du Colombier 	box->mtime = d->mtime;
2317dd7cddfSDavid du Colombier 	box->qid = d->qid;
2327dd7cddfSDavid du Colombier 	last = nil;
2337dd7cddfSDavid du Colombier 	msgs = box->msgs;
2347dd7cddfSDavid du Colombier 	max = 0;
2357dd7cddfSDavid du Colombier 	new = 0;
2369a747e4fSDavid du Colombier 	free(d);
2379a747e4fSDavid du Colombier 	while((nd = dirread(fd, &d)) > 0){
2387dd7cddfSDavid du Colombier 		for(i = 0; i < nd; i++){
2397dd7cddfSDavid du Colombier 			s = d[i].name;
2407dd7cddfSDavid du Colombier 			id = strtol(s, &s, 10);
2417dd7cddfSDavid du Colombier 			if(id <= max || *s != '\0'
2429a747e4fSDavid du Colombier 			|| (d[i].mode & DMDIR) != DMDIR)
2437dd7cddfSDavid du Colombier 				continue;
2447dd7cddfSDavid du Colombier 
2457dd7cddfSDavid du Colombier 			max = id;
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier 			while(msgs != nil){
2487dd7cddfSDavid du Colombier 				last = msgs;
2497dd7cddfSDavid du Colombier 				msgs = msgs->next;
2507dd7cddfSDavid du Colombier 				if(last->id == id)
2517dd7cddfSDavid du Colombier 					goto continueDir;
2527dd7cddfSDavid du Colombier 				last->expunged = 1;
2537dd7cddfSDavid du Colombier 			}
2547dd7cddfSDavid du Colombier 
2557dd7cddfSDavid du Colombier 			new = 1;
2567dd7cddfSDavid du Colombier 			m = MKZ(Msg);
2577dd7cddfSDavid du Colombier 			m->id = id;
2587dd7cddfSDavid du Colombier 			m->fsDir = box->fsDir;
2599a747e4fSDavid du Colombier 			m->fs = emalloc(2 * (MsgNameLen + 1));
2609a747e4fSDavid du Colombier 			m->efs = seprint(m->fs, m->fs + (MsgNameLen + 1), "%lud/", id);
2617dd7cddfSDavid du Colombier 			m->size = ~0UL;
2627dd7cddfSDavid du Colombier 			m->lines = ~0UL;
2637dd7cddfSDavid du Colombier 			m->prev = last;
2647dd7cddfSDavid du Colombier 			m->flags = MRecent;
2657dd7cddfSDavid du Colombier 			if(!msgInfo(m))
2667dd7cddfSDavid du Colombier 				freeMsg(m);
2677dd7cddfSDavid du Colombier 			else{
2687dd7cddfSDavid du Colombier 				if(last == nil)
2697dd7cddfSDavid du Colombier 					box->msgs = m;
2707dd7cddfSDavid du Colombier 				else
2717dd7cddfSDavid du Colombier 					last->next = m;
2727dd7cddfSDavid du Colombier 				last = m;
2737dd7cddfSDavid du Colombier 			}
2747dd7cddfSDavid du Colombier 	continueDir:;
2757dd7cddfSDavid du Colombier 		}
2769a747e4fSDavid du Colombier 		free(d);
2777dd7cddfSDavid du Colombier 	}
2787dd7cddfSDavid du Colombier 	close(fd);
2797dd7cddfSDavid du Colombier 	for(; msgs != nil; msgs = msgs->next)
2807dd7cddfSDavid du Colombier 		msgs->expunged = 1;
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier 	/*
2837dd7cddfSDavid du Colombier 	 * make up the imap message sequence numbers
2847dd7cddfSDavid du Colombier 	 */
2857dd7cddfSDavid du Colombier 	id = 1;
2867dd7cddfSDavid du Colombier 	for(m = box->msgs; m != nil; m = m->next){
2877dd7cddfSDavid du Colombier 		if(m->seq && m->seq != id)
2887dd7cddfSDavid du Colombier 			bye("internal error assigning message numbers");
2897dd7cddfSDavid du Colombier 		m->seq = id++;
2907dd7cddfSDavid du Colombier 	}
2917dd7cddfSDavid du Colombier 	box->max = id - 1;
2927dd7cddfSDavid du Colombier 
2937dd7cddfSDavid du Colombier 	return new;
2947dd7cddfSDavid du Colombier }
2957dd7cddfSDavid du Colombier 
2967dd7cddfSDavid du Colombier /*
2977dd7cddfSDavid du Colombier  * read in the .imp file, or make one if it doesn't exist.
2987dd7cddfSDavid du Colombier  * make sure all flags and uids are consistent.
2997dd7cddfSDavid du Colombier  * return the mailbox lock.
3007dd7cddfSDavid du Colombier  */
3017dd7cddfSDavid du Colombier #define IMPMAGIC	"imap internal mailbox description\n"
3027dd7cddfSDavid du Colombier static MbLock*
openImp(Box * box,int new)3037dd7cddfSDavid du Colombier openImp(Box *box, int new)
3047dd7cddfSDavid du Colombier {
3059a747e4fSDavid du Colombier 	Qid qid;
3067dd7cddfSDavid du Colombier 	Biobuf b;
3077dd7cddfSDavid du Colombier 	MbLock *ml;
3087dd7cddfSDavid du Colombier 	int fd;
30980ee5cbfSDavid du Colombier //ZZZZ
31080ee5cbfSDavid du Colombier 	int once;
3117dd7cddfSDavid du Colombier 
3127dd7cddfSDavid du Colombier 	ml = mbLock();
3137dd7cddfSDavid du Colombier 	if(ml == nil)
3147dd7cddfSDavid du Colombier 		return nil;
3157dd7cddfSDavid du Colombier 	fd = cdOpen(mboxDir, box->imp, OREAD);
31680ee5cbfSDavid du Colombier 	once = 0;
31780ee5cbfSDavid du Colombier ZZZhack:
3189a747e4fSDavid du Colombier 	if(fd < 0 || fqid(fd, &qid) < 0){
31980ee5cbfSDavid du Colombier 		if(fd < 0){
3209a747e4fSDavid du Colombier 			char buf[ERRMAX];
32180ee5cbfSDavid du Colombier 
3229a747e4fSDavid du Colombier 			errstr(buf, sizeof buf);
32380ee5cbfSDavid du Colombier 			if(cistrstr(buf, "does not exist") == nil)
3249a747e4fSDavid du Colombier 				fprint(2, "imap4d at %lud: imp open failed: %s\n", time(nil), buf);
32580ee5cbfSDavid du Colombier 			if(!once && cistrstr(buf, "locked") != nil){
32680ee5cbfSDavid du Colombier 				once = 1;
3279a747e4fSDavid du Colombier 				fprint(2, "imap4d at %lud: imp %s/%s %s locked when it shouldn't be; spinning\n", time(nil), username, box->name, box->imp);
32880ee5cbfSDavid du Colombier 				fd = openLocked(mboxDir, box->imp, OREAD);
32980ee5cbfSDavid du Colombier 				goto ZZZhack;
33080ee5cbfSDavid du Colombier 			}
33180ee5cbfSDavid du Colombier 		}
3327dd7cddfSDavid du Colombier 		if(fd >= 0)
3337dd7cddfSDavid du Colombier 			close(fd);
3349a747e4fSDavid du Colombier 		fd = createImp(box, &qid);
3357dd7cddfSDavid du Colombier 		if(fd < 0){
3367dd7cddfSDavid du Colombier 			mbUnlock(ml);
3377dd7cddfSDavid du Colombier 			return nil;
3387dd7cddfSDavid du Colombier 		}
3397dd7cddfSDavid du Colombier 		box->dirtyImp = 1;
3407dd7cddfSDavid du Colombier 		if(box->uidvalidity == 0)
3417dd7cddfSDavid du Colombier 			box->uidvalidity = box->mtime;
3429a747e4fSDavid du Colombier 		box->impQid = qid;
3437dd7cddfSDavid du Colombier 		new = 1;
3449a747e4fSDavid du Colombier 	}else if(qid.path != box->impQid.path || qid.vers != box->impQid.vers){
3457dd7cddfSDavid du Colombier 		Binit(&b, fd, OREAD);
3467dd7cddfSDavid du Colombier 		if(!parseImp(&b, box)){
3477dd7cddfSDavid du Colombier 			box->dirtyImp = 1;
3487dd7cddfSDavid du Colombier 			if(box->uidvalidity == 0)
3497dd7cddfSDavid du Colombier 				box->uidvalidity = box->mtime;
3507dd7cddfSDavid du Colombier 		}
3517dd7cddfSDavid du Colombier 		Bterm(&b);
3529a747e4fSDavid du Colombier 		box->impQid = qid;
3537dd7cddfSDavid du Colombier 		new = 1;
3547dd7cddfSDavid du Colombier 	}
3557dd7cddfSDavid du Colombier 	if(new)
3567dd7cddfSDavid du Colombier 		boxFlags(box);
3577dd7cddfSDavid du Colombier 	close(fd);
3587dd7cddfSDavid du Colombier 	return ml;
3597dd7cddfSDavid du Colombier }
3607dd7cddfSDavid du Colombier 
3617dd7cddfSDavid du Colombier /*
3627dd7cddfSDavid du Colombier  * close the .imp file, after writing out any changes
3637dd7cddfSDavid du Colombier  */
3647dd7cddfSDavid du Colombier void
closeImp(Box * box,MbLock * ml)3657dd7cddfSDavid du Colombier closeImp(Box *box, MbLock *ml)
3667dd7cddfSDavid du Colombier {
3677dd7cddfSDavid du Colombier 	Msg *m;
3689a747e4fSDavid du Colombier 	Qid qid;
3697dd7cddfSDavid du Colombier 	Biobuf b;
3707dd7cddfSDavid du Colombier 	char buf[NFlags+1];
3717dd7cddfSDavid du Colombier 	int fd;
3727dd7cddfSDavid du Colombier 
3737dd7cddfSDavid du Colombier 	if(ml == nil)
3747dd7cddfSDavid du Colombier 		return;
3757dd7cddfSDavid du Colombier 	if(!box->dirtyImp){
3767dd7cddfSDavid du Colombier 		mbUnlock(ml);
3777dd7cddfSDavid du Colombier 		return;
3787dd7cddfSDavid du Colombier 	}
3797dd7cddfSDavid du Colombier 
3807dd7cddfSDavid du Colombier 	fd = cdCreate(mboxDir, box->imp, OWRITE, 0664);
3817dd7cddfSDavid du Colombier 	if(fd < 0){
3827dd7cddfSDavid du Colombier 		mbUnlock(ml);
3837dd7cddfSDavid du Colombier 		return;
3847dd7cddfSDavid du Colombier 	}
3857dd7cddfSDavid du Colombier 	Binit(&b, fd, OWRITE);
3867dd7cddfSDavid du Colombier 
3877dd7cddfSDavid du Colombier 	box->dirtyImp = 0;
3887dd7cddfSDavid du Colombier 	Bprint(&b, "%s", IMPMAGIC);
3897dd7cddfSDavid du Colombier 	Bprint(&b, "%.*lud %.*lud\n", NUid, box->uidvalidity, NUid, box->uidnext);
3907dd7cddfSDavid du Colombier 	for(m = box->msgs; m != nil; m = m->next){
3917dd7cddfSDavid du Colombier 		if(m->expunged)
3927dd7cddfSDavid du Colombier 			continue;
3937dd7cddfSDavid du Colombier 		wrImpFlags(buf, m->flags, strcmp(box->fs, "imap") == 0);
3947dd7cddfSDavid du Colombier 		Bprint(&b, "%.*s %.*lud %s\n", NDigest, m->info[IDigest], NUid, m->uid, buf);
3957dd7cddfSDavid du Colombier 	}
3967dd7cddfSDavid du Colombier 	Bterm(&b);
3977dd7cddfSDavid du Colombier 
3989a747e4fSDavid du Colombier 	if(fqid(fd, &qid) >= 0)
3999a747e4fSDavid du Colombier 		box->impQid = qid;
4007dd7cddfSDavid du Colombier 	close(fd);
4017dd7cddfSDavid du Colombier 	mbUnlock(ml);
4027dd7cddfSDavid du Colombier }
4037dd7cddfSDavid du Colombier 
4047dd7cddfSDavid du Colombier void
wrImpFlags(char * buf,int flags,int killRecent)4057dd7cddfSDavid du Colombier wrImpFlags(char *buf, int flags, int killRecent)
4067dd7cddfSDavid du Colombier {
4077dd7cddfSDavid du Colombier 	int i;
4087dd7cddfSDavid du Colombier 
4097dd7cddfSDavid du Colombier 	for(i = 0; i < NFlags; i++){
4107dd7cddfSDavid du Colombier 		if((flags & flagChars[i].v)
4117dd7cddfSDavid du Colombier 		&& (flagChars[i].v != MRecent || !killRecent))
4127dd7cddfSDavid du Colombier 			buf[i] = flagChars[i].name[0];
4137dd7cddfSDavid du Colombier 		else
4147dd7cddfSDavid du Colombier 			buf[i] = '-';
4157dd7cddfSDavid du Colombier 	}
4167dd7cddfSDavid du Colombier 	buf[i] = '\0';
4177dd7cddfSDavid du Colombier }
4187dd7cddfSDavid du Colombier 
4197dd7cddfSDavid du Colombier int
emptyImp(char * mbox)4207dd7cddfSDavid du Colombier emptyImp(char *mbox)
4217dd7cddfSDavid du Colombier {
4229a747e4fSDavid du Colombier 	Dir *d;
4239a747e4fSDavid du Colombier 	long mode;
4247dd7cddfSDavid du Colombier 	int fd;
4257dd7cddfSDavid du Colombier 
4267dd7cddfSDavid du Colombier 	fd = cdCreate(mboxDir, impName(mbox), OWRITE, 0664);
4277dd7cddfSDavid du Colombier 	if(fd < 0)
4287dd7cddfSDavid du Colombier 		return -1;
4299a747e4fSDavid du Colombier 	d = cdDirstat(mboxDir, mbox);
4309a747e4fSDavid du Colombier 	if(d == nil){
4317dd7cddfSDavid du Colombier 		close(fd);
4327dd7cddfSDavid du Colombier 		return -1;
4337dd7cddfSDavid du Colombier 	}
4349a747e4fSDavid du Colombier 	fprint(fd, "%s%.*lud %.*lud\n", IMPMAGIC, NUid, d->mtime, NUid, 1UL);
4359a747e4fSDavid du Colombier 	mode = d->mode & 0777;
4369a747e4fSDavid du Colombier 	nulldir(d);
4379a747e4fSDavid du Colombier 	d->mode = mode;
4389a747e4fSDavid du Colombier 	dirfwstat(fd, d);
4399a747e4fSDavid du Colombier 	free(d);
4407dd7cddfSDavid du Colombier 	return fd;
4417dd7cddfSDavid du Colombier }
4427dd7cddfSDavid du Colombier 
4437dd7cddfSDavid du Colombier /*
4447dd7cddfSDavid du Colombier  * try to match permissions with mbox
4457dd7cddfSDavid du Colombier  */
4467dd7cddfSDavid du Colombier static int
createImp(Box * box,Qid * qid)4479a747e4fSDavid du Colombier createImp(Box *box, Qid *qid)
4487dd7cddfSDavid du Colombier {
4499a747e4fSDavid du Colombier 	Dir *d;
4509a747e4fSDavid du Colombier 	long mode;
4517dd7cddfSDavid du Colombier 	int fd;
4527dd7cddfSDavid du Colombier 
4537dd7cddfSDavid du Colombier 	fd = cdCreate(mboxDir, box->imp, OREAD, 0664);
4547dd7cddfSDavid du Colombier 	if(fd < 0)
4557dd7cddfSDavid du Colombier 		return -1;
4569a747e4fSDavid du Colombier 	d = cdDirstat(mboxDir, box->name);
4579a747e4fSDavid du Colombier 	if(d != nil){
4589a747e4fSDavid du Colombier 		mode = d->mode & 0777;
4599a747e4fSDavid du Colombier 		nulldir(d);
4609a747e4fSDavid du Colombier 		d->mode = mode;
4617dd7cddfSDavid du Colombier 		dirfwstat(fd, d);
4629a747e4fSDavid du Colombier 		free(d);
4637dd7cddfSDavid du Colombier 	}
4649a747e4fSDavid du Colombier 	if(fqid(fd, qid) < 0){
4657dd7cddfSDavid du Colombier 		close(fd);
4667dd7cddfSDavid du Colombier 		return -1;
4677dd7cddfSDavid du Colombier 	}
4687dd7cddfSDavid du Colombier 
4697dd7cddfSDavid du Colombier 	return fd;
4707dd7cddfSDavid du Colombier }
4717dd7cddfSDavid du Colombier 
4727dd7cddfSDavid du Colombier /*
4737dd7cddfSDavid du Colombier  * read or re-read a .imp file.
4747dd7cddfSDavid du Colombier  * this is tricky:
4757dd7cddfSDavid du Colombier  *	messages can be deleted by another agent
4767dd7cddfSDavid du Colombier  *	we might still have a Msg for an expunged message,
4777dd7cddfSDavid du Colombier  *		because we haven't told the client yet.
4787dd7cddfSDavid du Colombier  *	we can have a Msg without a .imp entry.
4797dd7cddfSDavid du Colombier  *	flag information is added at the end of the .imp by copy & append
4807dd7cddfSDavid du Colombier  *	there can be duplicate messages (same digests).
4817dd7cddfSDavid du Colombier  *
4827dd7cddfSDavid du Colombier  * look up existing messages based on uid.
4837dd7cddfSDavid du Colombier  * look up new messages based on in order digest matching.
4847dd7cddfSDavid du Colombier  *
4857dd7cddfSDavid du Colombier  * note: in the face of duplicate messages, one of which is deleted,
4867dd7cddfSDavid du Colombier  * two active servers may decide different ones are valid, and so return
4877dd7cddfSDavid du Colombier  * different uids for the messages.  this situation will stablize when the servers exit.
4887dd7cddfSDavid du Colombier  */
4897dd7cddfSDavid du Colombier static int
parseImp(Biobuf * b,Box * box)4907dd7cddfSDavid du Colombier parseImp(Biobuf *b, Box *box)
4917dd7cddfSDavid du Colombier {
4928da4fac9SDavid du Colombier 	Msg *m, *mm;
4937dd7cddfSDavid du Colombier 	char *s, *t, *toks[3];
4947dd7cddfSDavid du Colombier 	ulong uid, u;
4957dd7cddfSDavid du Colombier 	int match, n;
4967dd7cddfSDavid du Colombier 
4977dd7cddfSDavid du Colombier 	m = box->msgs;
4987dd7cddfSDavid du Colombier 	s = Brdline(b, '\n');
4997dd7cddfSDavid du Colombier 	if(s == nil || Blinelen(b) != STRLEN(IMPMAGIC)
5007dd7cddfSDavid du Colombier 	|| strncmp(s, IMPMAGIC, STRLEN(IMPMAGIC)) != 0)
5017dd7cddfSDavid du Colombier 		return 0;
5027dd7cddfSDavid du Colombier 
5037dd7cddfSDavid du Colombier 	s = Brdline(b, '\n');
5047dd7cddfSDavid du Colombier 	if(s == nil || Blinelen(b) != 2*NUid + 2)
5057dd7cddfSDavid du Colombier 		return 0;
5067dd7cddfSDavid du Colombier 	s[2*NUid + 1] = '\0';
5077dd7cddfSDavid du Colombier 	u = strtoul(s, &t, 10);
5087dd7cddfSDavid du Colombier 	if(u != box->uidvalidity && box->uidvalidity != 0)
5097dd7cddfSDavid du Colombier 		return 0;
5107dd7cddfSDavid du Colombier 	box->uidvalidity = u;
5117dd7cddfSDavid du Colombier 	if(*t != ' ' || t != s + NUid)
5127dd7cddfSDavid du Colombier 		return 0;
5137dd7cddfSDavid du Colombier 	t++;
5147dd7cddfSDavid du Colombier 	u = strtoul(t, &t, 10);
5157dd7cddfSDavid du Colombier 	if(box->uidnext > u)
5167dd7cddfSDavid du Colombier 		return 0;
5177dd7cddfSDavid du Colombier 	box->uidnext = u;
5187dd7cddfSDavid du Colombier 	if(t != s + 2*NUid+1 || box->uidnext == 0)
5197dd7cddfSDavid du Colombier 		return 0;
5207dd7cddfSDavid du Colombier 
5217dd7cddfSDavid du Colombier 	uid = ~0;
5227dd7cddfSDavid du Colombier 	while(m != nil){
5237dd7cddfSDavid du Colombier 		s = Brdline(b, '\n');
5247dd7cddfSDavid du Colombier 		if(s == nil)
5257dd7cddfSDavid du Colombier 			break;
5267dd7cddfSDavid du Colombier 		n = Blinelen(b) - 1;
5277dd7cddfSDavid du Colombier 		if(n != NDigest + NUid + NFlags + 2
5287dd7cddfSDavid du Colombier 		|| s[NDigest] != ' ' || s[NDigest + NUid + 1] != ' ')
5297dd7cddfSDavid du Colombier 			return 0;
5307dd7cddfSDavid du Colombier 		toks[0] = s;
5317dd7cddfSDavid du Colombier 		s[NDigest] = '\0';
5327dd7cddfSDavid du Colombier 		toks[1] = s + NDigest + 1;
5337dd7cddfSDavid du Colombier 		s[NDigest + NUid + 1] = '\0';
5347dd7cddfSDavid du Colombier 		toks[2] = s + NDigest + NUid + 2;
5357dd7cddfSDavid du Colombier 		s[n] = '\0';
5367dd7cddfSDavid du Colombier 		t = toks[1];
5377dd7cddfSDavid du Colombier 		u = strtoul(t, &t, 10);
5387dd7cddfSDavid du Colombier 		if(*t != '\0' || uid != ~0 && (uid >= u && u || u && !uid))
5397dd7cddfSDavid du Colombier 			return 0;
5407dd7cddfSDavid du Colombier 		uid = u;
5417dd7cddfSDavid du Colombier 
5427dd7cddfSDavid du Colombier 		/*
5437dd7cddfSDavid du Colombier 		 * zero uid => added by append or copy, only flags valid
5447dd7cddfSDavid du Colombier 		 * can only match messages without uids, but this message
5457dd7cddfSDavid du Colombier 		 * may not be the next one, and may have been deleted.
5467dd7cddfSDavid du Colombier 		 */
5477dd7cddfSDavid du Colombier 		if(!uid){
5487dd7cddfSDavid du Colombier 			for(; m != nil && m->uid; m = m->next)
5497dd7cddfSDavid du Colombier 				;
5507dd7cddfSDavid du Colombier 			for(mm = m; mm != nil; mm = mm->next){
551778cb651SDavid du Colombier 				if(mm->info[IDigest] != nil &&
552778cb651SDavid du Colombier 				    strcmp(mm->info[IDigest], toks[0]) == 0){
5537dd7cddfSDavid du Colombier 					if(!mm->uid)
5547dd7cddfSDavid du Colombier 						mm->flags = 0;
5557dd7cddfSDavid du Colombier 					if(!impFlags(box, mm, toks[2]))
5567dd7cddfSDavid du Colombier 						return 0;
5577dd7cddfSDavid du Colombier 					m = mm->next;
5587dd7cddfSDavid du Colombier 					break;
5597dd7cddfSDavid du Colombier 				}
5607dd7cddfSDavid du Colombier 			}
5617dd7cddfSDavid du Colombier 			continue;
5627dd7cddfSDavid du Colombier 		}
5637dd7cddfSDavid du Colombier 
5647dd7cddfSDavid du Colombier 		/*
5657dd7cddfSDavid du Colombier 		 * ignore expunged messages,
5667dd7cddfSDavid du Colombier 		 * and messages already assigned uids which don't match this uid.
5677dd7cddfSDavid du Colombier 		 * such messages must have been deleted by another imap server,
5687dd7cddfSDavid du Colombier 		 * which updated the mailbox and .imp file since we read the mailbox,
5697dd7cddfSDavid du Colombier 		 * or because upas/fs got confused by consecutive duplicate messages,
5707dd7cddfSDavid du Colombier 		 * the first of which was deleted by another imap server.
5717dd7cddfSDavid du Colombier 		 */
5727dd7cddfSDavid du Colombier 		for(; m != nil && (m->expunged || m->uid && m->uid < uid); m = m->next)
5737dd7cddfSDavid du Colombier 			;
5747dd7cddfSDavid du Colombier 		if(m == nil)
5757dd7cddfSDavid du Colombier 			break;
5767dd7cddfSDavid du Colombier 
5777dd7cddfSDavid du Colombier 		/*
5787dd7cddfSDavid du Colombier 		 * only check for digest match on the next message,
5797dd7cddfSDavid du Colombier 		 * since it comes before all other messages, and therefore
5807dd7cddfSDavid du Colombier 		 * must be in the .imp file if they should be.
5817dd7cddfSDavid du Colombier 		 */
5828da4fac9SDavid du Colombier 		match = m->info[IDigest] != nil &&
583778cb651SDavid du Colombier 			strcmp(m->info[IDigest], toks[0]) == 0;
5847dd7cddfSDavid du Colombier 		if(uid && (m->uid == uid || !m->uid && match)){
5857dd7cddfSDavid du Colombier 			if(!match)
5867dd7cddfSDavid du Colombier 				bye("inconsistent uid");
5877dd7cddfSDavid du Colombier 
5887dd7cddfSDavid du Colombier 			/*
5897dd7cddfSDavid du Colombier 			 * wipe out recent flag if some other server saw this new message.
5907dd7cddfSDavid du Colombier 			 * it will be read from the .imp file if is really should be set,
5917dd7cddfSDavid du Colombier 			 * ie the message was only seen by a status command.
5927dd7cddfSDavid du Colombier 			 */
5937dd7cddfSDavid du Colombier 			if(!m->uid)
5947dd7cddfSDavid du Colombier 				m->flags = 0;
5957dd7cddfSDavid du Colombier 
5967dd7cddfSDavid du Colombier 			if(!impFlags(box, m, toks[2]))
5977dd7cddfSDavid du Colombier 				return 0;
5987dd7cddfSDavid du Colombier 			m->uid = uid;
5997dd7cddfSDavid du Colombier 			m = m->next;
6007dd7cddfSDavid du Colombier 		}
6017dd7cddfSDavid du Colombier 	}
6027dd7cddfSDavid du Colombier 	return 1;
6037dd7cddfSDavid du Colombier }
6047dd7cddfSDavid du Colombier 
6057dd7cddfSDavid du Colombier /*
6067dd7cddfSDavid du Colombier  * parse .imp flags
6077dd7cddfSDavid du Colombier  */
6087dd7cddfSDavid du Colombier static int
impFlags(Box * box,Msg * m,char * flags)6097dd7cddfSDavid du Colombier impFlags(Box *box, Msg *m, char *flags)
6107dd7cddfSDavid du Colombier {
6117dd7cddfSDavid du Colombier 	int i, f;
6127dd7cddfSDavid du Colombier 
6137dd7cddfSDavid du Colombier 	f = 0;
6147dd7cddfSDavid du Colombier 	for(i = 0; i < NFlags; i++){
6157dd7cddfSDavid du Colombier 		if(flags[i] == '-')
6167dd7cddfSDavid du Colombier 			continue;
6177dd7cddfSDavid du Colombier 		if(flags[i] != flagChars[i].name[0])
6187dd7cddfSDavid du Colombier 			return 0;
6197dd7cddfSDavid du Colombier 		f |= flagChars[i].v;
6207dd7cddfSDavid du Colombier 	}
6217dd7cddfSDavid du Colombier 
6227dd7cddfSDavid du Colombier 	/*
6237dd7cddfSDavid du Colombier 	 * recent flags are set until the first time message's box is selected or examined.
6247dd7cddfSDavid du Colombier 	 * it may be stored in the file as a side effect of a status or subscribe command;
6257dd7cddfSDavid du Colombier 	 * if so, clear it out.
6267dd7cddfSDavid du Colombier 	 */
6277dd7cddfSDavid du Colombier 	if((f & MRecent) && strcmp(box->fs, "imap") == 0)
6287dd7cddfSDavid du Colombier 		box->dirtyImp = 1;
6297dd7cddfSDavid du Colombier 	f |= m->flags & MRecent;
6307dd7cddfSDavid du Colombier 
6317dd7cddfSDavid du Colombier 	/*
6327dd7cddfSDavid du Colombier 	 * all old messages with changed flags should be reported to the client
6337dd7cddfSDavid du Colombier 	 */
6347dd7cddfSDavid du Colombier 	if(m->uid && m->flags != f){
6357dd7cddfSDavid du Colombier 		box->sendFlags = 1;
6367dd7cddfSDavid du Colombier 		m->sendFlags = 1;
6377dd7cddfSDavid du Colombier 	}
6387dd7cddfSDavid du Colombier 	m->flags = f;
6397dd7cddfSDavid du Colombier 	return 1;
6407dd7cddfSDavid du Colombier }
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier /*
6437dd7cddfSDavid du Colombier  * assign uids to any new messages
6447dd7cddfSDavid du Colombier  * which aren't already in the .imp file.
6457dd7cddfSDavid du Colombier  * sum up totals for flag values.
6467dd7cddfSDavid du Colombier  */
6477dd7cddfSDavid du Colombier static void
boxFlags(Box * box)6487dd7cddfSDavid du Colombier boxFlags(Box *box)
6497dd7cddfSDavid du Colombier {
6507dd7cddfSDavid du Colombier 	Msg *m;
6517dd7cddfSDavid du Colombier 
6527dd7cddfSDavid du Colombier 	box->recent = 0;
6537dd7cddfSDavid du Colombier 	for(m = box->msgs; m != nil; m = m->next){
6547dd7cddfSDavid du Colombier 		if(m->uid == 0){
6557dd7cddfSDavid du Colombier 			box->dirtyImp = 1;
6567dd7cddfSDavid du Colombier 			box->uidnext = uidRenumber(m, box->uidnext, 0);
6577dd7cddfSDavid du Colombier 		}
6587dd7cddfSDavid du Colombier 		if(m->flags & MRecent)
6597dd7cddfSDavid du Colombier 			box->recent++;
6607dd7cddfSDavid du Colombier 	}
6617dd7cddfSDavid du Colombier }
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier static ulong
uidRenumber(Msg * m,ulong uid,int force)6647dd7cddfSDavid du Colombier uidRenumber(Msg *m, ulong uid, int force)
6657dd7cddfSDavid du Colombier {
6667dd7cddfSDavid du Colombier 	for(; m != nil; m = m->next){
6677dd7cddfSDavid du Colombier 		if(!force && m->uid != 0)
6687dd7cddfSDavid du Colombier 			bye("uid renumbering with a valid uid");
6697dd7cddfSDavid du Colombier 		m->uid = uid++;
6707dd7cddfSDavid du Colombier 	}
6717dd7cddfSDavid du Colombier 	return uid;
6727dd7cddfSDavid du Colombier }
6737dd7cddfSDavid du Colombier 
6747dd7cddfSDavid du Colombier void
closeBox(Box * box,int opened)6757dd7cddfSDavid du Colombier closeBox(Box *box, int opened)
6767dd7cddfSDavid du Colombier {
6777dd7cddfSDavid du Colombier 	Msg *m, *next;
6787dd7cddfSDavid du Colombier 
6797dd7cddfSDavid du Colombier 	/*
6807dd7cddfSDavid du Colombier 	 * make sure to leave the mailbox directory so upas/fs can close the mailbox
6817dd7cddfSDavid du Colombier 	 */
6827dd7cddfSDavid du Colombier 	myChdir(mboxDir);
6837dd7cddfSDavid du Colombier 
6847dd7cddfSDavid du Colombier 	if(box->writable){
6857dd7cddfSDavid du Colombier 		deleteMsgs(box);
68659cc4ca5SDavid du Colombier 		if(expungeMsgs(box, 0))
68759cc4ca5SDavid du Colombier 			closeImp(box, checkBox(box, 1));
6887dd7cddfSDavid du Colombier 	}
6897dd7cddfSDavid du Colombier 
6907dd7cddfSDavid du Colombier 	if(fprint(fsCtl, "close %s", box->fs) < 0 && opened)
6917dd7cddfSDavid du Colombier 		bye("can't talk to mail server");
6927dd7cddfSDavid du Colombier 	for(m = box->msgs; m != nil; m = next){
6937dd7cddfSDavid du Colombier 		next = m->next;
6947dd7cddfSDavid du Colombier 		freeMsg(m);
6957dd7cddfSDavid du Colombier 	}
6967dd7cddfSDavid du Colombier 	free(box->name);
6977dd7cddfSDavid du Colombier 	free(box->fs);
6987dd7cddfSDavid du Colombier 	free(box->fsDir);
6997dd7cddfSDavid du Colombier 	free(box->imp);
7007dd7cddfSDavid du Colombier 	free(box);
7017dd7cddfSDavid du Colombier }
7027dd7cddfSDavid du Colombier 
7037dd7cddfSDavid du Colombier int
deleteMsgs(Box * box)7047dd7cddfSDavid du Colombier deleteMsgs(Box *box)
7057dd7cddfSDavid du Colombier {
7067dd7cddfSDavid du Colombier 	Msg *m;
70759cc4ca5SDavid du Colombier 	char buf[BufSize], *p, *start;
7087dd7cddfSDavid du Colombier 	int ok;
7097dd7cddfSDavid du Colombier 
7107dd7cddfSDavid du Colombier 	if(!box->writable)
7117dd7cddfSDavid du Colombier 		return 0;
7127dd7cddfSDavid du Colombier 
7137dd7cddfSDavid du Colombier 	/*
71459cc4ca5SDavid du Colombier 	 * first pass: delete messages; gang the writes together for speed.
7157dd7cddfSDavid du Colombier 	 */
7167dd7cddfSDavid du Colombier 	ok = 1;
71759cc4ca5SDavid du Colombier 	start = seprint(buf, buf + sizeof(buf), "delete %s", box->fs);
71859cc4ca5SDavid du Colombier 	p = start;
7197dd7cddfSDavid du Colombier 	for(m = box->msgs; m != nil; m = m->next){
7207dd7cddfSDavid du Colombier 		if((m->flags & MDeleted) && !m->expunged){
7217dd7cddfSDavid du Colombier 			m->expunged = 1;
72259cc4ca5SDavid du Colombier 			p = seprint(p, buf + sizeof(buf), " %lud", m->id);
72359cc4ca5SDavid du Colombier 			if(p + 32 >= buf + sizeof(buf)){
72459cc4ca5SDavid du Colombier 				if(write(fsCtl, buf, p - buf) < 0)
72559cc4ca5SDavid du Colombier 					bye("can't talk to mail server");
72659cc4ca5SDavid du Colombier 				p = start;
7277dd7cddfSDavid du Colombier 			}
7287dd7cddfSDavid du Colombier 		}
72959cc4ca5SDavid du Colombier 	}
73059cc4ca5SDavid du Colombier 	if(p != start && write(fsCtl, buf, p - buf) < 0)
73159cc4ca5SDavid du Colombier 		bye("can't talk to mail server");
7327dd7cddfSDavid du Colombier 
7337dd7cddfSDavid du Colombier 	return ok;
7347dd7cddfSDavid du Colombier }
7357dd7cddfSDavid du Colombier 
7367dd7cddfSDavid du Colombier /*
7377dd7cddfSDavid du Colombier  * second pass: remove the message structure,
7387dd7cddfSDavid du Colombier  * and renumber message sequence numbers.
7397dd7cddfSDavid du Colombier  * update messages counts in mailbox.
7407dd7cddfSDavid du Colombier  * returns true if anything changed.
7417dd7cddfSDavid du Colombier  */
7427dd7cddfSDavid du Colombier int
expungeMsgs(Box * box,int send)7437dd7cddfSDavid du Colombier expungeMsgs(Box *box, int send)
7447dd7cddfSDavid du Colombier {
7457dd7cddfSDavid du Colombier 	Msg *m, *next, *last;
7467dd7cddfSDavid du Colombier 	ulong n;
7477dd7cddfSDavid du Colombier 
7487dd7cddfSDavid du Colombier 	n = 0;
7497dd7cddfSDavid du Colombier 	last = nil;
7507dd7cddfSDavid du Colombier 	for(m = box->msgs; m != nil; m = next){
7517dd7cddfSDavid du Colombier 		m->seq -= n;
7527dd7cddfSDavid du Colombier 		next = m->next;
7537dd7cddfSDavid du Colombier 		if(m->expunged){
7547dd7cddfSDavid du Colombier 			if(send)
7557dd7cddfSDavid du Colombier 				Bprint(&bout, "* %lud expunge\r\n", m->seq);
7567dd7cddfSDavid du Colombier 			if(m->flags & MRecent)
7577dd7cddfSDavid du Colombier 				box->recent--;
7587dd7cddfSDavid du Colombier 			n++;
7597dd7cddfSDavid du Colombier 			if(last == nil)
7607dd7cddfSDavid du Colombier 				box->msgs = next;
7617dd7cddfSDavid du Colombier 			else
7627dd7cddfSDavid du Colombier 				last->next = next;
7637dd7cddfSDavid du Colombier 			freeMsg(m);
7647dd7cddfSDavid du Colombier 		}else
7657dd7cddfSDavid du Colombier 			last = m;
7667dd7cddfSDavid du Colombier 	}
7677dd7cddfSDavid du Colombier 	if(n){
7687dd7cddfSDavid du Colombier 		box->max -= n;
7697dd7cddfSDavid du Colombier 		box->dirtyImp = 1;
7707dd7cddfSDavid du Colombier 	}
7717dd7cddfSDavid du Colombier 	return n;
7727dd7cddfSDavid du Colombier }
7737dd7cddfSDavid du Colombier 
7747dd7cddfSDavid du Colombier static void
fsInit(void)7757dd7cddfSDavid du Colombier fsInit(void)
7767dd7cddfSDavid du Colombier {
7777dd7cddfSDavid du Colombier 	if(fsCtl >= 0)
7787dd7cddfSDavid du Colombier 		return;
7797dd7cddfSDavid du Colombier 	fsCtl = open("/mail/fs/ctl", ORDWR);
7807dd7cddfSDavid du Colombier 	if(fsCtl < 0)
7817dd7cddfSDavid du Colombier 		bye("can't open mail file system");
7827dd7cddfSDavid du Colombier 	if(fprint(fsCtl, "close mbox") < 0)
7837dd7cddfSDavid du Colombier 		bye("can't initialize mail file system");
7847dd7cddfSDavid du Colombier }
7857dd7cddfSDavid du Colombier 
7867dd7cddfSDavid du Colombier static char *stoplist[] =
7877dd7cddfSDavid du Colombier {
7887dd7cddfSDavid du Colombier 	"mbox",
7897dd7cddfSDavid du Colombier 	"pipeto",
7907dd7cddfSDavid du Colombier 	"forward",
7917dd7cddfSDavid du Colombier 	"names",
79243aadf5eSDavid du Colombier 	"pipefrom",
79343aadf5eSDavid du Colombier 	"headers",
79443aadf5eSDavid du Colombier 	"imap.ok",
7957dd7cddfSDavid du Colombier 	0
7967dd7cddfSDavid du Colombier };
7977dd7cddfSDavid du Colombier 
7983468a491SDavid du Colombier enum {
7993468a491SDavid du Colombier 	Maxokbytes	= 4096,
8003468a491SDavid du Colombier 	Maxfolders	= Maxokbytes / 4,
8013468a491SDavid du Colombier };
8023468a491SDavid du Colombier 
8033468a491SDavid du Colombier static char *folders[Maxfolders];
80443aadf5eSDavid du Colombier static char *folderbuff;
80543aadf5eSDavid du Colombier 
80643aadf5eSDavid du Colombier static void
readokfolders(void)80743aadf5eSDavid du Colombier readokfolders(void)
80843aadf5eSDavid du Colombier {
80943aadf5eSDavid du Colombier 	int fd, nr;
81043aadf5eSDavid du Colombier 
81143aadf5eSDavid du Colombier 	fd = open("imap.ok", OREAD);
8123468a491SDavid du Colombier 	if(fd < 0)
8133468a491SDavid du Colombier 		return;
8143468a491SDavid du Colombier 	folderbuff = malloc(Maxokbytes);
8153468a491SDavid du Colombier 	if(folderbuff == nil) {
8163468a491SDavid du Colombier 		close(fd);
8173468a491SDavid du Colombier 		return;
8183468a491SDavid du Colombier 	}
8193468a491SDavid du Colombier 	nr = read(fd, folderbuff, Maxokbytes-1);	/* once is ok */
8203468a491SDavid du Colombier 	close(fd);
8213468a491SDavid du Colombier 	if(nr < 0){
82243aadf5eSDavid du Colombier 		free(folderbuff);
82343aadf5eSDavid du Colombier 		folderbuff = nil;
82443aadf5eSDavid du Colombier 		return;
82543aadf5eSDavid du Colombier 	}
82643aadf5eSDavid du Colombier 	folderbuff[nr] = 0;
82743aadf5eSDavid du Colombier 	tokenize(folderbuff, folders, nelem(folders));
82843aadf5eSDavid du Colombier }
82943aadf5eSDavid du Colombier 
8307dd7cddfSDavid du Colombier /*
8317dd7cddfSDavid du Colombier  * reject bad mailboxes based on mailbox name
8327dd7cddfSDavid du Colombier  */
8337dd7cddfSDavid du Colombier int
okMbox(char * path)8347dd7cddfSDavid du Colombier okMbox(char *path)
8357dd7cddfSDavid du Colombier {
8367dd7cddfSDavid du Colombier 	char *name;
8377dd7cddfSDavid du Colombier 	int i;
8387dd7cddfSDavid du Colombier 
83943aadf5eSDavid du Colombier 	if(folderbuff == nil && access("imap.ok", AREAD) == 0)
84043aadf5eSDavid du Colombier 		readokfolders();
8417dd7cddfSDavid du Colombier 	name = strrchr(path, '/');
8427dd7cddfSDavid du Colombier 	if(name == nil)
8437dd7cddfSDavid du Colombier 		name = path;
8447dd7cddfSDavid du Colombier 	else
8457dd7cddfSDavid du Colombier 		name++;
84643aadf5eSDavid du Colombier 	if(folderbuff != nil){
84743aadf5eSDavid du Colombier 		for(i = 0; i < nelem(folders) && folders[i] != nil; i++)
84843aadf5eSDavid du Colombier 			if(cistrcmp(folders[i], name) == 0)
84943aadf5eSDavid du Colombier 				return 1;
85043aadf5eSDavid du Colombier 		return 0;
85143aadf5eSDavid du Colombier 	}
8529a747e4fSDavid du Colombier 	if(strlen(name) + STRLEN(".imp") >= MboxNameLen)
8537dd7cddfSDavid du Colombier 		return 0;
8547dd7cddfSDavid du Colombier 	for(i = 0; stoplist[i]; i++)
8557dd7cddfSDavid du Colombier 		if(strcmp(name, stoplist[i]) == 0)
8567dd7cddfSDavid du Colombier 			return 0;
8577dd7cddfSDavid du Colombier 	if(isprefix("L.", name) || isprefix("imap-tmp.", name)
8587dd7cddfSDavid du Colombier 	|| issuffix(".imp", name)
8599a747e4fSDavid du Colombier 	|| strcmp("imap.subscribed", name) == 0
8607dd7cddfSDavid du Colombier 	|| isdotdot(name) || name[0] == '/')
8617dd7cddfSDavid du Colombier 		return 0;
8627dd7cddfSDavid du Colombier 	return 1;
8637dd7cddfSDavid du Colombier }
864