xref: /plan9/sys/src/cmd/upas/fs/mbox.c (revision 223a736ebd2849388a6a0145cd1e22a96bd28460)
17dd7cddfSDavid du Colombier #include "common.h"
27dd7cddfSDavid du Colombier #include <ctype.h>
37dd7cddfSDavid du Colombier #include <plumb.h>
47dd7cddfSDavid du Colombier #include <libsec.h>
57dd7cddfSDavid du Colombier #include "dat.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier typedef struct Header Header;
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier struct Header {
107dd7cddfSDavid du Colombier 	char *type;
117dd7cddfSDavid du Colombier 	void (*f)(Message*, Header*, char*);
127dd7cddfSDavid du Colombier 	int len;
137dd7cddfSDavid du Colombier };
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier /* headers */
167dd7cddfSDavid du Colombier static	void	ctype(Message*, Header*, char*);
177dd7cddfSDavid du Colombier static	void	cencoding(Message*, Header*, char*);
187dd7cddfSDavid du Colombier static	void	cdisposition(Message*, Header*, char*);
197dd7cddfSDavid du Colombier static	void	date822(Message*, Header*, char*);
207dd7cddfSDavid du Colombier static	void	from822(Message*, Header*, char*);
217dd7cddfSDavid du Colombier static	void	to822(Message*, Header*, char*);
227dd7cddfSDavid du Colombier static	void	sender822(Message*, Header*, char*);
237dd7cddfSDavid du Colombier static	void	replyto822(Message*, Header*, char*);
247dd7cddfSDavid du Colombier static	void	subject822(Message*, Header*, char*);
257dd7cddfSDavid du Colombier static	void	inreplyto822(Message*, Header*, char*);
267dd7cddfSDavid du Colombier static	void	cc822(Message*, Header*, char*);
277dd7cddfSDavid du Colombier static	void	bcc822(Message*, Header*, char*);
287dd7cddfSDavid du Colombier static	void	messageid822(Message*, Header*, char*);
297dd7cddfSDavid du Colombier static	void	mimeversion(Message*, Header*, char*);
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier enum
327dd7cddfSDavid du Colombier {
337dd7cddfSDavid du Colombier 	Mhead=	11,	/* offset of first mime header */
347dd7cddfSDavid du Colombier };
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier Header head[] =
377dd7cddfSDavid du Colombier {
387dd7cddfSDavid du Colombier 	{ "date:", date822, },
397dd7cddfSDavid du Colombier 	{ "from:", from822, },
407dd7cddfSDavid du Colombier 	{ "to:", to822, },
417dd7cddfSDavid du Colombier 	{ "sender:", sender822, },
427dd7cddfSDavid du Colombier 	{ "reply-to:", replyto822, },
437dd7cddfSDavid du Colombier 	{ "subject:", subject822, },
447dd7cddfSDavid du Colombier 	{ "cc:", cc822, },
457dd7cddfSDavid du Colombier 	{ "bcc:", bcc822, },
467dd7cddfSDavid du Colombier 	{ "in-reply-to:", inreplyto822, },
477dd7cddfSDavid du Colombier 	{ "mime-version:", mimeversion, },
487dd7cddfSDavid du Colombier 	{ "message-id:", messageid822, },
497dd7cddfSDavid du Colombier 
507dd7cddfSDavid du Colombier [Mhead]	{ "content-type:", ctype, },
517dd7cddfSDavid du Colombier 	{ "content-transfer-encoding:", cencoding, },
527dd7cddfSDavid du Colombier 	{ "content-disposition:", cdisposition, },
537dd7cddfSDavid du Colombier 	{ 0, },
547dd7cddfSDavid du Colombier };
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier static	void	fatal(char *fmt, ...);
577dd7cddfSDavid du Colombier static	void	initquoted(void);
587dd7cddfSDavid du Colombier static	void	startheader(Message*);
597dd7cddfSDavid du Colombier static	void	startbody(Message*);
607dd7cddfSDavid du Colombier static	char*	skipwhite(char*);
617dd7cddfSDavid du Colombier static	char*	skiptosemi(char*);
627dd7cddfSDavid du Colombier static	char*	getstring(char*, String*, int);
637dd7cddfSDavid du Colombier static	void	setfilename(Message*, char*);
647dd7cddfSDavid du Colombier static	char*	lowercase(char*);
657dd7cddfSDavid du Colombier static	int	is8bit(Message*);
667dd7cddfSDavid du Colombier static	void	parse(Message*, int, Mailbox*);
677dd7cddfSDavid du Colombier static	void	parseunix(Message*);
687dd7cddfSDavid du Colombier static	int	headerline(char**, String*);
697dd7cddfSDavid du Colombier static	void	initheaders(void);
707dd7cddfSDavid du Colombier static void	parseattachments(Message*, Mailbox*);
717dd7cddfSDavid du Colombier 
727dd7cddfSDavid du Colombier int debug;
737dd7cddfSDavid du Colombier 
747dd7cddfSDavid du Colombier enum
757dd7cddfSDavid du Colombier {
767dd7cddfSDavid du Colombier 	Chunksize = 1024,
777dd7cddfSDavid du Colombier };
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier /* create a new mailbox */
807dd7cddfSDavid du Colombier char*
817dd7cddfSDavid du Colombier newmbox(char *path, char *name, int std)
827dd7cddfSDavid du Colombier {
837dd7cddfSDavid du Colombier 	Mailbox *mb, **l;
847dd7cddfSDavid du Colombier 	char *p, *rv;
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier 	mb = emalloc(sizeof(*mb));
877dd7cddfSDavid du Colombier 	strncpy(mb->path, path, sizeof(mb->path)-1);
887dd7cddfSDavid du Colombier 	if(name == nil){
897dd7cddfSDavid du Colombier 		p = strrchr(path, '/');
907dd7cddfSDavid du Colombier 		if(p == nil)
917dd7cddfSDavid du Colombier 			p = path;
927dd7cddfSDavid du Colombier 		else
937dd7cddfSDavid du Colombier 			p++;
947dd7cddfSDavid du Colombier 		if(*p == 0){
957dd7cddfSDavid du Colombier 			free(mb);
967dd7cddfSDavid du Colombier 			return "bad mbox name";
977dd7cddfSDavid du Colombier 		}
987dd7cddfSDavid du Colombier 		strncpy(mb->name, p, sizeof(mb->name)-1);
997dd7cddfSDavid du Colombier 	} else {
1007dd7cddfSDavid du Colombier 		strncpy(mb->name, name, sizeof(mb->name)-1);
1017dd7cddfSDavid du Colombier 	}
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier 	// make sure name isn't taken
1047dd7cddfSDavid du Colombier 	qlock(&mbllock);
1057dd7cddfSDavid du Colombier 	for(l = &mbl; *l != nil; l = &(*l)->next)
1067dd7cddfSDavid du Colombier 		if(strcmp((*l)->name, mb->name) == 0){
1077dd7cddfSDavid du Colombier 			if(strcmp(path, mb->path) == 0)
1087dd7cddfSDavid du Colombier 				rv = nil;
1097dd7cddfSDavid du Colombier 			else
1107dd7cddfSDavid du Colombier 				rv = "mbox name in use";
1117dd7cddfSDavid du Colombier 			free(mb);
1127dd7cddfSDavid du Colombier 			qunlock(&mbllock);
1137dd7cddfSDavid du Colombier 			return rv;
1147dd7cddfSDavid du Colombier 		}
1157dd7cddfSDavid du Colombier 	mb->refs = 1;
1167dd7cddfSDavid du Colombier 	mb->next = nil;
1177dd7cddfSDavid du Colombier 	mb->id = newid();
1187dd7cddfSDavid du Colombier 	mb->root = newmessage(nil);
1197dd7cddfSDavid du Colombier 	mb->std = std;
1207dd7cddfSDavid du Colombier 	*l = mb;
1217dd7cddfSDavid du Colombier 	qunlock(&mbllock);
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier 	qlock(mb);
1247dd7cddfSDavid du Colombier 	rv = syncmbox(mb, 0);
1257dd7cddfSDavid du Colombier 	qunlock(mb);
1267dd7cddfSDavid du Colombier 
1277dd7cddfSDavid du Colombier 	return rv;
1287dd7cddfSDavid du Colombier }
1297dd7cddfSDavid du Colombier 
1307dd7cddfSDavid du Colombier // close the named mailbox
1317dd7cddfSDavid du Colombier void
1327dd7cddfSDavid du Colombier freembox(char *name)
1337dd7cddfSDavid du Colombier {
1347dd7cddfSDavid du Colombier 	Mailbox *mb;
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier 	qlock(&mbllock);
1377dd7cddfSDavid du Colombier 	for(mb = mbl; mb != nil; mb = mb->next)
1387dd7cddfSDavid du Colombier 		if(strcmp(name, mb->name) == 0){
1397dd7cddfSDavid du Colombier 			mboxdecref(mb);
1407dd7cddfSDavid du Colombier 			break;
1417dd7cddfSDavid du Colombier 		}
1427dd7cddfSDavid du Colombier 	hfree(CHDIR|PATH(0, Qtop), name);
1437dd7cddfSDavid du Colombier 	qunlock(&mbllock);
1447dd7cddfSDavid du Colombier }
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier enum {
1477dd7cddfSDavid du Colombier 	Buffersize = 64*1024,
1487dd7cddfSDavid du Colombier };
1497dd7cddfSDavid du Colombier 
1507dd7cddfSDavid du Colombier typedef struct Inbuf Inbuf;
1517dd7cddfSDavid du Colombier struct Inbuf
1527dd7cddfSDavid du Colombier {
1537dd7cddfSDavid du Colombier 	int	fd;
1547dd7cddfSDavid du Colombier 	uchar	*lim;
1557dd7cddfSDavid du Colombier 	uchar	*rptr;
1567dd7cddfSDavid du Colombier 	uchar	*wptr;
1577dd7cddfSDavid du Colombier 	uchar	data[Buffersize+7];
1587dd7cddfSDavid du Colombier };
1597dd7cddfSDavid du Colombier 
1607dd7cddfSDavid du Colombier static void
1617dd7cddfSDavid du Colombier addtomessage(Message *m, uchar *p, int n, int done)
1627dd7cddfSDavid du Colombier {
1637dd7cddfSDavid du Colombier 	int i, len;
1647dd7cddfSDavid du Colombier 
1657dd7cddfSDavid du Colombier 	// add to message (+ 1 in malloc is for a trailing null)
1667dd7cddfSDavid du Colombier 	if(m->lim - m->end < n){
1677dd7cddfSDavid du Colombier 		if(m->start != nil){
1687dd7cddfSDavid du Colombier 			i = m->end-m->start;
1697dd7cddfSDavid du Colombier 			if(done)
1707dd7cddfSDavid du Colombier 				len = i + n;
1717dd7cddfSDavid du Colombier 			else
1727dd7cddfSDavid du Colombier 				len = (4*(i+n))/3;
1737dd7cddfSDavid du Colombier 			m->start = erealloc(m->start, len + 1);
1747dd7cddfSDavid du Colombier 			m->end = m->start + i;
1757dd7cddfSDavid du Colombier 		} else {
1767dd7cddfSDavid du Colombier 			if(done)
1777dd7cddfSDavid du Colombier 				len = n;
1787dd7cddfSDavid du Colombier 			else
1797dd7cddfSDavid du Colombier 				len = 2*n;
1807dd7cddfSDavid du Colombier 			m->start = emalloc(len + 1);
1817dd7cddfSDavid du Colombier 			m->end = m->start;
1827dd7cddfSDavid du Colombier 		}
1837dd7cddfSDavid du Colombier 		m->lim = m->start + len;
1847dd7cddfSDavid du Colombier 	}
1857dd7cddfSDavid du Colombier 
1867dd7cddfSDavid du Colombier 	memmove(m->end, p, n);
1877dd7cddfSDavid du Colombier 	m->end += n;
1887dd7cddfSDavid du Colombier }
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier //
1917dd7cddfSDavid du Colombier //  read in a single message
1927dd7cddfSDavid du Colombier //
1937dd7cddfSDavid du Colombier static int
1947dd7cddfSDavid du Colombier readmessage(Message *m, Inbuf *inb)
1957dd7cddfSDavid du Colombier {
1967dd7cddfSDavid du Colombier 	int i, n, done;
1977dd7cddfSDavid du Colombier 	uchar *p, *np;
1987dd7cddfSDavid du Colombier 	char sdigest[SHA1dlen*2+1];
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier 	for(done = 0; !done;){
2017dd7cddfSDavid du Colombier 		n = inb->wptr - inb->rptr;
2027dd7cddfSDavid du Colombier 		if(n < 6){
2037dd7cddfSDavid du Colombier 			if(n)
2047dd7cddfSDavid du Colombier 				memmove(inb->data, inb->rptr, n);
2057dd7cddfSDavid du Colombier 			inb->rptr = inb->data;
2067dd7cddfSDavid du Colombier 			inb->wptr = inb->rptr + n;
2077dd7cddfSDavid du Colombier 			i = read(inb->fd, inb->wptr, Buffersize);
2087dd7cddfSDavid du Colombier 			if(i < 0)
2097dd7cddfSDavid du Colombier 				return -1;
2107dd7cddfSDavid du Colombier 			if(i == 0){
2117dd7cddfSDavid du Colombier 				if(n != 0)
2127dd7cddfSDavid du Colombier 					addtomessage(m, inb->rptr, n, 1);
2137dd7cddfSDavid du Colombier 				if(m->end == m->start)
2147dd7cddfSDavid du Colombier 					return -1;
2157dd7cddfSDavid du Colombier 				break;
2167dd7cddfSDavid du Colombier 			}
2177dd7cddfSDavid du Colombier 			inb->wptr += i;
2187dd7cddfSDavid du Colombier 		}
2197dd7cddfSDavid du Colombier 
2207dd7cddfSDavid du Colombier 		// look for end of message
2217dd7cddfSDavid du Colombier 		for(p = inb->rptr; p < inb->wptr; p = np+1){
2227dd7cddfSDavid du Colombier 			// first part of search for '\nFrom '
2237dd7cddfSDavid du Colombier 			np = memchr(p, '\n', inb->wptr - p);
2247dd7cddfSDavid du Colombier 			if(np == nil){
2257dd7cddfSDavid du Colombier 				p = inb->wptr;
2267dd7cddfSDavid du Colombier 				break;
2277dd7cddfSDavid du Colombier 			}
2287dd7cddfSDavid du Colombier 
2297dd7cddfSDavid du Colombier 			/*
2307dd7cddfSDavid du Colombier 			 *  if we've found a \n but there's
2317dd7cddfSDavid du Colombier 			 *  not enough room for '\nFrom ', don't do
2327dd7cddfSDavid du Colombier 			 *  the comparison till we've read in more.
2337dd7cddfSDavid du Colombier 			 */
2347dd7cddfSDavid du Colombier 			if(inb->wptr - np < 6){
2357dd7cddfSDavid du Colombier 				p = np;
2367dd7cddfSDavid du Colombier 				break;
2377dd7cddfSDavid du Colombier 			}
2387dd7cddfSDavid du Colombier 
2397dd7cddfSDavid du Colombier 			if(strncmp((char*)np, "\nFrom ", 6) == 0){
2407dd7cddfSDavid du Colombier 				done = 1;
2417dd7cddfSDavid du Colombier 				p = np+1;
2427dd7cddfSDavid du Colombier 				break;
2437dd7cddfSDavid du Colombier 			}
2447dd7cddfSDavid du Colombier 		}
2457dd7cddfSDavid du Colombier 
2467dd7cddfSDavid du Colombier 		// add to message (+ 1 in malloc is for a trailing null)
2477dd7cddfSDavid du Colombier 		n = p - inb->rptr;
2487dd7cddfSDavid du Colombier 		addtomessage(m, inb->rptr, n, done);
2497dd7cddfSDavid du Colombier 		inb->rptr += n;
2507dd7cddfSDavid du Colombier 	}
2517dd7cddfSDavid du Colombier 
2527dd7cddfSDavid du Colombier 	// if it doesn't start with a 'From ', this ain't a mailbox
2537dd7cddfSDavid du Colombier 	if(strncmp(m->start, "From ", 5) != 0)
2547dd7cddfSDavid du Colombier 		return -1;
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier 	// dump trailing newline, make sure there's a trailing null
2577dd7cddfSDavid du Colombier 	// (helps in body searches)
2587dd7cddfSDavid du Colombier 	if(*(m->end-1) == '\n')
2597dd7cddfSDavid du Colombier 		m->end--;
2607dd7cddfSDavid du Colombier 	*m->end = 0;
2617dd7cddfSDavid du Colombier 	m->bend = m->rbend = m->end;
2627dd7cddfSDavid du Colombier 
2637dd7cddfSDavid du Colombier 	// digest message
2647dd7cddfSDavid du Colombier 	sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
2657dd7cddfSDavid du Colombier 	for(i = 0; i < SHA1dlen; i++)
2667dd7cddfSDavid du Colombier 		sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
2677dd7cddfSDavid du Colombier 	m->sdigest = s_copy(sdigest);
2687dd7cddfSDavid du Colombier 
2697dd7cddfSDavid du Colombier 	return 0;
2707dd7cddfSDavid du Colombier }
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier // throw out deleted messages.  return number of freshly deleted messages
2737dd7cddfSDavid du Colombier int
2747dd7cddfSDavid du Colombier purgedeleted(Mailbox *mb)
2757dd7cddfSDavid du Colombier {
2767dd7cddfSDavid du Colombier 	Message *m, *next;
2777dd7cddfSDavid du Colombier 	int newdels;
2787dd7cddfSDavid du Colombier 
2797dd7cddfSDavid du Colombier 	// forget about what's no longer in the mailbox
2807dd7cddfSDavid du Colombier 	newdels = 0;
2817dd7cddfSDavid du Colombier 	for(m = mb->root->part; m != nil; m = next){
2827dd7cddfSDavid du Colombier 		next = m->next;
2837dd7cddfSDavid du Colombier 		if(m->deleted && m->refs == 0){
2847dd7cddfSDavid du Colombier 			if(m->inmbox)
2857dd7cddfSDavid du Colombier 				newdels++;
2867dd7cddfSDavid du Colombier 			delmessage(mb, m);
2877dd7cddfSDavid du Colombier 		}
2887dd7cddfSDavid du Colombier 	}
2897dd7cddfSDavid du Colombier 	return newdels;
2907dd7cddfSDavid du Colombier }
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier //
2937dd7cddfSDavid du Colombier //  read in the mailbox and parse into messages.
2947dd7cddfSDavid du Colombier //
2957dd7cddfSDavid du Colombier static char*
2967dd7cddfSDavid du Colombier _readmbox(Mailbox *mb, int doplumb, Mlock *lk)
2977dd7cddfSDavid du Colombier {
2987dd7cddfSDavid du Colombier 	int fd;
2997dd7cddfSDavid du Colombier 	String *tmp;
3007dd7cddfSDavid du Colombier 	Dir d;
3017dd7cddfSDavid du Colombier 	static char err[ERRLEN];
3027dd7cddfSDavid du Colombier 	Message *m, **l;
3037dd7cddfSDavid du Colombier 	Inbuf *inb;
3047dd7cddfSDavid du Colombier 	char *x;
3057dd7cddfSDavid du Colombier 
3067dd7cddfSDavid du Colombier 	l = &mb->root->part;
3077dd7cddfSDavid du Colombier 	initheaders();
3087dd7cddfSDavid du Colombier 
3097dd7cddfSDavid du Colombier 	/*
3107dd7cddfSDavid du Colombier 	 *  open the mailbox.  If it doesn't exist, try the temporary one.
3117dd7cddfSDavid du Colombier 	 */
3127dd7cddfSDavid du Colombier retry:
3137dd7cddfSDavid du Colombier 	fd = open(mb->path, OREAD);
3147dd7cddfSDavid du Colombier 	if(fd < 0){
3157dd7cddfSDavid du Colombier 		errstr(err);
3167dd7cddfSDavid du Colombier 		if(strstr(err, "exist") != 0){
3177dd7cddfSDavid du Colombier 			tmp = s_copy(mb->path);
3187dd7cddfSDavid du Colombier 			s_append(tmp, ".tmp");
3197dd7cddfSDavid du Colombier 			if(sysrename(s_to_c(tmp), mb->path) == 0){
3207dd7cddfSDavid du Colombier 				s_free(tmp);
3217dd7cddfSDavid du Colombier 				goto retry;
3227dd7cddfSDavid du Colombier 			}
3237dd7cddfSDavid du Colombier 			s_free(tmp);
3247dd7cddfSDavid du Colombier 		}
3257dd7cddfSDavid du Colombier 		return err;
3267dd7cddfSDavid du Colombier 	}
3277dd7cddfSDavid du Colombier 
3287dd7cddfSDavid du Colombier 	/*
3297dd7cddfSDavid du Colombier 	 *  a new qid.path means reread the mailbox, while
3307dd7cddfSDavid du Colombier 	 *  a new qid.vers means read any new messages
3317dd7cddfSDavid du Colombier 	 */
3327dd7cddfSDavid du Colombier 	if(dirfstat(fd, &d) < 0){
3337dd7cddfSDavid du Colombier 		close(fd);
3347dd7cddfSDavid du Colombier 		errstr(err);
3357dd7cddfSDavid du Colombier 		return err;
3367dd7cddfSDavid du Colombier 	}
3377dd7cddfSDavid du Colombier 	if(d.qid.path == mb->d.qid.path && d.qid.vers == mb->d.qid.vers){
3387dd7cddfSDavid du Colombier 		close(fd);
3397dd7cddfSDavid du Colombier 		return nil;
3407dd7cddfSDavid du Colombier 	}
3417dd7cddfSDavid du Colombier 	if(d.qid.path == mb->d.qid.path){
3427dd7cddfSDavid du Colombier 		while(*l != nil)
3437dd7cddfSDavid du Colombier 			l = &(*l)->next;
3447dd7cddfSDavid du Colombier 		seek(fd, mb->d.length, 0);
3457dd7cddfSDavid du Colombier 	}
3467dd7cddfSDavid du Colombier 	memmove(&mb->d, &d, sizeof(d));
3477dd7cddfSDavid du Colombier 	mb->vers++;
3487dd7cddfSDavid du Colombier 	henter(CHDIR|PATH(0, Qtop), mb->name,
3497dd7cddfSDavid du Colombier 		(Qid){CHDIR|PATH(mb->id, Qmbox), mb->vers}, nil, mb);
3507dd7cddfSDavid du Colombier 
3517dd7cddfSDavid du Colombier 	inb = emalloc(sizeof(Inbuf));
3527dd7cddfSDavid du Colombier 	inb->rptr = inb->wptr = inb->data;
3537dd7cddfSDavid du Colombier 	inb->fd = fd;
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier 	//  read new messages
3567dd7cddfSDavid du Colombier 	for(;;){
3577dd7cddfSDavid du Colombier 		if(lk != nil)
3587dd7cddfSDavid du Colombier 			syslockrefresh(lk);
3597dd7cddfSDavid du Colombier 		m = newmessage(mb->root);
3607dd7cddfSDavid du Colombier 		m->mallocd = 1;
3617dd7cddfSDavid du Colombier 		m->inmbox = 1;
3627dd7cddfSDavid du Colombier 		if(readmessage(m, inb) < 0){
3637dd7cddfSDavid du Colombier 			delmessage(mb, m);
3647dd7cddfSDavid du Colombier 			mb->root->subname--;
3657dd7cddfSDavid du Colombier 			break;
3667dd7cddfSDavid du Colombier 		}
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier 		// merge mailbox versions
3697dd7cddfSDavid du Colombier 		while(*l != nil){
3707dd7cddfSDavid du Colombier 			if(memcmp((*l)->digest, m->digest, SHA1dlen) == 0){
3717dd7cddfSDavid du Colombier 				// matches mail we already read, discard
3727dd7cddfSDavid du Colombier 				delmessage(mb, m);
3737dd7cddfSDavid du Colombier 				mb->root->subname--;
3747dd7cddfSDavid du Colombier 				m = nil;
3757dd7cddfSDavid du Colombier 				l = &(*l)->next;
3767dd7cddfSDavid du Colombier 				break;
3777dd7cddfSDavid du Colombier 			} else {
3787dd7cddfSDavid du Colombier 				// old mail no longer in box, mark deleted
3797dd7cddfSDavid du Colombier 				if(doplumb)
3807dd7cddfSDavid du Colombier 					mailplumb(mb, *l, 1);
3817dd7cddfSDavid du Colombier 				(*l)->inmbox = 0;
3827dd7cddfSDavid du Colombier 				(*l)->deleted = 1;
3837dd7cddfSDavid du Colombier 				l = &(*l)->next;
3847dd7cddfSDavid du Colombier 			}
3857dd7cddfSDavid du Colombier 		}
3867dd7cddfSDavid du Colombier 		if(m == nil)
3877dd7cddfSDavid du Colombier 			continue;
3887dd7cddfSDavid du Colombier 
3897dd7cddfSDavid du Colombier 		x = strchr(m->start, '\n');
3907dd7cddfSDavid du Colombier 		if(x == nil)
3917dd7cddfSDavid du Colombier 			m->header = m->end;
3927dd7cddfSDavid du Colombier 		else
3937dd7cddfSDavid du Colombier 			m->header = x + 1;
3947dd7cddfSDavid du Colombier 		m->mheader = m->mhend = m->header;
3957dd7cddfSDavid du Colombier 		parseunix(m);
3967dd7cddfSDavid du Colombier 		parse(m, 0, mb);
3977dd7cddfSDavid du Colombier 
3987dd7cddfSDavid du Colombier 		/* chain in */
3997dd7cddfSDavid du Colombier 		*l = m;
4007dd7cddfSDavid du Colombier 		l = &m->next;
4017dd7cddfSDavid du Colombier 		if(doplumb)
4027dd7cddfSDavid du Colombier 			mailplumb(mb, m, 0);
4037dd7cddfSDavid du Colombier 
4047dd7cddfSDavid du Colombier 	}
4057dd7cddfSDavid du Colombier 
4067dd7cddfSDavid du Colombier 	// whatever is left has been removed from the mbox, mark deleted
4077dd7cddfSDavid du Colombier 	while(*l != nil){
4087dd7cddfSDavid du Colombier 		if(doplumb)
4097dd7cddfSDavid du Colombier 			mailplumb(mb, *l, 1);
4107dd7cddfSDavid du Colombier 		(*l)->inmbox = 0;
4117dd7cddfSDavid du Colombier 		(*l)->deleted = 1;
4127dd7cddfSDavid du Colombier 		l = &(*l)->next;
4137dd7cddfSDavid du Colombier 	}
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier 	close(fd);
4167dd7cddfSDavid du Colombier 	free(inb);
4177dd7cddfSDavid du Colombier 	return nil;
4187dd7cddfSDavid du Colombier }
4197dd7cddfSDavid du Colombier 
4207dd7cddfSDavid du Colombier static void
4217dd7cddfSDavid du Colombier _writembox(Mailbox *mb, Mlock *lk)
4227dd7cddfSDavid du Colombier {
4237dd7cddfSDavid du Colombier 	Dir d;
4247dd7cddfSDavid du Colombier 	Message *m;
4257dd7cddfSDavid du Colombier 	String *tmp;
4267dd7cddfSDavid du Colombier 	int mode, errs;
4277dd7cddfSDavid du Colombier 	Biobuf *b;
4287dd7cddfSDavid du Colombier 
4297dd7cddfSDavid du Colombier 	tmp = s_copy(mb->path);
4307dd7cddfSDavid du Colombier 	s_append(tmp, ".tmp");
4317dd7cddfSDavid du Colombier 
4327dd7cddfSDavid du Colombier 	/*
4337dd7cddfSDavid du Colombier 	 * preserve old files permissions, if possible
4347dd7cddfSDavid du Colombier 	 */
4357dd7cddfSDavid du Colombier 	if(dirstat(mb->path, &d) >= 0)
4367dd7cddfSDavid du Colombier 		mode = d.mode&0777;
4377dd7cddfSDavid du Colombier 	else
4387dd7cddfSDavid du Colombier 		mode = MBOXMODE;
4397dd7cddfSDavid du Colombier 
4407dd7cddfSDavid du Colombier 	sysremove(s_to_c(tmp));
4417dd7cddfSDavid du Colombier 	b = sysopen(s_to_c(tmp), "alc", mode);
4427dd7cddfSDavid du Colombier 	if(b == 0){
4437dd7cddfSDavid du Colombier 		fprint(2, "can't write temporary mailbox\n");
4447dd7cddfSDavid du Colombier 		return;
4457dd7cddfSDavid du Colombier 	}
4467dd7cddfSDavid du Colombier 
4477dd7cddfSDavid du Colombier 	errs = 0;
4487dd7cddfSDavid du Colombier 	for(m = mb->root->part; m != nil; m = m->next){
4497dd7cddfSDavid du Colombier 		if(lk != nil)
4507dd7cddfSDavid du Colombier 			syslockrefresh(lk);
4517dd7cddfSDavid du Colombier 		if(m->deleted)
4527dd7cddfSDavid du Colombier 			continue;
4537dd7cddfSDavid du Colombier 		if(Bwrite(b, m->start, m->end - m->start) < 0)
4547dd7cddfSDavid du Colombier 			errs = 1;
4557dd7cddfSDavid du Colombier 		if(Bwrite(b, "\n", 1) < 0)
4567dd7cddfSDavid du Colombier 			errs = 1;
4577dd7cddfSDavid du Colombier 	}
4587dd7cddfSDavid du Colombier 
4597dd7cddfSDavid du Colombier 	if(sysclose(b) < 0)
4607dd7cddfSDavid du Colombier 		errs = 1;
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	if(errs){
4637dd7cddfSDavid du Colombier 		fprint(2, "error writing temporary mail file\n");
4647dd7cddfSDavid du Colombier 		s_free(tmp);
4657dd7cddfSDavid du Colombier 		return;
4667dd7cddfSDavid du Colombier 	}
4677dd7cddfSDavid du Colombier 
4687dd7cddfSDavid du Colombier 	sysremove(mb->path);
4697dd7cddfSDavid du Colombier 	if(sysrename(s_to_c(tmp), mb->path) < 0)
4707dd7cddfSDavid du Colombier 		fprint(2, "%s: can't rename %s to %s: %r\n", argv0,
4717dd7cddfSDavid du Colombier 			s_to_c(tmp), mb->path);
4727dd7cddfSDavid du Colombier 	s_free(tmp);
4737dd7cddfSDavid du Colombier 	dirstat(mb->path, &mb->d);
4747dd7cddfSDavid du Colombier }
4757dd7cddfSDavid du Colombier 
4767dd7cddfSDavid du Colombier char*
4777dd7cddfSDavid du Colombier syncmbox(Mailbox *mb, int doplumb)
4787dd7cddfSDavid du Colombier {
4797dd7cddfSDavid du Colombier 	Mlock *lk;
4807dd7cddfSDavid du Colombier 	char *rv;
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier 	lk = nil;
4837dd7cddfSDavid du Colombier 	if(mb->std){
4847dd7cddfSDavid du Colombier 		lk = syslock(mb->path);
4857dd7cddfSDavid du Colombier 		if(lk == nil)
4867dd7cddfSDavid du Colombier 			return "can't lock mailbox";
4877dd7cddfSDavid du Colombier 	}
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 	rv = _readmbox(mb, doplumb, lk);		/* interpolate */
4907dd7cddfSDavid du Colombier 	if(purgedeleted(mb) > 0)
4917dd7cddfSDavid du Colombier 		_writembox(mb, lk);
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier 	if(lk != nil)
4947dd7cddfSDavid du Colombier 		sysunlock(lk);
4957dd7cddfSDavid du Colombier 
4967dd7cddfSDavid du Colombier 	return rv;
4977dd7cddfSDavid du Colombier }
4987dd7cddfSDavid du Colombier 
4997dd7cddfSDavid du Colombier static void
5007dd7cddfSDavid du Colombier initheaders(void)
5017dd7cddfSDavid du Colombier {
5027dd7cddfSDavid du Colombier 	Header *h;
5037dd7cddfSDavid du Colombier 	static int already;
5047dd7cddfSDavid du Colombier 
5057dd7cddfSDavid du Colombier 	if(already)
5067dd7cddfSDavid du Colombier 		return;
5077dd7cddfSDavid du Colombier 	already = 1;
5087dd7cddfSDavid du Colombier 
5097dd7cddfSDavid du Colombier 	for(h = head; h->type != nil; h++)
5107dd7cddfSDavid du Colombier 		h->len = strlen(h->type);
5117dd7cddfSDavid du Colombier }
5127dd7cddfSDavid du Colombier 
5137dd7cddfSDavid du Colombier /*
5147dd7cddfSDavid du Colombier  *  parse a Unix style header
5157dd7cddfSDavid du Colombier  */
5167dd7cddfSDavid du Colombier static void
5177dd7cddfSDavid du Colombier parseunix(Message *m)
5187dd7cddfSDavid du Colombier {
5197dd7cddfSDavid du Colombier 	char *p;
5207dd7cddfSDavid du Colombier 	String *h;
5217dd7cddfSDavid du Colombier 
5227dd7cddfSDavid du Colombier 	h = s_new();
5237dd7cddfSDavid du Colombier 	for(p = m->start + 5; *p && *p != '\r' && *p != '\n'; p++)
5247dd7cddfSDavid du Colombier 		s_putc(h, *p);
5257dd7cddfSDavid du Colombier 	s_terminate(h);
5267dd7cddfSDavid du Colombier 	s_restart(h);
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 	m->unixfrom = s_parse(h, s_reset(m->unixfrom));
5297dd7cddfSDavid du Colombier 	m->unixdate = s_append(s_reset(m->unixdate), h->ptr);
5307dd7cddfSDavid du Colombier 
5317dd7cddfSDavid du Colombier 	s_free(h);
5327dd7cddfSDavid du Colombier }
5337dd7cddfSDavid du Colombier 
5347dd7cddfSDavid du Colombier /*
5357dd7cddfSDavid du Colombier  *  parse a message
5367dd7cddfSDavid du Colombier  */
5377dd7cddfSDavid du Colombier static void
5387dd7cddfSDavid du Colombier parse(Message *m, int justmime, Mailbox *mb)
5397dd7cddfSDavid du Colombier {
5407dd7cddfSDavid du Colombier 	String *hl;
5417dd7cddfSDavid du Colombier 	Header *h;
5427dd7cddfSDavid du Colombier 	char *p;
5437dd7cddfSDavid du Colombier 	int i;
5447dd7cddfSDavid du Colombier 
5457dd7cddfSDavid du Colombier 
5467dd7cddfSDavid du Colombier 	if(m->whole == m->whole->whole){
5477dd7cddfSDavid du Colombier 		henter(CHDIR|PATH(mb->id, Qmbox), m->name,
5487dd7cddfSDavid du Colombier 			(Qid){CHDIR|PATH(m->id, Qdir), 0}, m, mb);
5497dd7cddfSDavid du Colombier 	} else {
5507dd7cddfSDavid du Colombier 		henter(CHDIR|PATH(m->whole->id, Qdir), m->name,
5517dd7cddfSDavid du Colombier 			(Qid){CHDIR|PATH(m->id, Qdir), 0}, m, mb);
5527dd7cddfSDavid du Colombier 	}
5537dd7cddfSDavid du Colombier 	for(i = 0; i < Qmax; i++)
5547dd7cddfSDavid du Colombier 		henter(CHDIR|PATH(m->id, Qdir), dirtab[i],
5557dd7cddfSDavid du Colombier 			(Qid){PATH(m->id, i), 0}, m, mb);
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 	// parse mime headers
5587dd7cddfSDavid du Colombier 	p = m->header;
5597dd7cddfSDavid du Colombier 	hl = s_new();
5607dd7cddfSDavid du Colombier 	while(headerline(&p, hl)){
5617dd7cddfSDavid du Colombier 		if(justmime)
5627dd7cddfSDavid du Colombier 			h = &head[Mhead];
5637dd7cddfSDavid du Colombier 		else
5647dd7cddfSDavid du Colombier 			h = head;
5657dd7cddfSDavid du Colombier 		for(; h->type; h++){
5667dd7cddfSDavid du Colombier 			if(cistrncmp(s_to_c(hl), h->type, h->len) == 0){
5677dd7cddfSDavid du Colombier 				(*h->f)(m, h, s_to_c(hl));
5687dd7cddfSDavid du Colombier 				break;
5697dd7cddfSDavid du Colombier 			}
5707dd7cddfSDavid du Colombier 		}
5717dd7cddfSDavid du Colombier 		s_reset(hl);
5727dd7cddfSDavid du Colombier 	}
5737dd7cddfSDavid du Colombier 	s_free(hl);
5747dd7cddfSDavid du Colombier 
5757dd7cddfSDavid du Colombier 	// the blank line isn't really part of the body or header
5767dd7cddfSDavid du Colombier 	if(justmime){
5777dd7cddfSDavid du Colombier 		m->mhend = p;
5787dd7cddfSDavid du Colombier 		m->hend = m->header;
5797dd7cddfSDavid du Colombier 	} else {
5807dd7cddfSDavid du Colombier 		m->hend = p;
5817dd7cddfSDavid du Colombier 	}
5827dd7cddfSDavid du Colombier 	if(*p == '\n')
5837dd7cddfSDavid du Colombier 		p++;
5847dd7cddfSDavid du Colombier 	m->rbody = m->body = p;
5857dd7cddfSDavid du Colombier 
5867dd7cddfSDavid du Colombier 	// if the message isn't mime, ignore the type
5877dd7cddfSDavid du Colombier 	if(m->whole == m->whole->whole && m->mimeversion == nil){
5887dd7cddfSDavid du Colombier 		s_append(s_reset(m->type), "text/plain");
5897dd7cddfSDavid du Colombier 	} else {
5907dd7cddfSDavid du Colombier 		// recurse
5917dd7cddfSDavid du Colombier 		if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){
5927dd7cddfSDavid du Colombier 			parseattachments(m, mb);
5937dd7cddfSDavid du Colombier 		} else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
5947dd7cddfSDavid du Colombier 			decode(m);
5957dd7cddfSDavid du Colombier 			parseattachments(m, mb);
5967dd7cddfSDavid du Colombier 		}
5977dd7cddfSDavid du Colombier 	}
5987dd7cddfSDavid du Colombier }
5997dd7cddfSDavid du Colombier 
6007dd7cddfSDavid du Colombier static void
6017dd7cddfSDavid du Colombier parseattachments(Message *m, Mailbox *mb)
6027dd7cddfSDavid du Colombier {
6037dd7cddfSDavid du Colombier 	Message *nm, **l;
6047dd7cddfSDavid du Colombier 	char *p, *x;
6057dd7cddfSDavid du Colombier 
6067dd7cddfSDavid du Colombier 	// if there's a boundary, recurse...
6077dd7cddfSDavid du Colombier 	if(m->boundary != nil){
6087dd7cddfSDavid du Colombier 		p = m->body;
6097dd7cddfSDavid du Colombier 		nm = nil;
6107dd7cddfSDavid du Colombier 		l = &m->part;
6117dd7cddfSDavid du Colombier 		for(;;){
6127dd7cddfSDavid du Colombier 			x = strstr(p, s_to_c(m->boundary));
6137dd7cddfSDavid du Colombier 			if(x == nil || (x != m->body && *(x-1) != '\n')){
6147dd7cddfSDavid du Colombier 				if(nm != nil)
6157dd7cddfSDavid du Colombier 					nm->rbend = nm->bend = nm->end = m->bend;
6167dd7cddfSDavid du Colombier 				break;
6177dd7cddfSDavid du Colombier 			}
6187dd7cddfSDavid du Colombier 			if(nm != nil)
6197dd7cddfSDavid du Colombier 				nm->rbend = nm->bend = nm->end = x;
6207dd7cddfSDavid du Colombier 			x += strlen(s_to_c(m->boundary));
6217dd7cddfSDavid du Colombier 
6227dd7cddfSDavid du Colombier 			/* is this the last part? ignore anything after it */
6237dd7cddfSDavid du Colombier 			if(strncmp(x, "--", 2) == 0)
6247dd7cddfSDavid du Colombier 				break;
6257dd7cddfSDavid du Colombier 
6267dd7cddfSDavid du Colombier 			p = strchr(x, '\n');
6277dd7cddfSDavid du Colombier 			if(p == nil)
6287dd7cddfSDavid du Colombier 				break;
6297dd7cddfSDavid du Colombier 			nm = newmessage(m);
6307dd7cddfSDavid du Colombier 			nm->start = nm->header = nm->body = nm->rbody = ++p;
6317dd7cddfSDavid du Colombier 			nm->mheader = nm->header;
6327dd7cddfSDavid du Colombier 			*l = nm;
6337dd7cddfSDavid du Colombier 			l = &nm->next;
6347dd7cddfSDavid du Colombier 		}
6357dd7cddfSDavid du Colombier 		for(nm = m->part; nm != nil; nm = nm->next)
6367dd7cddfSDavid du Colombier 			parse(nm, 1, mb);
6377dd7cddfSDavid du Colombier 	} else {
6387dd7cddfSDavid du Colombier 		// reparse rfc822 messages
6397dd7cddfSDavid du Colombier 		if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
6407dd7cddfSDavid du Colombier 			if(m->unixfrom == nil && m->from822 == nil){
6417dd7cddfSDavid du Colombier 				m->header = m->body;
6427dd7cddfSDavid du Colombier 				if(m->ballocd){
6437dd7cddfSDavid du Colombier 					m->hallocd = 1;
6447dd7cddfSDavid du Colombier 					m->ballocd = 0;
6457dd7cddfSDavid du Colombier 				}
6467dd7cddfSDavid du Colombier 				s_free(m->type);
6477dd7cddfSDavid du Colombier 				m->type = s_copy("text/plain");
6487dd7cddfSDavid du Colombier 				m->decoded = 0;
6497dd7cddfSDavid du Colombier 				m->converted = 0;
6507dd7cddfSDavid du Colombier 				parse(m, 0, mb);
6517dd7cddfSDavid du Colombier 			} else {
6527dd7cddfSDavid du Colombier 				nm = newmessage(m);
6537dd7cddfSDavid du Colombier 				m->part = nm;
6547dd7cddfSDavid du Colombier 				nm->start = nm->header = nm->body = nm->rbody = m->body;
6557dd7cddfSDavid du Colombier 				nm->end = nm->bend = nm->rbend = m->bend;
6567dd7cddfSDavid du Colombier 				parse(nm, 0, mb);
6577dd7cddfSDavid du Colombier 			}
6587dd7cddfSDavid du Colombier 		}
6597dd7cddfSDavid du Colombier 	}
6607dd7cddfSDavid du Colombier }
6617dd7cddfSDavid du Colombier 
6627dd7cddfSDavid du Colombier /*
6637dd7cddfSDavid du Colombier  *  pick up a header line
6647dd7cddfSDavid du Colombier  */
6657dd7cddfSDavid du Colombier static int
6667dd7cddfSDavid du Colombier headerline(char **pp, String *hl)
6677dd7cddfSDavid du Colombier {
6687dd7cddfSDavid du Colombier 	char *p, *x;
6697dd7cddfSDavid du Colombier 
6707dd7cddfSDavid du Colombier 	s_reset(hl);
6717dd7cddfSDavid du Colombier 	p = *pp;
6727dd7cddfSDavid du Colombier 	x = strpbrk(p, ":\n");
6737dd7cddfSDavid du Colombier 	if(x == nil || *x == '\n')
6747dd7cddfSDavid du Colombier 		return 0;
6757dd7cddfSDavid du Colombier 	for(;;){
6767dd7cddfSDavid du Colombier 		x = strchr(p, '\n');
6777dd7cddfSDavid du Colombier 		if(x == nil)
6787dd7cddfSDavid du Colombier 			x = p + strlen(p);
6797dd7cddfSDavid du Colombier 		s_nappend(hl, p, x-p);
6807dd7cddfSDavid du Colombier 		p = x;
681*223a736eSDavid du Colombier 		if(*p != '\n' || *++p != ' ' && *p != '\t')
6827dd7cddfSDavid du Colombier 			break;
683*223a736eSDavid du Colombier 		while(*p == ' ' || *p == '\t')
684*223a736eSDavid du Colombier 			p++;
685*223a736eSDavid du Colombier 		s_putc(hl, ' ');
6867dd7cddfSDavid du Colombier 	}
6877dd7cddfSDavid du Colombier 	*pp = p;
6887dd7cddfSDavid du Colombier 	return 1;
6897dd7cddfSDavid du Colombier }
6907dd7cddfSDavid du Colombier 
6917dd7cddfSDavid du Colombier static String*
6927dd7cddfSDavid du Colombier addr822(char *p)
6937dd7cddfSDavid du Colombier {
6947dd7cddfSDavid du Colombier 	String *s, *list;
6957dd7cddfSDavid du Colombier 	int incomment, addrdone, inanticomment, quoted;
6967dd7cddfSDavid du Colombier 	int n;
6977dd7cddfSDavid du Colombier 	int c;
6987dd7cddfSDavid du Colombier 
6997dd7cddfSDavid du Colombier 	list = s_new();
7007dd7cddfSDavid du Colombier 	s = s_new();
7017dd7cddfSDavid du Colombier 	quoted = incomment = addrdone = inanticomment = 0;
7027dd7cddfSDavid du Colombier 	n = 0;
7037dd7cddfSDavid du Colombier 	for(; *p; p++){
7047dd7cddfSDavid du Colombier 		c = *p;
7057dd7cddfSDavid du Colombier 
7067dd7cddfSDavid du Colombier 		// whitespace is ignored
7077dd7cddfSDavid du Colombier 		if(!quoted && isspace(c) || c == '\r')
7087dd7cddfSDavid du Colombier 			continue;
7097dd7cddfSDavid du Colombier 
7107dd7cddfSDavid du Colombier 		// strings are always treated as atoms
7117dd7cddfSDavid du Colombier 		if(!quoted && c == '"'){
7127dd7cddfSDavid du Colombier 			if(!addrdone && !incomment)
7137dd7cddfSDavid du Colombier 				s_putc(s, c);
7147dd7cddfSDavid du Colombier 			for(p++; *p; p++){
7157dd7cddfSDavid du Colombier 				if(!addrdone && !incomment)
7167dd7cddfSDavid du Colombier 					s_putc(s, *p);
7177dd7cddfSDavid du Colombier 				if(!quoted && *p == '"')
7187dd7cddfSDavid du Colombier 					break;
7197dd7cddfSDavid du Colombier 				if(*p == '\\')
7207dd7cddfSDavid du Colombier 					quoted = 1;
7217dd7cddfSDavid du Colombier 				else
7227dd7cddfSDavid du Colombier 					quoted = 0;
7237dd7cddfSDavid du Colombier 			}
7247dd7cddfSDavid du Colombier 			if(*p == 0)
7257dd7cddfSDavid du Colombier 				break;
7267dd7cddfSDavid du Colombier 			quoted = 0;
7277dd7cddfSDavid du Colombier 			continue;
7287dd7cddfSDavid du Colombier 		}
7297dd7cddfSDavid du Colombier 
7307dd7cddfSDavid du Colombier 		// ignore everything in an expicit comment
7317dd7cddfSDavid du Colombier 		if(!quoted && c == '('){
7327dd7cddfSDavid du Colombier 			incomment = 1;
7337dd7cddfSDavid du Colombier 			continue;
7347dd7cddfSDavid du Colombier 		}
7357dd7cddfSDavid du Colombier 		if(incomment){
7367dd7cddfSDavid du Colombier 			if(!quoted && c == ')')
7377dd7cddfSDavid du Colombier 				incomment = 0;
7387dd7cddfSDavid du Colombier 			quoted = 0;
7397dd7cddfSDavid du Colombier 			continue;
7407dd7cddfSDavid du Colombier 		}
7417dd7cddfSDavid du Colombier 
7427dd7cddfSDavid du Colombier 		// anticomments makes everything outside of them comments
7437dd7cddfSDavid du Colombier 		if(!quoted && c == '<' && !inanticomment){
7447dd7cddfSDavid du Colombier 			inanticomment = 1;
7457dd7cddfSDavid du Colombier 			s = s_reset(s);
7467dd7cddfSDavid du Colombier 			continue;
7477dd7cddfSDavid du Colombier 		}
7487dd7cddfSDavid du Colombier 		if(!quoted && c == '>' && inanticomment){
7497dd7cddfSDavid du Colombier 			addrdone = 1;
7507dd7cddfSDavid du Colombier 			inanticomment = 0;
7517dd7cddfSDavid du Colombier 			continue;
7527dd7cddfSDavid du Colombier 		}
7537dd7cddfSDavid du Colombier 
7547dd7cddfSDavid du Colombier 		// commas separate addresses
7557dd7cddfSDavid du Colombier 		if(!quoted && c == ',' && !inanticomment){
7567dd7cddfSDavid du Colombier 			s_terminate(s);
7577dd7cddfSDavid du Colombier 			addrdone = 0;
7587dd7cddfSDavid du Colombier 			if(n++ != 0)
7597dd7cddfSDavid du Colombier 				s_append(list, " ");
7607dd7cddfSDavid du Colombier 			s_append(list, s_to_c(s));
7617dd7cddfSDavid du Colombier 			s = s_reset(s);
7627dd7cddfSDavid du Colombier 			continue;
7637dd7cddfSDavid du Colombier 		}
7647dd7cddfSDavid du Colombier 
7657dd7cddfSDavid du Colombier 		// what's left is part of the address
7667dd7cddfSDavid du Colombier 		s_putc(s, c);
7677dd7cddfSDavid du Colombier 
7687dd7cddfSDavid du Colombier 		// quoted characters are recognized only as characters
7697dd7cddfSDavid du Colombier 		if(c == '\\')
7707dd7cddfSDavid du Colombier 			quoted = 1;
7717dd7cddfSDavid du Colombier 		else
7727dd7cddfSDavid du Colombier 			quoted = 0;
7737dd7cddfSDavid du Colombier 
7747dd7cddfSDavid du Colombier 	}
7757dd7cddfSDavid du Colombier 
7767dd7cddfSDavid du Colombier 	if(*s_to_c(s) != 0){
7777dd7cddfSDavid du Colombier 		s_terminate(s);
7787dd7cddfSDavid du Colombier 		if(n++ != 0)
7797dd7cddfSDavid du Colombier 			s_append(list, " ");
7807dd7cddfSDavid du Colombier 		s_append(list, s_to_c(s));
7817dd7cddfSDavid du Colombier 	}
7827dd7cddfSDavid du Colombier 	s_free(s);
7837dd7cddfSDavid du Colombier 
7847dd7cddfSDavid du Colombier 	if(n == 0){
7857dd7cddfSDavid du Colombier 		s_free(list);
7867dd7cddfSDavid du Colombier 		return nil;
7877dd7cddfSDavid du Colombier 	}
7887dd7cddfSDavid du Colombier 	return list;
7897dd7cddfSDavid du Colombier }
7907dd7cddfSDavid du Colombier 
7917dd7cddfSDavid du Colombier static void
7927dd7cddfSDavid du Colombier to822(Message *m, Header *h, char *p)
7937dd7cddfSDavid du Colombier {
7947dd7cddfSDavid du Colombier 	p += strlen(h->type);
7957dd7cddfSDavid du Colombier 	s_free(m->to822);
7967dd7cddfSDavid du Colombier 	m->to822 = addr822(p);
7977dd7cddfSDavid du Colombier }
7987dd7cddfSDavid du Colombier 
7997dd7cddfSDavid du Colombier static void
8007dd7cddfSDavid du Colombier cc822(Message *m, Header *h, char *p)
8017dd7cddfSDavid du Colombier {
8027dd7cddfSDavid du Colombier 	p += strlen(h->type);
8037dd7cddfSDavid du Colombier 	s_free(m->cc822);
8047dd7cddfSDavid du Colombier 	m->cc822 = addr822(p);
8057dd7cddfSDavid du Colombier }
8067dd7cddfSDavid du Colombier 
8077dd7cddfSDavid du Colombier static void
8087dd7cddfSDavid du Colombier bcc822(Message *m, Header *h, char *p)
8097dd7cddfSDavid du Colombier {
8107dd7cddfSDavid du Colombier 	p += strlen(h->type);
8117dd7cddfSDavid du Colombier 	s_free(m->bcc822);
8127dd7cddfSDavid du Colombier 	m->bcc822 = addr822(p);
8137dd7cddfSDavid du Colombier }
8147dd7cddfSDavid du Colombier 
8157dd7cddfSDavid du Colombier static void
8167dd7cddfSDavid du Colombier from822(Message *m, Header *h, char *p)
8177dd7cddfSDavid du Colombier {
8187dd7cddfSDavid du Colombier 	p += strlen(h->type);
8197dd7cddfSDavid du Colombier 	s_free(m->from822);
8207dd7cddfSDavid du Colombier 	m->from822 = addr822(p);
8217dd7cddfSDavid du Colombier }
8227dd7cddfSDavid du Colombier 
8237dd7cddfSDavid du Colombier static void
8247dd7cddfSDavid du Colombier sender822(Message *m, Header *h, char *p)
8257dd7cddfSDavid du Colombier {
8267dd7cddfSDavid du Colombier 	p += strlen(h->type);
8277dd7cddfSDavid du Colombier 	s_free(m->sender822);
8287dd7cddfSDavid du Colombier 	m->sender822 = addr822(p);
8297dd7cddfSDavid du Colombier }
8307dd7cddfSDavid du Colombier 
8317dd7cddfSDavid du Colombier static void
8327dd7cddfSDavid du Colombier replyto822(Message *m, Header *h, char *p)
8337dd7cddfSDavid du Colombier {
8347dd7cddfSDavid du Colombier 	p += strlen(h->type);
8357dd7cddfSDavid du Colombier 	s_free(m->replyto822);
8367dd7cddfSDavid du Colombier 	m->replyto822 = addr822(p);
8377dd7cddfSDavid du Colombier }
8387dd7cddfSDavid du Colombier 
8397dd7cddfSDavid du Colombier static void
8407dd7cddfSDavid du Colombier mimeversion(Message *m, Header *h, char *p)
8417dd7cddfSDavid du Colombier {
8427dd7cddfSDavid du Colombier 	p += strlen(h->type);
8437dd7cddfSDavid du Colombier 	s_free(m->mimeversion);
8447dd7cddfSDavid du Colombier 	m->mimeversion = addr822(p);
8457dd7cddfSDavid du Colombier }
8467dd7cddfSDavid du Colombier 
8477dd7cddfSDavid du Colombier static void
8487dd7cddfSDavid du Colombier killtrailingwhite(char *p)
8497dd7cddfSDavid du Colombier {
8507dd7cddfSDavid du Colombier 	char *e;
8517dd7cddfSDavid du Colombier 
8527dd7cddfSDavid du Colombier 	e = p + strlen(p) - 1;
8537dd7cddfSDavid du Colombier 	while(e > p && isspace(*e))
8547dd7cddfSDavid du Colombier 		*e-- = 0;
8557dd7cddfSDavid du Colombier }
8567dd7cddfSDavid du Colombier 
8577dd7cddfSDavid du Colombier static void
8587dd7cddfSDavid du Colombier date822(Message *m, Header *h, char *p)
8597dd7cddfSDavid du Colombier {
8607dd7cddfSDavid du Colombier 	p += strlen(h->type);
8617dd7cddfSDavid du Colombier 	p = skipwhite(p);
8627dd7cddfSDavid du Colombier 	s_free(m->date822);
8637dd7cddfSDavid du Colombier 	m->date822 = s_copy(p);
8647dd7cddfSDavid du Colombier 	p = s_to_c(m->date822);
8657dd7cddfSDavid du Colombier 	killtrailingwhite(p);
8667dd7cddfSDavid du Colombier }
8677dd7cddfSDavid du Colombier 
8687dd7cddfSDavid du Colombier static void
8697dd7cddfSDavid du Colombier subject822(Message *m, Header *h, char *p)
8707dd7cddfSDavid du Colombier {
8717dd7cddfSDavid du Colombier 	p += strlen(h->type);
8727dd7cddfSDavid du Colombier 	p = skipwhite(p);
8737dd7cddfSDavid du Colombier 	s_free(m->subject822);
8747dd7cddfSDavid du Colombier 	m->subject822 = s_copy(p);
8757dd7cddfSDavid du Colombier 	p = s_to_c(m->subject822);
8767dd7cddfSDavid du Colombier 	killtrailingwhite(p);
8777dd7cddfSDavid du Colombier }
8787dd7cddfSDavid du Colombier 
8797dd7cddfSDavid du Colombier static void
8807dd7cddfSDavid du Colombier inreplyto822(Message *m, Header *h, char *p)
8817dd7cddfSDavid du Colombier {
8827dd7cddfSDavid du Colombier 	p += strlen(h->type);
8837dd7cddfSDavid du Colombier 	p = skipwhite(p);
8847dd7cddfSDavid du Colombier 	s_free(m->inreplyto822);
8857dd7cddfSDavid du Colombier 	m->inreplyto822 = s_copy(p);
8867dd7cddfSDavid du Colombier 	p = s_to_c(m->inreplyto822);
8877dd7cddfSDavid du Colombier 	killtrailingwhite(p);
8887dd7cddfSDavid du Colombier }
8897dd7cddfSDavid du Colombier 
8907dd7cddfSDavid du Colombier static void
8917dd7cddfSDavid du Colombier messageid822(Message *m, Header *h, char *p)
8927dd7cddfSDavid du Colombier {
8937dd7cddfSDavid du Colombier 	p += strlen(h->type);
8947dd7cddfSDavid du Colombier 	p = skipwhite(p);
8957dd7cddfSDavid du Colombier 	s_free(m->messageid822);
8967dd7cddfSDavid du Colombier 	m->messageid822 = s_copy(p);
8977dd7cddfSDavid du Colombier 	p = s_to_c(m->messageid822);
8987dd7cddfSDavid du Colombier 	killtrailingwhite(p);
8997dd7cddfSDavid du Colombier }
9007dd7cddfSDavid du Colombier 
9017dd7cddfSDavid du Colombier static int
9027dd7cddfSDavid du Colombier isattribute(char **pp, char *attr)
9037dd7cddfSDavid du Colombier {
9047dd7cddfSDavid du Colombier 	char *p;
9057dd7cddfSDavid du Colombier 	int n;
9067dd7cddfSDavid du Colombier 
9077dd7cddfSDavid du Colombier 	n = strlen(attr);
9087dd7cddfSDavid du Colombier 	p = *pp;
9097dd7cddfSDavid du Colombier 	if(cistrncmp(p, attr, n) != 0)
9107dd7cddfSDavid du Colombier 		return 0;
9117dd7cddfSDavid du Colombier 	p += n;
9127dd7cddfSDavid du Colombier 	while(*p == ' ')
9137dd7cddfSDavid du Colombier 		p++;
9147dd7cddfSDavid du Colombier 	if(*p++ != '=')
9157dd7cddfSDavid du Colombier 		return 0;
9167dd7cddfSDavid du Colombier 	while(*p == ' ')
9177dd7cddfSDavid du Colombier 		p++;
9187dd7cddfSDavid du Colombier 	*pp = p;
9197dd7cddfSDavid du Colombier 	return 1;
9207dd7cddfSDavid du Colombier }
9217dd7cddfSDavid du Colombier 
9227dd7cddfSDavid du Colombier static void
9237dd7cddfSDavid du Colombier ctype(Message *m, Header *h, char *p)
9247dd7cddfSDavid du Colombier {
9257dd7cddfSDavid du Colombier 	String *s;
9267dd7cddfSDavid du Colombier 
9277dd7cddfSDavid du Colombier 	p += h->len;
9287dd7cddfSDavid du Colombier 	p = skipwhite(p);
9297dd7cddfSDavid du Colombier 
9307dd7cddfSDavid du Colombier 	p = getstring(p, m->type, 1);
9317dd7cddfSDavid du Colombier 
9327dd7cddfSDavid du Colombier 	while(*p){
9337dd7cddfSDavid du Colombier 		if(isattribute(&p, "boundary")){
9347dd7cddfSDavid du Colombier 			s = s_new();
9357dd7cddfSDavid du Colombier 			p = getstring(p, s, 0);
9367dd7cddfSDavid du Colombier 			m->boundary = s_reset(m->boundary);
9377dd7cddfSDavid du Colombier 			s_append(m->boundary, "--");
9387dd7cddfSDavid du Colombier 			s_append(m->boundary, s_to_c(s));
9397dd7cddfSDavid du Colombier 			s_free(s);
9407dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "multipart", 9) == 0){
9417dd7cddfSDavid du Colombier 			/*
9427dd7cddfSDavid du Colombier 			 *  the first unbounded part of a multipart message,
9437dd7cddfSDavid du Colombier 			 *  the preamble, is not displayed or saved
9447dd7cddfSDavid du Colombier 			 */
9457dd7cddfSDavid du Colombier 		} else if(isattribute(&p, "name")){
9467dd7cddfSDavid du Colombier 			if(m->filename == nil)
9477dd7cddfSDavid du Colombier 				setfilename(m, p);
9487dd7cddfSDavid du Colombier 		} else if(isattribute(&p, "charset")){
9497dd7cddfSDavid du Colombier 			p = getstring(p, s_reset(m->charset), 0);
9507dd7cddfSDavid du Colombier 		}
9517dd7cddfSDavid du Colombier 
9527dd7cddfSDavid du Colombier 		p = skiptosemi(p);
9537dd7cddfSDavid du Colombier 	}
9547dd7cddfSDavid du Colombier }
9557dd7cddfSDavid du Colombier 
9567dd7cddfSDavid du Colombier static void
9577dd7cddfSDavid du Colombier cencoding(Message *m, Header *h, char *p)
9587dd7cddfSDavid du Colombier {
9597dd7cddfSDavid du Colombier 	p += h->len;
9607dd7cddfSDavid du Colombier 	p = skipwhite(p);
9617dd7cddfSDavid du Colombier 	if(cistrncmp(p, "base64", 6) == 0)
9627dd7cddfSDavid du Colombier 		m->encoding = Ebase64;
9637dd7cddfSDavid du Colombier 	else if(cistrncmp(p, "quoted-printable", 16) == 0)
9647dd7cddfSDavid du Colombier 		m->encoding = Equoted;
9657dd7cddfSDavid du Colombier }
9667dd7cddfSDavid du Colombier 
9677dd7cddfSDavid du Colombier static void
9687dd7cddfSDavid du Colombier cdisposition(Message *m, Header *h, char *p)
9697dd7cddfSDavid du Colombier {
9707dd7cddfSDavid du Colombier 	p += h->len;
9717dd7cddfSDavid du Colombier 	p = skipwhite(p);
9727dd7cddfSDavid du Colombier 	while(*p){
9737dd7cddfSDavid du Colombier 		if(cistrncmp(p, "inline", 6) == 0){
9747dd7cddfSDavid du Colombier 			m->disposition = Dinline;
9757dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "attachment", 10) == 0){
9767dd7cddfSDavid du Colombier 			m->disposition = Dfile;
9777dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "filename=", 9) == 0){
9787dd7cddfSDavid du Colombier 			p += 9;
9797dd7cddfSDavid du Colombier 			setfilename(m, p);
9807dd7cddfSDavid du Colombier 		}
9817dd7cddfSDavid du Colombier 		p = skiptosemi(p);
9827dd7cddfSDavid du Colombier 	}
9837dd7cddfSDavid du Colombier 
9847dd7cddfSDavid du Colombier }
9857dd7cddfSDavid du Colombier 
9867dd7cddfSDavid du Colombier ulong msgallocd, msgfreed;
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier Message*
9897dd7cddfSDavid du Colombier newmessage(Message *parent)
9907dd7cddfSDavid du Colombier {
9917dd7cddfSDavid du Colombier 	static int id;
9927dd7cddfSDavid du Colombier 	Message *m;
9937dd7cddfSDavid du Colombier 
9947dd7cddfSDavid du Colombier 	msgallocd++;
9957dd7cddfSDavid du Colombier 
9967dd7cddfSDavid du Colombier 	m = emalloc(sizeof(*m));
9977dd7cddfSDavid du Colombier 	memset(m, 0, sizeof(*m));
9987dd7cddfSDavid du Colombier 	m->disposition = Dnone;
9997dd7cddfSDavid du Colombier 	m->type = s_copy("text/plain");
10007dd7cddfSDavid du Colombier 	m->charset = s_copy("iso-8859-1");
10017dd7cddfSDavid du Colombier 	m->id = newid();
10027dd7cddfSDavid du Colombier 	if(parent)
10037dd7cddfSDavid du Colombier 		sprint(m->name, "%d", ++(parent->subname));
10047dd7cddfSDavid du Colombier 	if(parent == nil)
10057dd7cddfSDavid du Colombier 		parent = m;
10067dd7cddfSDavid du Colombier 	m->whole = parent;
10077dd7cddfSDavid du Colombier 	m->hlen = -1;
10087dd7cddfSDavid du Colombier 	return m;
10097dd7cddfSDavid du Colombier }
10107dd7cddfSDavid du Colombier 
10117dd7cddfSDavid du Colombier // delete a message from a mailbox
10127dd7cddfSDavid du Colombier void
10137dd7cddfSDavid du Colombier delmessage(Mailbox *mb, Message *m)
10147dd7cddfSDavid du Colombier {
10157dd7cddfSDavid du Colombier 	Message **l;
10167dd7cddfSDavid du Colombier 	int i;
10177dd7cddfSDavid du Colombier 
10187dd7cddfSDavid du Colombier 	mb->vers++;
10197dd7cddfSDavid du Colombier 	msgfreed++;
10207dd7cddfSDavid du Colombier 
10217dd7cddfSDavid du Colombier 	if(m->whole != m){
10227dd7cddfSDavid du Colombier 		// unchain from parent
10237dd7cddfSDavid du Colombier 		for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
10247dd7cddfSDavid du Colombier 			;
10257dd7cddfSDavid du Colombier 		if(*l != nil)
10267dd7cddfSDavid du Colombier 			*l = m->next;
10277dd7cddfSDavid du Colombier 
10287dd7cddfSDavid du Colombier 		// clear out of name lookup hash table
10297dd7cddfSDavid du Colombier 		if(m->whole->whole == m->whole)
10307dd7cddfSDavid du Colombier 			hfree(CHDIR|PATH(mb->id, Qmbox), m->name);
10317dd7cddfSDavid du Colombier 		else
10327dd7cddfSDavid du Colombier 			hfree(CHDIR|PATH(m->whole->id, Qdir), m->name);
10337dd7cddfSDavid du Colombier 		for(i = 0; i < Qmax; i++)
10347dd7cddfSDavid du Colombier 			hfree(CHDIR|PATH(m->id, Qdir), dirtab[i]);
10357dd7cddfSDavid du Colombier 	}
10367dd7cddfSDavid du Colombier 
10377dd7cddfSDavid du Colombier 	/* recurse through sub-parts */
10387dd7cddfSDavid du Colombier 	while(m->part)
10397dd7cddfSDavid du Colombier 		delmessage(mb, m->part);
10407dd7cddfSDavid du Colombier 
10417dd7cddfSDavid du Colombier 	/* free memory */
10427dd7cddfSDavid du Colombier 	if(m->mallocd)
10437dd7cddfSDavid du Colombier 		free(m->start);
10447dd7cddfSDavid du Colombier 	if(m->hallocd)
10457dd7cddfSDavid du Colombier 		free(m->header);
10467dd7cddfSDavid du Colombier 	if(m->ballocd)
10477dd7cddfSDavid du Colombier 		free(m->body);
10487dd7cddfSDavid du Colombier 	s_free(m->unixfrom);
10497dd7cddfSDavid du Colombier 	s_free(m->unixdate);
10507dd7cddfSDavid du Colombier 	s_free(m->from822);
10517dd7cddfSDavid du Colombier 	s_free(m->sender822);
10527dd7cddfSDavid du Colombier 	s_free(m->to822);
10537dd7cddfSDavid du Colombier 	s_free(m->cc822);
10547dd7cddfSDavid du Colombier 	s_free(m->bcc822);
10557dd7cddfSDavid du Colombier 	s_free(m->replyto822);
10567dd7cddfSDavid du Colombier 	s_free(m->date822);
10577dd7cddfSDavid du Colombier 	s_free(m->subject822);
10587dd7cddfSDavid du Colombier 	s_free(m->inreplyto822);
10597dd7cddfSDavid du Colombier 	s_free(m->messageid822);
10607dd7cddfSDavid du Colombier 	s_free(m->addrs);
10617dd7cddfSDavid du Colombier 	s_free(m->mimeversion);
10627dd7cddfSDavid du Colombier 	s_free(m->boundary);
10637dd7cddfSDavid du Colombier 	s_free(m->type);
10647dd7cddfSDavid du Colombier 	s_free(m->charset);
10657dd7cddfSDavid du Colombier 	s_free(m->filename);
10667dd7cddfSDavid du Colombier 
10677dd7cddfSDavid du Colombier 	free(m);
10687dd7cddfSDavid du Colombier }
10697dd7cddfSDavid du Colombier 
10707dd7cddfSDavid du Colombier // mark messages (identified by path) for deletion
10717dd7cddfSDavid du Colombier void
10727dd7cddfSDavid du Colombier delmessages(int ac, char **av)
10737dd7cddfSDavid du Colombier {
10747dd7cddfSDavid du Colombier 	Mailbox *mb;
10757dd7cddfSDavid du Colombier 	Message *m;
10767dd7cddfSDavid du Colombier 	int i, needwrite;
10777dd7cddfSDavid du Colombier 
10787dd7cddfSDavid du Colombier 	qlock(&mbllock);
10797dd7cddfSDavid du Colombier 	for(mb = mbl; mb != nil; mb = mb->next)
10807dd7cddfSDavid du Colombier 		if(strcmp(av[0], mb->name) == 0){
10817dd7cddfSDavid du Colombier 			qlock(mb);
10827dd7cddfSDavid du Colombier 			break;
10837dd7cddfSDavid du Colombier 		}
10847dd7cddfSDavid du Colombier 	qunlock(&mbllock);
10857dd7cddfSDavid du Colombier 	if(mb == nil)
10867dd7cddfSDavid du Colombier 		return;
10877dd7cddfSDavid du Colombier 
10887dd7cddfSDavid du Colombier 	needwrite = 0;
10897dd7cddfSDavid du Colombier 	for(i = 1; i < ac; i++){
10907dd7cddfSDavid du Colombier 		for(m = mb->root->part; m != nil; m = m->next)
10917dd7cddfSDavid du Colombier 			if(strcmp(m->name, av[i]) == 0){
10927dd7cddfSDavid du Colombier 				if(!m->deleted){
10937dd7cddfSDavid du Colombier 					mailplumb(mb, m, 1);
10947dd7cddfSDavid du Colombier 					needwrite = 1;
10957dd7cddfSDavid du Colombier 					m->deleted = 1;
10967dd7cddfSDavid du Colombier 				}
10977dd7cddfSDavid du Colombier 				break;
10987dd7cddfSDavid du Colombier 			}
10997dd7cddfSDavid du Colombier 	}
11007dd7cddfSDavid du Colombier 	if(needwrite)
11017dd7cddfSDavid du Colombier 		syncmbox(mb, 1);
11027dd7cddfSDavid du Colombier 	qunlock(mb);
11037dd7cddfSDavid du Colombier }
11047dd7cddfSDavid du Colombier 
11057dd7cddfSDavid du Colombier /*
11067dd7cddfSDavid du Colombier  *  the following are called with the mailbox qlocked
11077dd7cddfSDavid du Colombier  */
11087dd7cddfSDavid du Colombier void
11097dd7cddfSDavid du Colombier msgincref(Message *m)
11107dd7cddfSDavid du Colombier {
11117dd7cddfSDavid du Colombier 	m->refs++;
11127dd7cddfSDavid du Colombier }
11137dd7cddfSDavid du Colombier void
11147dd7cddfSDavid du Colombier msgdecref(Mailbox *mb, Message *m)
11157dd7cddfSDavid du Colombier {
11167dd7cddfSDavid du Colombier 	m->refs--;
11177dd7cddfSDavid du Colombier 	if(m->refs == 0 && m->deleted)
11187dd7cddfSDavid du Colombier 		syncmbox(mb, 1);
11197dd7cddfSDavid du Colombier }
11207dd7cddfSDavid du Colombier 
11217dd7cddfSDavid du Colombier /*
11227dd7cddfSDavid du Colombier  *  the following are called with mbllock'd
11237dd7cddfSDavid du Colombier  */
11247dd7cddfSDavid du Colombier void
11257dd7cddfSDavid du Colombier mboxincref(Mailbox *mb)
11267dd7cddfSDavid du Colombier {
11277dd7cddfSDavid du Colombier 	mb->refs++;
11287dd7cddfSDavid du Colombier }
11297dd7cddfSDavid du Colombier void
11307dd7cddfSDavid du Colombier mboxdecref(Mailbox *mb)
11317dd7cddfSDavid du Colombier {
11327dd7cddfSDavid du Colombier 	Mailbox **l;
11337dd7cddfSDavid du Colombier 
11347dd7cddfSDavid du Colombier 	qlock(mb);
11357dd7cddfSDavid du Colombier 	mb->refs--;
11367dd7cddfSDavid du Colombier 	if(mb->refs == 0){
11377dd7cddfSDavid du Colombier 		for(l = &mbl; *l != nil; l = &(*l)->next){
11387dd7cddfSDavid du Colombier 			if(*l == mb){
11397dd7cddfSDavid du Colombier 				*l = mb->next;
11407dd7cddfSDavid du Colombier 				break;
11417dd7cddfSDavid du Colombier 			}
11427dd7cddfSDavid du Colombier 		}
11437dd7cddfSDavid du Colombier 		delmessage(mb, mb->root);
11447dd7cddfSDavid du Colombier 		qunlock(mb);
11457dd7cddfSDavid du Colombier 		free(mb);
11467dd7cddfSDavid du Colombier 	} else
11477dd7cddfSDavid du Colombier 		qunlock(mb);
11487dd7cddfSDavid du Colombier }
11497dd7cddfSDavid du Colombier 
11507dd7cddfSDavid du Colombier int
11517dd7cddfSDavid du Colombier cistrncmp(char *a, char *b, int n)
11527dd7cddfSDavid du Colombier {
11537dd7cddfSDavid du Colombier 	while(n-- > 0){
11547dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
11557dd7cddfSDavid du Colombier 			return -1;
11567dd7cddfSDavid du Colombier 	}
11577dd7cddfSDavid du Colombier 	return 0;
11587dd7cddfSDavid du Colombier }
11597dd7cddfSDavid du Colombier 
11607dd7cddfSDavid du Colombier int
11617dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
11627dd7cddfSDavid du Colombier {
11637dd7cddfSDavid du Colombier 	for(;;){
11647dd7cddfSDavid du Colombier 		if(tolower(*a) != tolower(*b++))
11657dd7cddfSDavid du Colombier 			return -1;
11667dd7cddfSDavid du Colombier 		if(*a++ == 0)
11677dd7cddfSDavid du Colombier 			break;
11687dd7cddfSDavid du Colombier 	}
11697dd7cddfSDavid du Colombier 	return 0;
11707dd7cddfSDavid du Colombier }
11717dd7cddfSDavid du Colombier 
11727dd7cddfSDavid du Colombier static char*
11737dd7cddfSDavid du Colombier skipwhite(char *p)
11747dd7cddfSDavid du Colombier {
11757dd7cddfSDavid du Colombier 	while(isspace(*p))
11767dd7cddfSDavid du Colombier 		p++;
11777dd7cddfSDavid du Colombier 	return p;
11787dd7cddfSDavid du Colombier }
11797dd7cddfSDavid du Colombier 
11807dd7cddfSDavid du Colombier static char*
11817dd7cddfSDavid du Colombier skiptosemi(char *p)
11827dd7cddfSDavid du Colombier {
11837dd7cddfSDavid du Colombier 	while(*p && *p != ';')
11847dd7cddfSDavid du Colombier 		p++;
11857dd7cddfSDavid du Colombier 	while(*p == ';' || isspace(*p))
11867dd7cddfSDavid du Colombier 		p++;
11877dd7cddfSDavid du Colombier 	return p;
11887dd7cddfSDavid du Colombier }
11897dd7cddfSDavid du Colombier 
11907dd7cddfSDavid du Colombier static char*
11917dd7cddfSDavid du Colombier getstring(char *p, String *s, int dolower)
11927dd7cddfSDavid du Colombier {
11937dd7cddfSDavid du Colombier 	s = s_reset(s);
11947dd7cddfSDavid du Colombier 	p = skipwhite(p);
11957dd7cddfSDavid du Colombier 	if(*p == '"'){
11967dd7cddfSDavid du Colombier 		p++;
11977dd7cddfSDavid du Colombier 		for(;*p && *p != '"'; p++)
11987dd7cddfSDavid du Colombier 			if(dolower)
11997dd7cddfSDavid du Colombier 				s_putc(s, tolower(*p));
12007dd7cddfSDavid du Colombier 			else
12017dd7cddfSDavid du Colombier 				s_putc(s, *p);
12027dd7cddfSDavid du Colombier 		if(*p == '"')
12037dd7cddfSDavid du Colombier 			p++;
12047dd7cddfSDavid du Colombier 		s_terminate(s);
12057dd7cddfSDavid du Colombier 
12067dd7cddfSDavid du Colombier 		return p;
12077dd7cddfSDavid du Colombier 	}
12087dd7cddfSDavid du Colombier 
12097dd7cddfSDavid du Colombier 	for(;!isspace(*p) && *p != ';'; p++)
12107dd7cddfSDavid du Colombier 		if(dolower)
12117dd7cddfSDavid du Colombier 			s_putc(s, tolower(*p));
12127dd7cddfSDavid du Colombier 		else
12137dd7cddfSDavid du Colombier 			s_putc(s, *p);
12147dd7cddfSDavid du Colombier 	s_terminate(s);
12157dd7cddfSDavid du Colombier 
12167dd7cddfSDavid du Colombier 	return p;
12177dd7cddfSDavid du Colombier }
12187dd7cddfSDavid du Colombier 
12197dd7cddfSDavid du Colombier static void
12207dd7cddfSDavid du Colombier setfilename(Message *m, char *p)
12217dd7cddfSDavid du Colombier {
12227dd7cddfSDavid du Colombier 	m->filename = s_reset(m->filename);
12237dd7cddfSDavid du Colombier 	getstring(p, m->filename, 0);
12247dd7cddfSDavid du Colombier 	for(p = s_to_c(m->filename); *p; p++)
12257dd7cddfSDavid du Colombier 		if(*p == ' ' || *p == '\t' || *p == ';')
12267dd7cddfSDavid du Colombier 			*p = '_';
12277dd7cddfSDavid du Colombier }
12287dd7cddfSDavid du Colombier 
12297dd7cddfSDavid du Colombier //
12307dd7cddfSDavid du Colombier // undecode message body
12317dd7cddfSDavid du Colombier //
12327dd7cddfSDavid du Colombier void
12337dd7cddfSDavid du Colombier decode(Message *m)
12347dd7cddfSDavid du Colombier {
12357dd7cddfSDavid du Colombier 	int i, len;
12367dd7cddfSDavid du Colombier 	char *x;
12377dd7cddfSDavid du Colombier 
12387dd7cddfSDavid du Colombier 	if(m->decoded)
12397dd7cddfSDavid du Colombier 		return;
12407dd7cddfSDavid du Colombier 	switch(m->encoding){
12417dd7cddfSDavid du Colombier 	case Ebase64:
12427dd7cddfSDavid du Colombier 		len = m->bend - m->body;
12437dd7cddfSDavid du Colombier 		i = (len*3)/4+1;	// room for max chars + null
12447dd7cddfSDavid du Colombier 		x = emalloc(i);
12457dd7cddfSDavid du Colombier 		len = dec64((uchar*)x, i, m->body, len);
12467dd7cddfSDavid du Colombier 		if(m->ballocd)
12477dd7cddfSDavid du Colombier 			free(m->body);
12487dd7cddfSDavid du Colombier 		m->body = x;
12497dd7cddfSDavid du Colombier 		m->bend = x + len;
12507dd7cddfSDavid du Colombier 		m->ballocd = 1;
12517dd7cddfSDavid du Colombier 		break;
12527dd7cddfSDavid du Colombier 	case Equoted:
12537dd7cddfSDavid du Colombier 		len = m->bend - m->body;
12547dd7cddfSDavid du Colombier 		x = emalloc(len+2);	// room for null and possible extra nl
12557dd7cddfSDavid du Colombier 		len = decquoted(x, m->body, m->bend);
12567dd7cddfSDavid du Colombier 		if(m->ballocd)
12577dd7cddfSDavid du Colombier 			free(m->body);
12587dd7cddfSDavid du Colombier 		m->body = x;
12597dd7cddfSDavid du Colombier 		m->bend = x + len;
12607dd7cddfSDavid du Colombier 		m->ballocd = 1;
12617dd7cddfSDavid du Colombier 		break;
12627dd7cddfSDavid du Colombier 	default:
12637dd7cddfSDavid du Colombier 		break;
12647dd7cddfSDavid du Colombier 	}
12657dd7cddfSDavid du Colombier 	m->decoded = 1;
12667dd7cddfSDavid du Colombier }
12677dd7cddfSDavid du Colombier 
12687dd7cddfSDavid du Colombier // convert latin1 to utf
12697dd7cddfSDavid du Colombier void
12707dd7cddfSDavid du Colombier convert(Message *m)
12717dd7cddfSDavid du Colombier {
12727dd7cddfSDavid du Colombier 	int len;
12737dd7cddfSDavid du Colombier 	char *x;
12747dd7cddfSDavid du Colombier 
12757dd7cddfSDavid du Colombier 	// don't convert if we're not a leaf, not text, or already converted
12767dd7cddfSDavid du Colombier 	if(m->converted)
12777dd7cddfSDavid du Colombier 		return;
12787dd7cddfSDavid du Colombier 	if(m->part != nil)
12797dd7cddfSDavid du Colombier 		return;
12807dd7cddfSDavid du Colombier 	if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
12817dd7cddfSDavid du Colombier 		return;
12827dd7cddfSDavid du Colombier 
12837dd7cddfSDavid du Colombier 	if(cistrcmp(s_to_c(m->charset), "us-ascii") == 0 ||
12847dd7cddfSDavid du Colombier 	   cistrcmp(s_to_c(m->charset), "iso-8859-1") == 0){
12857dd7cddfSDavid du Colombier 		len = is8bit(m);
12867dd7cddfSDavid du Colombier 		if(len > 0){
12877dd7cddfSDavid du Colombier 			len = 2*len + m->bend - m->body + 1;
12887dd7cddfSDavid du Colombier 			x = emalloc(len);
12897dd7cddfSDavid du Colombier 			len = latin1toutf(x, m->body, m->bend);
12907dd7cddfSDavid du Colombier 			if(m->ballocd)
12917dd7cddfSDavid du Colombier 				free(m->body);
12927dd7cddfSDavid du Colombier 			m->body = x;
12937dd7cddfSDavid du Colombier 			m->bend = x + len;
12947dd7cddfSDavid du Colombier 			m->ballocd = 1;
12957dd7cddfSDavid du Colombier 		}
12967dd7cddfSDavid du Colombier 	} else if(cistrcmp(s_to_c(m->charset), "big5") == 0){
12977dd7cddfSDavid du Colombier 		len = xtoutf("big5", &x, m->body, m->bend);
12987dd7cddfSDavid du Colombier 		if(len != 0){
12997dd7cddfSDavid du Colombier 			if(m->ballocd)
13007dd7cddfSDavid du Colombier 				free(m->body);
13017dd7cddfSDavid du Colombier 			m->body = x;
13027dd7cddfSDavid du Colombier 			m->bend = x + len;
13037dd7cddfSDavid du Colombier 			m->ballocd = 1;
13047dd7cddfSDavid du Colombier 		}
13057dd7cddfSDavid du Colombier 	} else if(cistrcmp(s_to_c(m->charset), "windows-1257") == 0){
13067dd7cddfSDavid du Colombier 		len = is8bit(m);
13077dd7cddfSDavid du Colombier 		if(len > 0){
13087dd7cddfSDavid du Colombier 			len = 2*len + m->bend - m->body + 1;
13097dd7cddfSDavid du Colombier 			x = emalloc(len);
13107dd7cddfSDavid du Colombier 			len = windows1257toutf(x, m->body, m->bend);
13117dd7cddfSDavid du Colombier 			if(m->ballocd)
13127dd7cddfSDavid du Colombier 				free(m->body);
13137dd7cddfSDavid du Colombier 			m->body = x;
13147dd7cddfSDavid du Colombier 			m->bend = x + len;
13157dd7cddfSDavid du Colombier 			m->ballocd = 1;
13167dd7cddfSDavid du Colombier 		}
13177dd7cddfSDavid du Colombier 	}
13187dd7cddfSDavid du Colombier 
13197dd7cddfSDavid du Colombier 	m->converted = 1;
13207dd7cddfSDavid du Colombier }
13217dd7cddfSDavid du Colombier 
13227dd7cddfSDavid du Colombier enum
13237dd7cddfSDavid du Colombier {
13247dd7cddfSDavid du Colombier 	Self=	1,
13257dd7cddfSDavid du Colombier 	Hex=	2,
13267dd7cddfSDavid du Colombier };
13277dd7cddfSDavid du Colombier uchar	tableqp[256];
13287dd7cddfSDavid du Colombier 
13297dd7cddfSDavid du Colombier static void
13307dd7cddfSDavid du Colombier initquoted(void)
13317dd7cddfSDavid du Colombier {
13327dd7cddfSDavid du Colombier 	int c;
13337dd7cddfSDavid du Colombier 
13347dd7cddfSDavid du Colombier 	memset(tableqp, 0, 256);
13357dd7cddfSDavid du Colombier 	for(c = ' '; c <= '<'; c++)
13367dd7cddfSDavid du Colombier 		tableqp[c] = Self;
13377dd7cddfSDavid du Colombier 	for(c = '>'; c <= '~'; c++)
13387dd7cddfSDavid du Colombier 		tableqp[c] = Self;
13397dd7cddfSDavid du Colombier 	tableqp['\t'] = Self;
13407dd7cddfSDavid du Colombier 	tableqp['='] = Hex;
13417dd7cddfSDavid du Colombier }
13427dd7cddfSDavid du Colombier 
13437dd7cddfSDavid du Colombier static int
13447dd7cddfSDavid du Colombier hex2int(int x)
13457dd7cddfSDavid du Colombier {
13467dd7cddfSDavid du Colombier 	if(x >= '0' && x <= '9')
13477dd7cddfSDavid du Colombier 		return x - '0';
13487dd7cddfSDavid du Colombier 	if(x >= 'A' && x <= 'F')
13497dd7cddfSDavid du Colombier 		return (x - 'A') + 10;
13507dd7cddfSDavid du Colombier 	if(x >= 'a' && x <= 'f')
13517dd7cddfSDavid du Colombier 		return (x - 'a') + 10;
13527dd7cddfSDavid du Colombier 	return 0;
13537dd7cddfSDavid du Colombier }
13547dd7cddfSDavid du Colombier 
13557dd7cddfSDavid du Colombier static char*
13567dd7cddfSDavid du Colombier decquotedline(char *out, char *in, char *e)
13577dd7cddfSDavid du Colombier {
13587dd7cddfSDavid du Colombier 	int c, soft;
13597dd7cddfSDavid du Colombier 
13607dd7cddfSDavid du Colombier 	/* dump trailing white space */
13617dd7cddfSDavid du Colombier 	while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
13627dd7cddfSDavid du Colombier 		e--;
13637dd7cddfSDavid du Colombier 
13647dd7cddfSDavid du Colombier 	/* trailing '=' means no newline */
13657dd7cddfSDavid du Colombier 	if(*e == '='){
13667dd7cddfSDavid du Colombier 		soft = 1;
13677dd7cddfSDavid du Colombier 		e--;
13687dd7cddfSDavid du Colombier 	} else
13697dd7cddfSDavid du Colombier 		soft = 0;
13707dd7cddfSDavid du Colombier 
13717dd7cddfSDavid du Colombier 	while(in <= e){
13727dd7cddfSDavid du Colombier 		c = (*in++) & 0xff;
13737dd7cddfSDavid du Colombier 		switch(tableqp[c]){
13747dd7cddfSDavid du Colombier 		case Self:
13757dd7cddfSDavid du Colombier 			*out++ = c;
13767dd7cddfSDavid du Colombier 			break;
13777dd7cddfSDavid du Colombier 		case Hex:
13787dd7cddfSDavid du Colombier 			c = hex2int(*in++)<<4;
13797dd7cddfSDavid du Colombier 			c |= hex2int(*in++);
13807dd7cddfSDavid du Colombier 			*out++ = c;
13817dd7cddfSDavid du Colombier 			break;
13827dd7cddfSDavid du Colombier 		}
13837dd7cddfSDavid du Colombier 	}
13847dd7cddfSDavid du Colombier 	if(!soft)
13857dd7cddfSDavid du Colombier 		*out++ = '\n';
13867dd7cddfSDavid du Colombier 	*out = 0;
13877dd7cddfSDavid du Colombier 
13887dd7cddfSDavid du Colombier 	return out;
13897dd7cddfSDavid du Colombier }
13907dd7cddfSDavid du Colombier 
13917dd7cddfSDavid du Colombier int
13927dd7cddfSDavid du Colombier decquoted(char *out, char *in, char *e)
13937dd7cddfSDavid du Colombier {
13947dd7cddfSDavid du Colombier 	char *p, *nl;
13957dd7cddfSDavid du Colombier 
13967dd7cddfSDavid du Colombier 	if(tableqp[' '] == 0)
13977dd7cddfSDavid du Colombier 		initquoted();
13987dd7cddfSDavid du Colombier 
13997dd7cddfSDavid du Colombier 	p = out;
14007dd7cddfSDavid du Colombier 	while((nl = strchr(in, '\n')) != nil && nl < e){
14017dd7cddfSDavid du Colombier 		p = decquotedline(p, in, nl);
14027dd7cddfSDavid du Colombier 		in = nl + 1;
14037dd7cddfSDavid du Colombier 	}
14047dd7cddfSDavid du Colombier 	if(in < e)
14057dd7cddfSDavid du Colombier 		p = decquotedline(p, in, e-1);
14067dd7cddfSDavid du Colombier 
14077dd7cddfSDavid du Colombier 	// make sure we end with a new line
14087dd7cddfSDavid du Colombier 	if(*(p-1) != '\n'){
14097dd7cddfSDavid du Colombier 		*p++ = '\n';
14107dd7cddfSDavid du Colombier 		*p = 0;
14117dd7cddfSDavid du Colombier 	}
14127dd7cddfSDavid du Colombier 
14137dd7cddfSDavid du Colombier 	return p - out;
14147dd7cddfSDavid du Colombier }
14157dd7cddfSDavid du Colombier 
14167dd7cddfSDavid du Colombier static char*
14177dd7cddfSDavid du Colombier lowercase(char *p)
14187dd7cddfSDavid du Colombier {
14197dd7cddfSDavid du Colombier 	char *op;
14207dd7cddfSDavid du Colombier 	int c;
14217dd7cddfSDavid du Colombier 
14227dd7cddfSDavid du Colombier 	for(op = p; c = *p; p++)
14237dd7cddfSDavid du Colombier 		if(isupper(c))
14247dd7cddfSDavid du Colombier 			*p = tolower(c);
14257dd7cddfSDavid du Colombier 	return op;
14267dd7cddfSDavid du Colombier }
14277dd7cddfSDavid du Colombier 
14287dd7cddfSDavid du Colombier /*
14297dd7cddfSDavid du Colombier  *  return number of 8 bit characters
14307dd7cddfSDavid du Colombier  */
14317dd7cddfSDavid du Colombier static int
14327dd7cddfSDavid du Colombier is8bit(Message *m)
14337dd7cddfSDavid du Colombier {
14347dd7cddfSDavid du Colombier 	int count = 0;
14357dd7cddfSDavid du Colombier 	char *p;
14367dd7cddfSDavid du Colombier 
14377dd7cddfSDavid du Colombier 	for(p = m->body; p < m->bend; p++)
14387dd7cddfSDavid du Colombier 		if(*p & 0x80)
14397dd7cddfSDavid du Colombier 			count++;
14407dd7cddfSDavid du Colombier 	return count;
14417dd7cddfSDavid du Colombier }
14427dd7cddfSDavid du Colombier 
14437dd7cddfSDavid du Colombier // translate latin1 directly since it fits neatly in utf
14447dd7cddfSDavid du Colombier int
14457dd7cddfSDavid du Colombier latin1toutf(char *out, char *in, char *e)
14467dd7cddfSDavid du Colombier {
14477dd7cddfSDavid du Colombier 	Rune r;
14487dd7cddfSDavid du Colombier 	char *p;
14497dd7cddfSDavid du Colombier 
14507dd7cddfSDavid du Colombier 	p = out;
14517dd7cddfSDavid du Colombier 	for(; in < e; in++){
14527dd7cddfSDavid du Colombier 		r = (*in) & 0xff;
14537dd7cddfSDavid du Colombier 		p += runetochar(p, &r);
14547dd7cddfSDavid du Colombier 	}
14557dd7cddfSDavid du Colombier 	*p = 0;
14567dd7cddfSDavid du Colombier 	return p - out;
14577dd7cddfSDavid du Colombier }
14587dd7cddfSDavid du Colombier 
14597dd7cddfSDavid du Colombier // translate any thing else using the tcs program
14607dd7cddfSDavid du Colombier int
14617dd7cddfSDavid du Colombier xtoutf(char *charset, char **out, char *in, char *e)
14627dd7cddfSDavid du Colombier {
14637dd7cddfSDavid du Colombier 	char *av[4];
14647dd7cddfSDavid du Colombier 	int totcs[2];
14657dd7cddfSDavid du Colombier 	int fromtcs[2];
14667dd7cddfSDavid du Colombier 	int n, len, sofar;
14677dd7cddfSDavid du Colombier 	char *p;
14687dd7cddfSDavid du Colombier 
14697dd7cddfSDavid du Colombier 	len = e-in+1;
14707dd7cddfSDavid du Colombier 	sofar = 0;
14717dd7cddfSDavid du Colombier 	*out = p = malloc(len+1);
14727dd7cddfSDavid du Colombier 	if(p == nil)
14737dd7cddfSDavid du Colombier 		return 0;
14747dd7cddfSDavid du Colombier 
14757dd7cddfSDavid du Colombier 	av[0] = charset;
14767dd7cddfSDavid du Colombier 	av[1] = "-f";
14777dd7cddfSDavid du Colombier 	av[2] = charset;
14787dd7cddfSDavid du Colombier 	av[3] = 0;
14797dd7cddfSDavid du Colombier 	if(pipe(totcs) < 0)
14807dd7cddfSDavid du Colombier 		return 0;
14817dd7cddfSDavid du Colombier 	if(pipe(fromtcs) < 0){
14827dd7cddfSDavid du Colombier 		close(totcs[0]); close(totcs[1]);
14837dd7cddfSDavid du Colombier 		return 0;
14847dd7cddfSDavid du Colombier 	}
14857dd7cddfSDavid du Colombier 	switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
14867dd7cddfSDavid du Colombier 	case -1:
14877dd7cddfSDavid du Colombier 		close(fromtcs[0]); close(fromtcs[1]);
14887dd7cddfSDavid du Colombier 		close(totcs[0]); close(totcs[1]);
14897dd7cddfSDavid du Colombier 		return 0;
14907dd7cddfSDavid du Colombier 	case 0:
14917dd7cddfSDavid du Colombier 		close(fromtcs[0]); close(totcs[1]);
14927dd7cddfSDavid du Colombier 		dup(fromtcs[1], 1);
14937dd7cddfSDavid du Colombier 		dup(totcs[0], 0);
14947dd7cddfSDavid du Colombier 		close(fromtcs[1]); close(totcs[0]);
14957dd7cddfSDavid du Colombier 		exec("/bin/tcs", av);
14967dd7cddfSDavid du Colombier 		_exits(0);
14977dd7cddfSDavid du Colombier 	default:
14987dd7cddfSDavid du Colombier 		close(fromtcs[1]); close(totcs[0]);
14997dd7cddfSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
15007dd7cddfSDavid du Colombier 		case -1:
15017dd7cddfSDavid du Colombier 			close(fromtcs[0]); close(totcs[1]);
15027dd7cddfSDavid du Colombier 			return 0;
15037dd7cddfSDavid du Colombier 		case 0:
15047dd7cddfSDavid du Colombier 			close(fromtcs[0]);
15057dd7cddfSDavid du Colombier 			while(in < e){
15067dd7cddfSDavid du Colombier 				n = write(totcs[1], in, e-in);
15077dd7cddfSDavid du Colombier 				if(n <= 0)
15087dd7cddfSDavid du Colombier 					break;
15097dd7cddfSDavid du Colombier 				in += n;
15107dd7cddfSDavid du Colombier 			}
15117dd7cddfSDavid du Colombier 			close(totcs[1]);
15127dd7cddfSDavid du Colombier 			_exits(0);
15137dd7cddfSDavid du Colombier 		default:
15147dd7cddfSDavid du Colombier 			close(totcs[1]);
15157dd7cddfSDavid du Colombier 			for(;;){
15167dd7cddfSDavid du Colombier 				n = read(fromtcs[0], &p[sofar], len-sofar);
15177dd7cddfSDavid du Colombier 				if(n <= 0)
15187dd7cddfSDavid du Colombier 					break;
15197dd7cddfSDavid du Colombier 				sofar += n;
15207dd7cddfSDavid du Colombier 				p[sofar] = 0;
15217dd7cddfSDavid du Colombier 				if(sofar == len){
15227dd7cddfSDavid du Colombier 					len += 1024;
15237dd7cddfSDavid du Colombier 					*out = p = realloc(p, len+1);
15247dd7cddfSDavid du Colombier 					if(p == nil)
15257dd7cddfSDavid du Colombier 						return 0;
15267dd7cddfSDavid du Colombier 				}
15277dd7cddfSDavid du Colombier 			}
15287dd7cddfSDavid du Colombier 			close(fromtcs[0]);
15297dd7cddfSDavid du Colombier 			break;
15307dd7cddfSDavid du Colombier 		}
15317dd7cddfSDavid du Colombier 		break;
15327dd7cddfSDavid du Colombier 	}
15337dd7cddfSDavid du Colombier 	return sofar;
15347dd7cddfSDavid du Colombier }
15357dd7cddfSDavid du Colombier 
15367dd7cddfSDavid du Colombier enum {
15377dd7cddfSDavid du Colombier 	Winstart= 0x7f,
15387dd7cddfSDavid du Colombier 	Winend= 0x9f,
15397dd7cddfSDavid du Colombier };
15407dd7cddfSDavid du Colombier 
15417dd7cddfSDavid du Colombier Rune winchars[] = {
15427dd7cddfSDavid du Colombier 	L'•',
15437dd7cddfSDavid du Colombier 	L'•', L'•', L'‚', L'ƒ', L'„', L'…', L'†', L'‡',
15447dd7cddfSDavid du Colombier 	L'ˆ', L'‰', L'Š', L'‹', L'Œ', L'•', L'•', L'•',
15457dd7cddfSDavid du Colombier 	L'•', L'‘', L'’', L'“', L'”', L'•', L'–', L'—',
15467dd7cddfSDavid du Colombier 	L'˜', L'™', L'š', L'›', L'œ', L'•', L'•', L'Ÿ',
15477dd7cddfSDavid du Colombier };
15487dd7cddfSDavid du Colombier 
15497dd7cddfSDavid du Colombier int
15507dd7cddfSDavid du Colombier windows1257toutf(char *out, char *in, char *e)
15517dd7cddfSDavid du Colombier {
15527dd7cddfSDavid du Colombier 	Rune r;
15537dd7cddfSDavid du Colombier 	char *p;
15547dd7cddfSDavid du Colombier 
15557dd7cddfSDavid du Colombier 	p = out;
15567dd7cddfSDavid du Colombier 	for(; in < e; in++){
15577dd7cddfSDavid du Colombier 		r = (*in) & 0xff;
15587dd7cddfSDavid du Colombier 		if(r >= 0x7f && r <= 0x9f)
15597dd7cddfSDavid du Colombier 			r = winchars[r-0x7f];
15607dd7cddfSDavid du Colombier 		p += runetochar(p, &r);
15617dd7cddfSDavid du Colombier 	}
15627dd7cddfSDavid du Colombier 	*p = 0;
15637dd7cddfSDavid du Colombier 	return p - out;
15647dd7cddfSDavid du Colombier }
15657dd7cddfSDavid du Colombier 
15667dd7cddfSDavid du Colombier void *
15677dd7cddfSDavid du Colombier emalloc(ulong n)
15687dd7cddfSDavid du Colombier {
15697dd7cddfSDavid du Colombier 	void *p;
15707dd7cddfSDavid du Colombier 
15717dd7cddfSDavid du Colombier 	p = mallocz(n, 1);
15727dd7cddfSDavid du Colombier 	if(!p){
15737dd7cddfSDavid du Colombier 		fprint(2, "%s: out of memory\n", argv0);
15747dd7cddfSDavid du Colombier 		exits("out of memory");
15757dd7cddfSDavid du Colombier 	}
15767dd7cddfSDavid du Colombier 	return p;
15777dd7cddfSDavid du Colombier }
15787dd7cddfSDavid du Colombier 
15797dd7cddfSDavid du Colombier void *
15807dd7cddfSDavid du Colombier erealloc(void *p, ulong n)
15817dd7cddfSDavid du Colombier {
15827dd7cddfSDavid du Colombier 	p = realloc(p, n);
15837dd7cddfSDavid du Colombier 	if(!p){
15847dd7cddfSDavid du Colombier 		fprint(2, "%s: out of memory\n", argv0);
15857dd7cddfSDavid du Colombier 		exits("out of memory");
15867dd7cddfSDavid du Colombier 	}
15877dd7cddfSDavid du Colombier 	return p;
15887dd7cddfSDavid du Colombier }
15897dd7cddfSDavid du Colombier 
15907dd7cddfSDavid du Colombier void
15917dd7cddfSDavid du Colombier mailplumb(Mailbox *mb, Message *m, int delete)
15927dd7cddfSDavid du Colombier {
15937dd7cddfSDavid du Colombier 	Plumbmsg p;
15947dd7cddfSDavid du Colombier 	Plumbattr a[7];
15957dd7cddfSDavid du Colombier 	char buf[256];
15967dd7cddfSDavid du Colombier 	int ai;
15977dd7cddfSDavid du Colombier 	char lenstr[10], *from, *subject, *date;
15987dd7cddfSDavid du Colombier 	static int fd = -1;
15997dd7cddfSDavid du Colombier 
16007dd7cddfSDavid du Colombier 	if(m->subject822 == nil)
16017dd7cddfSDavid du Colombier 		subject = "";
16027dd7cddfSDavid du Colombier 	else
16037dd7cddfSDavid du Colombier 		subject = s_to_c(m->subject822);
16047dd7cddfSDavid du Colombier 
16057dd7cddfSDavid du Colombier 	if(m->from822 != nil)
16067dd7cddfSDavid du Colombier 		from = s_to_c(m->from822);
16077dd7cddfSDavid du Colombier 	else if(m->unixfrom != nil)
16087dd7cddfSDavid du Colombier 		from = s_to_c(m->unixfrom);
16097dd7cddfSDavid du Colombier 	else
16107dd7cddfSDavid du Colombier 		from = "";
16117dd7cddfSDavid du Colombier 
16127dd7cddfSDavid du Colombier 	if(m->unixdate != nil)
16137dd7cddfSDavid du Colombier 		date = s_to_c(m->unixdate);
16147dd7cddfSDavid du Colombier 	else
16157dd7cddfSDavid du Colombier 		date = "";
16167dd7cddfSDavid du Colombier 
16177dd7cddfSDavid du Colombier 	sprint(lenstr, "%ld", m->end-m->start);
16187dd7cddfSDavid du Colombier 
16197dd7cddfSDavid du Colombier 	if(biffing && !delete)
16207dd7cddfSDavid du Colombier 		print("[ %s / %s / %s ]\n", from, subject, lenstr);
16217dd7cddfSDavid du Colombier 
16227dd7cddfSDavid du Colombier 	if(!plumbing)
16237dd7cddfSDavid du Colombier 		return;
16247dd7cddfSDavid du Colombier 
16257dd7cddfSDavid du Colombier 	if(fd < 0)
16267dd7cddfSDavid du Colombier 		fd = plumbopen("send", OWRITE);
16277dd7cddfSDavid du Colombier 	if(fd < 0)
16287dd7cddfSDavid du Colombier 		return;
16297dd7cddfSDavid du Colombier 
16307dd7cddfSDavid du Colombier 	p.src = "mailfs";
16317dd7cddfSDavid du Colombier 	p.dst = "seemail";
16327dd7cddfSDavid du Colombier 	p.wdir = "/mail/fs";
16337dd7cddfSDavid du Colombier 	p.type = "text";
16347dd7cddfSDavid du Colombier 
16357dd7cddfSDavid du Colombier 	ai = 0;
16367dd7cddfSDavid du Colombier 	a[ai].name = "filetype";
16377dd7cddfSDavid du Colombier 	a[ai].value = "mail";
16387dd7cddfSDavid du Colombier 
16397dd7cddfSDavid du Colombier 	a[++ai].name = "sender";
16407dd7cddfSDavid du Colombier 	a[ai].value = from;
16417dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
16427dd7cddfSDavid du Colombier 
16437dd7cddfSDavid du Colombier 	a[++ai].name = "length";
16447dd7cddfSDavid du Colombier 	a[ai].value = lenstr;
16457dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
16467dd7cddfSDavid du Colombier 
16477dd7cddfSDavid du Colombier 	a[++ai].name = "mailtype";
16487dd7cddfSDavid du Colombier 	a[ai].value = delete?"delete":"new";
16497dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
16507dd7cddfSDavid du Colombier 
16517dd7cddfSDavid du Colombier 	a[++ai].name = "date";
16527dd7cddfSDavid du Colombier 	a[ai].value = date;
16537dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
16547dd7cddfSDavid du Colombier 
16557dd7cddfSDavid du Colombier 	a[++ai].name = "digest";
16567dd7cddfSDavid du Colombier 	a[ai].value = s_to_c(m->sdigest);
16577dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
16587dd7cddfSDavid du Colombier 
16597dd7cddfSDavid du Colombier 	a[ai].next = nil;
16607dd7cddfSDavid du Colombier 
16617dd7cddfSDavid du Colombier 	p.attr = a;
16627dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/%s/%s",
16637dd7cddfSDavid du Colombier 		mntpt, mb->name, m->name);
16647dd7cddfSDavid du Colombier 	p.ndata = strlen(buf);
16657dd7cddfSDavid du Colombier 	p.data = buf;
16667dd7cddfSDavid du Colombier 
16677dd7cddfSDavid du Colombier 	plumbsend(fd, &p);
16687dd7cddfSDavid du Colombier }
16697dd7cddfSDavid du Colombier 
16707dd7cddfSDavid du Colombier //
16717dd7cddfSDavid du Colombier // count the number of lines in the body (for imap4)
16727dd7cddfSDavid du Colombier //
16737dd7cddfSDavid du Colombier void
16747dd7cddfSDavid du Colombier countlines(Message *m)
16757dd7cddfSDavid du Colombier {
16767dd7cddfSDavid du Colombier 	int i;
16777dd7cddfSDavid du Colombier 	char *p;
16787dd7cddfSDavid du Colombier 
16797dd7cddfSDavid du Colombier 	i = 0;
16807dd7cddfSDavid du Colombier 	for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
16817dd7cddfSDavid du Colombier 		i++;
16827dd7cddfSDavid du Colombier 	sprint(m->lines, "%d", i);
16837dd7cddfSDavid du Colombier }
1684