xref: /plan9/sys/src/cmd/upas/fs/mbox.c (revision 816336a7b57a41698d44534811a1b6f45b6cf776)
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*);
303ff48bf5SDavid du Colombier static	void	nullsqueeze(Message*);
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	int	headerline(char**, String*);
677dd7cddfSDavid du Colombier static	void	initheaders(void);
687dd7cddfSDavid du Colombier static void	parseattachments(Message*, Mailbox*);
697dd7cddfSDavid du Colombier 
707dd7cddfSDavid du Colombier int		debug;
7180ee5cbfSDavid du Colombier 
7280ee5cbfSDavid du Colombier char *Enotme = "path not served by this file server";
737dd7cddfSDavid du Colombier 
747dd7cddfSDavid du Colombier enum
757dd7cddfSDavid du Colombier {
767dd7cddfSDavid du Colombier 	Chunksize = 1024,
777dd7cddfSDavid du Colombier };
787dd7cddfSDavid du Colombier 
7980ee5cbfSDavid du Colombier Mailboxinit *boxinit[] = {
8080ee5cbfSDavid du Colombier 	imap4mbox,
8180ee5cbfSDavid du Colombier 	pop3mbox,
8280ee5cbfSDavid du Colombier 	plan9mbox,
8380ee5cbfSDavid du Colombier };
8480ee5cbfSDavid du Colombier 
8580ee5cbfSDavid du Colombier char*
8680ee5cbfSDavid du Colombier syncmbox(Mailbox *mb, int doplumb)
8780ee5cbfSDavid du Colombier {
8880ee5cbfSDavid du Colombier 	return (*mb->sync)(mb, doplumb);
8980ee5cbfSDavid du Colombier }
9080ee5cbfSDavid du Colombier 
917dd7cddfSDavid du Colombier /* create a new mailbox */
927dd7cddfSDavid du Colombier char*
937dd7cddfSDavid du Colombier newmbox(char *path, char *name, int std)
947dd7cddfSDavid du Colombier {
957dd7cddfSDavid du Colombier 	Mailbox *mb, **l;
967dd7cddfSDavid du Colombier 	char *p, *rv;
9780ee5cbfSDavid du Colombier 	int i;
9880ee5cbfSDavid du Colombier 
9980ee5cbfSDavid du Colombier 	initheaders();
10080ee5cbfSDavid du Colombier 
1017dd7cddfSDavid du Colombier 	mb = emalloc(sizeof(*mb));
1027dd7cddfSDavid du Colombier 	strncpy(mb->path, path, sizeof(mb->path)-1);
1037dd7cddfSDavid du Colombier 	if(name == nil){
1047dd7cddfSDavid du Colombier 		p = strrchr(path, '/');
1057dd7cddfSDavid du Colombier 		if(p == nil)
1067dd7cddfSDavid du Colombier 			p = path;
1077dd7cddfSDavid du Colombier 		else
1087dd7cddfSDavid du Colombier 			p++;
1097dd7cddfSDavid du Colombier 		if(*p == 0){
1107dd7cddfSDavid du Colombier 			free(mb);
1117dd7cddfSDavid du Colombier 			return "bad mbox name";
1127dd7cddfSDavid du Colombier 		}
1137dd7cddfSDavid du Colombier 		strncpy(mb->name, p, sizeof(mb->name)-1);
1147dd7cddfSDavid du Colombier 	} else {
1157dd7cddfSDavid du Colombier 		strncpy(mb->name, name, sizeof(mb->name)-1);
1167dd7cddfSDavid du Colombier 	}
1177dd7cddfSDavid du Colombier 
11880ee5cbfSDavid du Colombier 	rv = nil;
11980ee5cbfSDavid du Colombier 	// check for a mailbox type
12080ee5cbfSDavid du Colombier 	for(i=0; i<nelem(boxinit); i++)
12180ee5cbfSDavid du Colombier 		if((rv = (*boxinit[i])(mb, path)) != Enotme)
12280ee5cbfSDavid du Colombier 			break;
12380ee5cbfSDavid du Colombier 	if(i == nelem(boxinit)){
12480ee5cbfSDavid du Colombier 		free(mb);
12580ee5cbfSDavid du Colombier 		return "bad path";
12680ee5cbfSDavid du Colombier 	}
12780ee5cbfSDavid du Colombier 
12880ee5cbfSDavid du Colombier 	// on error, give up
1295d459b5aSDavid du Colombier 	if(rv){
1305d459b5aSDavid du Colombier 		free(mb);
13180ee5cbfSDavid du Colombier 		return rv;
1325d459b5aSDavid du Colombier 	}
13380ee5cbfSDavid du Colombier 
1347dd7cddfSDavid du Colombier 	// make sure name isn't taken
1357dd7cddfSDavid du Colombier 	qlock(&mbllock);
13659cc4ca5SDavid du Colombier 	for(l = &mbl; *l != nil; l = &(*l)->next){
1377dd7cddfSDavid du Colombier 		if(strcmp((*l)->name, mb->name) == 0){
13859cc4ca5SDavid du Colombier 			if(strcmp(path, (*l)->path) == 0)
1397dd7cddfSDavid du Colombier 				rv = nil;
1407dd7cddfSDavid du Colombier 			else
1417dd7cddfSDavid du Colombier 				rv = "mbox name in use";
14280ee5cbfSDavid du Colombier 			if(mb->close)
14380ee5cbfSDavid du Colombier 				(*mb->close)(mb);
1447dd7cddfSDavid du Colombier 			free(mb);
1457dd7cddfSDavid du Colombier 			qunlock(&mbllock);
1467dd7cddfSDavid du Colombier 			return rv;
1477dd7cddfSDavid du Colombier 		}
14859cc4ca5SDavid du Colombier 	}
14959cc4ca5SDavid du Colombier 
150ed250ae1SDavid du Colombier 	// always try locking
151ed250ae1SDavid du Colombier 	mb->dolock = 1;
15259cc4ca5SDavid du Colombier 
1537dd7cddfSDavid du Colombier 	mb->refs = 1;
1547dd7cddfSDavid du Colombier 	mb->next = nil;
1557dd7cddfSDavid du Colombier 	mb->id = newid();
1567dd7cddfSDavid du Colombier 	mb->root = newmessage(nil);
1577dd7cddfSDavid du Colombier 	mb->std = std;
1587dd7cddfSDavid du Colombier 	*l = mb;
1597dd7cddfSDavid du Colombier 	qunlock(&mbllock);
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	qlock(mb);
16280ee5cbfSDavid du Colombier 	if(mb->ctl){
1639a747e4fSDavid du Colombier 		henter(PATH(mb->id, Qmbox), "ctl",
1649a747e4fSDavid du Colombier 			(Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb);
16580ee5cbfSDavid du Colombier 	}
1667dd7cddfSDavid du Colombier 	rv = syncmbox(mb, 0);
1677dd7cddfSDavid du Colombier 	qunlock(mb);
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier 	return rv;
1707dd7cddfSDavid du Colombier }
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier // close the named mailbox
1737dd7cddfSDavid du Colombier void
1747dd7cddfSDavid du Colombier freembox(char *name)
1757dd7cddfSDavid du Colombier {
1765d459b5aSDavid du Colombier 	Mailbox **l, *mb;
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier 	qlock(&mbllock);
1795d459b5aSDavid du Colombier 	for(l=&mbl; *l != nil; l=&(*l)->next){
1805d459b5aSDavid du Colombier 		if(strcmp(name, (*l)->name) == 0){
1815d459b5aSDavid du Colombier 			mb = *l;
1825d459b5aSDavid du Colombier 			*l = mb->next;
1837dd7cddfSDavid du Colombier 			mboxdecref(mb);
1847dd7cddfSDavid du Colombier 			break;
1857dd7cddfSDavid du Colombier 		}
1865d459b5aSDavid du Colombier 	}
1879a747e4fSDavid du Colombier 	hfree(PATH(0, Qtop), name);
1887dd7cddfSDavid du Colombier 	qunlock(&mbllock);
1897dd7cddfSDavid du Colombier }
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier static void
1927dd7cddfSDavid du Colombier initheaders(void)
1937dd7cddfSDavid du Colombier {
1947dd7cddfSDavid du Colombier 	Header *h;
1957dd7cddfSDavid du Colombier 	static int already;
1967dd7cddfSDavid du Colombier 
1977dd7cddfSDavid du Colombier 	if(already)
1987dd7cddfSDavid du Colombier 		return;
1997dd7cddfSDavid du Colombier 	already = 1;
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier 	for(h = head; h->type != nil; h++)
2027dd7cddfSDavid du Colombier 		h->len = strlen(h->type);
2037dd7cddfSDavid du Colombier }
2047dd7cddfSDavid du Colombier 
2057dd7cddfSDavid du Colombier /*
2067dd7cddfSDavid du Colombier  *  parse a Unix style header
2077dd7cddfSDavid du Colombier  */
20880ee5cbfSDavid du Colombier void
2097dd7cddfSDavid du Colombier parseunix(Message *m)
2107dd7cddfSDavid du Colombier {
2117dd7cddfSDavid du Colombier 	char *p;
2127dd7cddfSDavid du Colombier 	String *h;
2137dd7cddfSDavid du Colombier 
2147dd7cddfSDavid du Colombier 	h = s_new();
2157dd7cddfSDavid du Colombier 	for(p = m->start + 5; *p && *p != '\r' && *p != '\n'; p++)
2167dd7cddfSDavid du Colombier 		s_putc(h, *p);
2177dd7cddfSDavid du Colombier 	s_terminate(h);
2187dd7cddfSDavid du Colombier 	s_restart(h);
2197dd7cddfSDavid du Colombier 
2207dd7cddfSDavid du Colombier 	m->unixfrom = s_parse(h, s_reset(m->unixfrom));
2217dd7cddfSDavid du Colombier 	m->unixdate = s_append(s_reset(m->unixdate), h->ptr);
2227dd7cddfSDavid du Colombier 
2237dd7cddfSDavid du Colombier 	s_free(h);
2247dd7cddfSDavid du Colombier }
2257dd7cddfSDavid du Colombier 
2267dd7cddfSDavid du Colombier /*
2277dd7cddfSDavid du Colombier  *  parse a message
2287dd7cddfSDavid du Colombier  */
22980ee5cbfSDavid du Colombier void
2307a02f3c0SDavid du Colombier parseheaders(Message *m, int justmime, Mailbox *mb, int addfrom)
2317dd7cddfSDavid du Colombier {
2327dd7cddfSDavid du Colombier 	String *hl;
2337dd7cddfSDavid du Colombier 	Header *h;
23480ee5cbfSDavid du Colombier 	char *p, *q;
2357dd7cddfSDavid du Colombier 	int i;
2367dd7cddfSDavid du Colombier 
2377dd7cddfSDavid du Colombier 	if(m->whole == m->whole->whole){
2389a747e4fSDavid du Colombier 		henter(PATH(mb->id, Qmbox), m->name,
2399a747e4fSDavid du Colombier 			(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
2407dd7cddfSDavid du Colombier 	} else {
2419a747e4fSDavid du Colombier 		henter(PATH(m->whole->id, Qdir), m->name,
2429a747e4fSDavid du Colombier 			(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
2437dd7cddfSDavid du Colombier 	}
2447dd7cddfSDavid du Colombier 	for(i = 0; i < Qmax; i++)
2459a747e4fSDavid du Colombier 		henter(PATH(m->id, Qdir), dirtab[i],
2469a747e4fSDavid du Colombier 			(Qid){PATH(m->id, i), 0, QTFILE}, m, mb);
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier 	// parse mime headers
2497dd7cddfSDavid du Colombier 	p = m->header;
2507dd7cddfSDavid du Colombier 	hl = s_new();
2517dd7cddfSDavid du Colombier 	while(headerline(&p, hl)){
2527dd7cddfSDavid du Colombier 		if(justmime)
2537dd7cddfSDavid du Colombier 			h = &head[Mhead];
2547dd7cddfSDavid du Colombier 		else
2557dd7cddfSDavid du Colombier 			h = head;
2567dd7cddfSDavid du Colombier 		for(; h->type; h++){
2577dd7cddfSDavid du Colombier 			if(cistrncmp(s_to_c(hl), h->type, h->len) == 0){
2587dd7cddfSDavid du Colombier 				(*h->f)(m, h, s_to_c(hl));
2597dd7cddfSDavid du Colombier 				break;
2607dd7cddfSDavid du Colombier 			}
2617dd7cddfSDavid du Colombier 		}
2627dd7cddfSDavid du Colombier 		s_reset(hl);
2637dd7cddfSDavid du Colombier 	}
2647dd7cddfSDavid du Colombier 	s_free(hl);
2657dd7cddfSDavid du Colombier 
2667dd7cddfSDavid du Colombier 	// the blank line isn't really part of the body or header
2677dd7cddfSDavid du Colombier 	if(justmime){
2687dd7cddfSDavid du Colombier 		m->mhend = p;
2697dd7cddfSDavid du Colombier 		m->hend = m->header;
2707dd7cddfSDavid du Colombier 	} else {
2717dd7cddfSDavid du Colombier 		m->hend = p;
2727dd7cddfSDavid du Colombier 	}
2737dd7cddfSDavid du Colombier 	if(*p == '\n')
2747dd7cddfSDavid du Colombier 		p++;
2757dd7cddfSDavid du Colombier 	m->rbody = m->body = p;
2767dd7cddfSDavid du Colombier 
2773ff48bf5SDavid du Colombier 	// if type is text, get any nulls out of the body.  This is
2783ff48bf5SDavid du Colombier 	// for the two seans and imap clients that get confused.
2793ff48bf5SDavid du Colombier 	if(strncmp(s_to_c(m->type), "text/", 5) == 0)
2803ff48bf5SDavid du Colombier 		nullsqueeze(m);
2813ff48bf5SDavid du Colombier 
28280ee5cbfSDavid du Colombier 	//
28380ee5cbfSDavid du Colombier 	// cobble together Unix-style from line
28480ee5cbfSDavid du Colombier 	// for local mailbox messages, we end up recreating the
28580ee5cbfSDavid du Colombier 	// original header.
28680ee5cbfSDavid du Colombier 	// for pop3 messages, the best we can do is
28780ee5cbfSDavid du Colombier 	// use the From: information and the RFC822 date.
28880ee5cbfSDavid du Colombier 	//
28967031067SDavid du Colombier 	if(m->unixdate == nil || strcmp(s_to_c(m->unixdate), "???") == 0
29067031067SDavid du Colombier 	|| strcmp(s_to_c(m->unixdate), "Thu Jan 1 00:00:00 GMT 1970") == 0){
29167031067SDavid du Colombier 		if(m->unixdate){
29267031067SDavid du Colombier 			s_free(m->unixdate);
29367031067SDavid du Colombier 			m->unixdate = nil;
29467031067SDavid du Colombier 		}
29580ee5cbfSDavid du Colombier 		// look for the date in the first Received: line.
29680ee5cbfSDavid du Colombier 		// it's likely to be the right time zone (it's
29780ee5cbfSDavid du Colombier 	 	// the local system) and in a convenient format.
29880ee5cbfSDavid du Colombier 		if(cistrncmp(m->header, "received:", 9)==0){
299d9306527SDavid du Colombier 			if((q = strchr(m->header, ';')) != nil){
300d9306527SDavid du Colombier 				p = q;
301d9306527SDavid du Colombier 				while((p = strchr(p, '\n')) != nil){
302d9306527SDavid du Colombier 					if(p[1] != ' ' && p[1] != '\t' && p[1] != '\n')
303d9306527SDavid du Colombier 						break;
304d9306527SDavid du Colombier 					p++;
305d9306527SDavid du Colombier 				}
306d9306527SDavid du Colombier 				if(p){
30780ee5cbfSDavid du Colombier 					*p = '\0';
30880ee5cbfSDavid du Colombier 					m->unixdate = date822tounix(q+1);
30980ee5cbfSDavid du Colombier 					*p = '\n';
31080ee5cbfSDavid du Colombier 				}
31180ee5cbfSDavid du Colombier 			}
312d9306527SDavid du Colombier 		}
31380ee5cbfSDavid du Colombier 
31480ee5cbfSDavid du Colombier 		// fall back on the rfc822 date
31580ee5cbfSDavid du Colombier 		if(m->unixdate==nil && m->date822)
31680ee5cbfSDavid du Colombier 			m->unixdate = date822tounix(s_to_c(m->date822));
31780ee5cbfSDavid du Colombier 	}
31880ee5cbfSDavid du Colombier 
3199a747e4fSDavid du Colombier 	if(m->unixheader != nil)
3209a747e4fSDavid du Colombier 		s_free(m->unixheader);
3217a02f3c0SDavid du Colombier 
3227a02f3c0SDavid du Colombier 	// only fake header for top-level messages for pop3 and imap4
3237a02f3c0SDavid du Colombier 	// clients (those protocols don't include the unix header).
3247a02f3c0SDavid du Colombier 	// adding the unix header all the time screws up mime-attached
3257a02f3c0SDavid du Colombier 	// rfc822 messages.
3267a02f3c0SDavid du Colombier 	if(!addfrom && !m->unixfrom){
3277a02f3c0SDavid du Colombier 		m->unixheader = nil;
3287a02f3c0SDavid du Colombier 		return;
3297a02f3c0SDavid du Colombier 	}
3307a02f3c0SDavid du Colombier 
33180ee5cbfSDavid du Colombier 	m->unixheader = s_copy("From ");
33267031067SDavid du Colombier 	if(m->unixfrom && strcmp(s_to_c(m->unixfrom), "???") != 0)
33380ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->unixfrom));
33480ee5cbfSDavid du Colombier 	else if(m->from822)
33580ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->from822));
33680ee5cbfSDavid du Colombier 	else
33780ee5cbfSDavid du Colombier 		s_append(m->unixheader, "???");
33880ee5cbfSDavid du Colombier 
33980ee5cbfSDavid du Colombier 	s_append(m->unixheader, " ");
34080ee5cbfSDavid du Colombier 	if(m->unixdate)
34180ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->unixdate));
34280ee5cbfSDavid du Colombier 	else
34367031067SDavid du Colombier 		s_append(m->unixheader, "Thu Jan  1 00:00:00 GMT 1970");
34480ee5cbfSDavid du Colombier 
34580ee5cbfSDavid du Colombier 	s_append(m->unixheader, "\n");
34680ee5cbfSDavid du Colombier }
34780ee5cbfSDavid du Colombier 
3489a747e4fSDavid du Colombier String*
3499a747e4fSDavid du Colombier promote(String **sp)
3509a747e4fSDavid du Colombier {
3519a747e4fSDavid du Colombier 	String *s;
3529a747e4fSDavid du Colombier 
3539a747e4fSDavid du Colombier 	if(*sp != nil)
3549a747e4fSDavid du Colombier 		s = s_clone(*sp);
3559a747e4fSDavid du Colombier 	else
3569a747e4fSDavid du Colombier 		s = nil;
3579a747e4fSDavid du Colombier 	return s;
3589a747e4fSDavid du Colombier }
3599a747e4fSDavid du Colombier 
36080ee5cbfSDavid du Colombier void
36180ee5cbfSDavid du Colombier parsebody(Message *m, Mailbox *mb)
36280ee5cbfSDavid du Colombier {
3639a747e4fSDavid du Colombier 	Message *nm;
3649a747e4fSDavid du Colombier 
3657dd7cddfSDavid du Colombier 	// recurse
3667dd7cddfSDavid du Colombier 	if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){
3677dd7cddfSDavid du Colombier 		parseattachments(m, mb);
3687dd7cddfSDavid du Colombier 	} else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
3697dd7cddfSDavid du Colombier 		decode(m);
3707dd7cddfSDavid du Colombier 		parseattachments(m, mb);
3719a747e4fSDavid du Colombier 		nm = m->part;
3729a747e4fSDavid du Colombier 
3739a747e4fSDavid du Colombier 		// promote headers
3745d459b5aSDavid du Colombier 		if(m->replyto822 == nil && m->from822 == nil && m->sender822 == nil){
3759a747e4fSDavid du Colombier 			m->from822 = promote(&nm->from822);
3769a747e4fSDavid du Colombier 			m->to822 = promote(&nm->to822);
3779a747e4fSDavid du Colombier 			m->date822 = promote(&nm->date822);
3789a747e4fSDavid du Colombier 			m->sender822 = promote(&nm->sender822);
3799a747e4fSDavid du Colombier 			m->replyto822 = promote(&nm->replyto822);
3809a747e4fSDavid du Colombier 			m->subject822 = promote(&nm->subject822);
3819a747e4fSDavid du Colombier 			m->unixdate = promote(&nm->unixdate);
3827dd7cddfSDavid du Colombier 		}
3837dd7cddfSDavid du Colombier 	}
3845d459b5aSDavid du Colombier }
3857dd7cddfSDavid du Colombier 
38680ee5cbfSDavid du Colombier void
3877a02f3c0SDavid du Colombier parse(Message *m, int justmime, Mailbox *mb, int addfrom)
38880ee5cbfSDavid du Colombier {
3897a02f3c0SDavid du Colombier 	parseheaders(m, justmime, mb, addfrom);
39080ee5cbfSDavid du Colombier 	parsebody(m, mb);
39180ee5cbfSDavid du Colombier }
39280ee5cbfSDavid du Colombier 
3937dd7cddfSDavid du Colombier static void
3947dd7cddfSDavid du Colombier parseattachments(Message *m, Mailbox *mb)
3957dd7cddfSDavid du Colombier {
3967dd7cddfSDavid du Colombier 	Message *nm, **l;
3977dd7cddfSDavid du Colombier 	char *p, *x;
3987dd7cddfSDavid du Colombier 
3997dd7cddfSDavid du Colombier 	// if there's a boundary, recurse...
4007dd7cddfSDavid du Colombier 	if(m->boundary != nil){
4017dd7cddfSDavid du Colombier 		p = m->body;
4027dd7cddfSDavid du Colombier 		nm = nil;
4037dd7cddfSDavid du Colombier 		l = &m->part;
4047dd7cddfSDavid du Colombier 		for(;;){
4057dd7cddfSDavid du Colombier 			x = strstr(p, s_to_c(m->boundary));
4063ff48bf5SDavid du Colombier 
4073ff48bf5SDavid du Colombier 			/* no boundary, we're done */
4083ff48bf5SDavid du Colombier 			if(x == nil){
4097dd7cddfSDavid du Colombier 				if(nm != nil)
4107dd7cddfSDavid du Colombier 					nm->rbend = nm->bend = nm->end = m->bend;
4117dd7cddfSDavid du Colombier 				break;
4127dd7cddfSDavid du Colombier 			}
4133ff48bf5SDavid du Colombier 
4143ff48bf5SDavid du Colombier 			/* boundary must be at the start of a line */
4153ff48bf5SDavid du Colombier 			if(x != m->body && *(x-1) != '\n'){
4163ff48bf5SDavid du Colombier 				p = x+1;
4173ff48bf5SDavid du Colombier 				continue;
4183ff48bf5SDavid du Colombier 			}
4193ff48bf5SDavid du Colombier 
4207dd7cddfSDavid du Colombier 			if(nm != nil)
4217dd7cddfSDavid du Colombier 				nm->rbend = nm->bend = nm->end = x;
4227dd7cddfSDavid du Colombier 			x += strlen(s_to_c(m->boundary));
4237dd7cddfSDavid du Colombier 
4247dd7cddfSDavid du Colombier 			/* is this the last part? ignore anything after it */
4257dd7cddfSDavid du Colombier 			if(strncmp(x, "--", 2) == 0)
4267dd7cddfSDavid du Colombier 				break;
4277dd7cddfSDavid du Colombier 
4287dd7cddfSDavid du Colombier 			p = strchr(x, '\n');
4297dd7cddfSDavid du Colombier 			if(p == nil)
4307dd7cddfSDavid du Colombier 				break;
4317dd7cddfSDavid du Colombier 			nm = newmessage(m);
4327dd7cddfSDavid du Colombier 			nm->start = nm->header = nm->body = nm->rbody = ++p;
4337dd7cddfSDavid du Colombier 			nm->mheader = nm->header;
4347dd7cddfSDavid du Colombier 			*l = nm;
4357dd7cddfSDavid du Colombier 			l = &nm->next;
4367dd7cddfSDavid du Colombier 		}
4377dd7cddfSDavid du Colombier 		for(nm = m->part; nm != nil; nm = nm->next)
4387a02f3c0SDavid du Colombier 			parse(nm, 1, mb, 0);
4399a747e4fSDavid du Colombier 		return;
4407dd7cddfSDavid du Colombier 	}
4419a747e4fSDavid du Colombier 
4429a747e4fSDavid du Colombier 	// if we've got an rfc822 message, recurse...
4439a747e4fSDavid du Colombier 	if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
4447dd7cddfSDavid du Colombier 		nm = newmessage(m);
4457dd7cddfSDavid du Colombier 		m->part = nm;
4467dd7cddfSDavid du Colombier 		nm->start = nm->header = nm->body = nm->rbody = m->body;
4477dd7cddfSDavid du Colombier 		nm->end = nm->bend = nm->rbend = m->bend;
4487a02f3c0SDavid du Colombier 		parse(nm, 0, mb, 0);
4497dd7cddfSDavid du Colombier 	}
4507dd7cddfSDavid du Colombier }
4517dd7cddfSDavid du Colombier 
4527dd7cddfSDavid du Colombier /*
4537dd7cddfSDavid du Colombier  *  pick up a header line
4547dd7cddfSDavid du Colombier  */
4557dd7cddfSDavid du Colombier static int
4567dd7cddfSDavid du Colombier headerline(char **pp, String *hl)
4577dd7cddfSDavid du Colombier {
4587dd7cddfSDavid du Colombier 	char *p, *x;
4597dd7cddfSDavid du Colombier 
4607dd7cddfSDavid du Colombier 	s_reset(hl);
4617dd7cddfSDavid du Colombier 	p = *pp;
4627dd7cddfSDavid du Colombier 	x = strpbrk(p, ":\n");
4637dd7cddfSDavid du Colombier 	if(x == nil || *x == '\n')
4647dd7cddfSDavid du Colombier 		return 0;
4657dd7cddfSDavid du Colombier 	for(;;){
4667dd7cddfSDavid du Colombier 		x = strchr(p, '\n');
4677dd7cddfSDavid du Colombier 		if(x == nil)
4687dd7cddfSDavid du Colombier 			x = p + strlen(p);
4697dd7cddfSDavid du Colombier 		s_nappend(hl, p, x-p);
4707dd7cddfSDavid du Colombier 		p = x;
471223a736eSDavid du Colombier 		if(*p != '\n' || *++p != ' ' && *p != '\t')
4727dd7cddfSDavid du Colombier 			break;
473223a736eSDavid du Colombier 		while(*p == ' ' || *p == '\t')
474223a736eSDavid du Colombier 			p++;
475223a736eSDavid du Colombier 		s_putc(hl, ' ');
4767dd7cddfSDavid du Colombier 	}
4777dd7cddfSDavid du Colombier 	*pp = p;
4787dd7cddfSDavid du Colombier 	return 1;
4797dd7cddfSDavid du Colombier }
4807dd7cddfSDavid du Colombier 
4817dd7cddfSDavid du Colombier static String*
4827dd7cddfSDavid du Colombier addr822(char *p)
4837dd7cddfSDavid du Colombier {
4847dd7cddfSDavid du Colombier 	String *s, *list;
4857dd7cddfSDavid du Colombier 	int incomment, addrdone, inanticomment, quoted;
4867dd7cddfSDavid du Colombier 	int n;
4877dd7cddfSDavid du Colombier 	int c;
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 	list = s_new();
4907dd7cddfSDavid du Colombier 	s = s_new();
4917dd7cddfSDavid du Colombier 	quoted = incomment = addrdone = inanticomment = 0;
4927dd7cddfSDavid du Colombier 	n = 0;
4937dd7cddfSDavid du Colombier 	for(; *p; p++){
4947dd7cddfSDavid du Colombier 		c = *p;
4957dd7cddfSDavid du Colombier 
4967dd7cddfSDavid du Colombier 		// whitespace is ignored
4977dd7cddfSDavid du Colombier 		if(!quoted && isspace(c) || c == '\r')
4987dd7cddfSDavid du Colombier 			continue;
4997dd7cddfSDavid du Colombier 
5007dd7cddfSDavid du Colombier 		// strings are always treated as atoms
5017dd7cddfSDavid du Colombier 		if(!quoted && c == '"'){
5027dd7cddfSDavid du Colombier 			if(!addrdone && !incomment)
5037dd7cddfSDavid du Colombier 				s_putc(s, c);
5047dd7cddfSDavid du Colombier 			for(p++; *p; p++){
5057dd7cddfSDavid du Colombier 				if(!addrdone && !incomment)
5067dd7cddfSDavid du Colombier 					s_putc(s, *p);
5077dd7cddfSDavid du Colombier 				if(!quoted && *p == '"')
5087dd7cddfSDavid du Colombier 					break;
5097dd7cddfSDavid du Colombier 				if(*p == '\\')
5107dd7cddfSDavid du Colombier 					quoted = 1;
5117dd7cddfSDavid du Colombier 				else
5127dd7cddfSDavid du Colombier 					quoted = 0;
5137dd7cddfSDavid du Colombier 			}
5147dd7cddfSDavid du Colombier 			if(*p == 0)
5157dd7cddfSDavid du Colombier 				break;
5167dd7cddfSDavid du Colombier 			quoted = 0;
5177dd7cddfSDavid du Colombier 			continue;
5187dd7cddfSDavid du Colombier 		}
5197dd7cddfSDavid du Colombier 
5207dd7cddfSDavid du Colombier 		// ignore everything in an expicit comment
5217dd7cddfSDavid du Colombier 		if(!quoted && c == '('){
5227dd7cddfSDavid du Colombier 			incomment = 1;
5237dd7cddfSDavid du Colombier 			continue;
5247dd7cddfSDavid du Colombier 		}
5257dd7cddfSDavid du Colombier 		if(incomment){
5267dd7cddfSDavid du Colombier 			if(!quoted && c == ')')
5277dd7cddfSDavid du Colombier 				incomment = 0;
5287dd7cddfSDavid du Colombier 			quoted = 0;
5297dd7cddfSDavid du Colombier 			continue;
5307dd7cddfSDavid du Colombier 		}
5317dd7cddfSDavid du Colombier 
5327dd7cddfSDavid du Colombier 		// anticomments makes everything outside of them comments
5337dd7cddfSDavid du Colombier 		if(!quoted && c == '<' && !inanticomment){
5347dd7cddfSDavid du Colombier 			inanticomment = 1;
5357dd7cddfSDavid du Colombier 			s = s_reset(s);
5367dd7cddfSDavid du Colombier 			continue;
5377dd7cddfSDavid du Colombier 		}
5387dd7cddfSDavid du Colombier 		if(!quoted && c == '>' && inanticomment){
5397dd7cddfSDavid du Colombier 			addrdone = 1;
5407dd7cddfSDavid du Colombier 			inanticomment = 0;
5417dd7cddfSDavid du Colombier 			continue;
5427dd7cddfSDavid du Colombier 		}
5437dd7cddfSDavid du Colombier 
5447dd7cddfSDavid du Colombier 		// commas separate addresses
5457dd7cddfSDavid du Colombier 		if(!quoted && c == ',' && !inanticomment){
5467dd7cddfSDavid du Colombier 			s_terminate(s);
5477dd7cddfSDavid du Colombier 			addrdone = 0;
5487dd7cddfSDavid du Colombier 			if(n++ != 0)
5497dd7cddfSDavid du Colombier 				s_append(list, " ");
5507dd7cddfSDavid du Colombier 			s_append(list, s_to_c(s));
5517dd7cddfSDavid du Colombier 			s = s_reset(s);
5527dd7cddfSDavid du Colombier 			continue;
5537dd7cddfSDavid du Colombier 		}
5547dd7cddfSDavid du Colombier 
5557dd7cddfSDavid du Colombier 		// what's left is part of the address
5567dd7cddfSDavid du Colombier 		s_putc(s, c);
5577dd7cddfSDavid du Colombier 
5587dd7cddfSDavid du Colombier 		// quoted characters are recognized only as characters
5597dd7cddfSDavid du Colombier 		if(c == '\\')
5607dd7cddfSDavid du Colombier 			quoted = 1;
5617dd7cddfSDavid du Colombier 		else
5627dd7cddfSDavid du Colombier 			quoted = 0;
5637dd7cddfSDavid du Colombier 
5647dd7cddfSDavid du Colombier 	}
5657dd7cddfSDavid du Colombier 
5667dd7cddfSDavid du Colombier 	if(*s_to_c(s) != 0){
5677dd7cddfSDavid du Colombier 		s_terminate(s);
5687dd7cddfSDavid du Colombier 		if(n++ != 0)
5697dd7cddfSDavid du Colombier 			s_append(list, " ");
5707dd7cddfSDavid du Colombier 		s_append(list, s_to_c(s));
5717dd7cddfSDavid du Colombier 	}
5727dd7cddfSDavid du Colombier 	s_free(s);
5737dd7cddfSDavid du Colombier 
5747dd7cddfSDavid du Colombier 	if(n == 0){
5757dd7cddfSDavid du Colombier 		s_free(list);
5767dd7cddfSDavid du Colombier 		return nil;
5777dd7cddfSDavid du Colombier 	}
5787dd7cddfSDavid du Colombier 	return list;
5797dd7cddfSDavid du Colombier }
5807dd7cddfSDavid du Colombier 
5817dd7cddfSDavid du Colombier static void
5827dd7cddfSDavid du Colombier to822(Message *m, Header *h, char *p)
5837dd7cddfSDavid du Colombier {
5847dd7cddfSDavid du Colombier 	p += strlen(h->type);
5857dd7cddfSDavid du Colombier 	s_free(m->to822);
5867dd7cddfSDavid du Colombier 	m->to822 = addr822(p);
5877dd7cddfSDavid du Colombier }
5887dd7cddfSDavid du Colombier 
5897dd7cddfSDavid du Colombier static void
5907dd7cddfSDavid du Colombier cc822(Message *m, Header *h, char *p)
5917dd7cddfSDavid du Colombier {
5927dd7cddfSDavid du Colombier 	p += strlen(h->type);
5937dd7cddfSDavid du Colombier 	s_free(m->cc822);
5947dd7cddfSDavid du Colombier 	m->cc822 = addr822(p);
5957dd7cddfSDavid du Colombier }
5967dd7cddfSDavid du Colombier 
5977dd7cddfSDavid du Colombier static void
5987dd7cddfSDavid du Colombier bcc822(Message *m, Header *h, char *p)
5997dd7cddfSDavid du Colombier {
6007dd7cddfSDavid du Colombier 	p += strlen(h->type);
6017dd7cddfSDavid du Colombier 	s_free(m->bcc822);
6027dd7cddfSDavid du Colombier 	m->bcc822 = addr822(p);
6037dd7cddfSDavid du Colombier }
6047dd7cddfSDavid du Colombier 
6057dd7cddfSDavid du Colombier static void
6067dd7cddfSDavid du Colombier from822(Message *m, Header *h, char *p)
6077dd7cddfSDavid du Colombier {
6087dd7cddfSDavid du Colombier 	p += strlen(h->type);
6097dd7cddfSDavid du Colombier 	s_free(m->from822);
6107dd7cddfSDavid du Colombier 	m->from822 = addr822(p);
6117dd7cddfSDavid du Colombier }
6127dd7cddfSDavid du Colombier 
6137dd7cddfSDavid du Colombier static void
6147dd7cddfSDavid du Colombier sender822(Message *m, Header *h, char *p)
6157dd7cddfSDavid du Colombier {
6167dd7cddfSDavid du Colombier 	p += strlen(h->type);
6177dd7cddfSDavid du Colombier 	s_free(m->sender822);
6187dd7cddfSDavid du Colombier 	m->sender822 = addr822(p);
6197dd7cddfSDavid du Colombier }
6207dd7cddfSDavid du Colombier 
6217dd7cddfSDavid du Colombier static void
6227dd7cddfSDavid du Colombier replyto822(Message *m, Header *h, char *p)
6237dd7cddfSDavid du Colombier {
6247dd7cddfSDavid du Colombier 	p += strlen(h->type);
6257dd7cddfSDavid du Colombier 	s_free(m->replyto822);
6267dd7cddfSDavid du Colombier 	m->replyto822 = addr822(p);
6277dd7cddfSDavid du Colombier }
6287dd7cddfSDavid du Colombier 
6297dd7cddfSDavid du Colombier static void
6307dd7cddfSDavid du Colombier mimeversion(Message *m, Header *h, char *p)
6317dd7cddfSDavid du Colombier {
6327dd7cddfSDavid du Colombier 	p += strlen(h->type);
6337dd7cddfSDavid du Colombier 	s_free(m->mimeversion);
6347dd7cddfSDavid du Colombier 	m->mimeversion = addr822(p);
6357dd7cddfSDavid du Colombier }
6367dd7cddfSDavid du Colombier 
6377dd7cddfSDavid du Colombier static void
6387dd7cddfSDavid du Colombier killtrailingwhite(char *p)
6397dd7cddfSDavid du Colombier {
6407dd7cddfSDavid du Colombier 	char *e;
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier 	e = p + strlen(p) - 1;
6437dd7cddfSDavid du Colombier 	while(e > p && isspace(*e))
6447dd7cddfSDavid du Colombier 		*e-- = 0;
6457dd7cddfSDavid du Colombier }
6467dd7cddfSDavid du Colombier 
6477dd7cddfSDavid du Colombier static void
6487dd7cddfSDavid du Colombier date822(Message *m, Header *h, char *p)
6497dd7cddfSDavid du Colombier {
6507dd7cddfSDavid du Colombier 	p += strlen(h->type);
6517dd7cddfSDavid du Colombier 	p = skipwhite(p);
6527dd7cddfSDavid du Colombier 	s_free(m->date822);
6537dd7cddfSDavid du Colombier 	m->date822 = s_copy(p);
6547dd7cddfSDavid du Colombier 	p = s_to_c(m->date822);
6557dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6567dd7cddfSDavid du Colombier }
6577dd7cddfSDavid du Colombier 
6587dd7cddfSDavid du Colombier static void
6597dd7cddfSDavid du Colombier subject822(Message *m, Header *h, char *p)
6607dd7cddfSDavid du Colombier {
6617dd7cddfSDavid du Colombier 	p += strlen(h->type);
6627dd7cddfSDavid du Colombier 	p = skipwhite(p);
6637dd7cddfSDavid du Colombier 	s_free(m->subject822);
6647dd7cddfSDavid du Colombier 	m->subject822 = s_copy(p);
6657dd7cddfSDavid du Colombier 	p = s_to_c(m->subject822);
6667dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6677dd7cddfSDavid du Colombier }
6687dd7cddfSDavid du Colombier 
6697dd7cddfSDavid du Colombier static void
6707dd7cddfSDavid du Colombier inreplyto822(Message *m, Header *h, char *p)
6717dd7cddfSDavid du Colombier {
6727dd7cddfSDavid du Colombier 	p += strlen(h->type);
6737dd7cddfSDavid du Colombier 	p = skipwhite(p);
6747dd7cddfSDavid du Colombier 	s_free(m->inreplyto822);
6757dd7cddfSDavid du Colombier 	m->inreplyto822 = s_copy(p);
6767dd7cddfSDavid du Colombier 	p = s_to_c(m->inreplyto822);
6777dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6787dd7cddfSDavid du Colombier }
6797dd7cddfSDavid du Colombier 
6807dd7cddfSDavid du Colombier static void
6817dd7cddfSDavid du Colombier messageid822(Message *m, Header *h, char *p)
6827dd7cddfSDavid du Colombier {
6837dd7cddfSDavid du Colombier 	p += strlen(h->type);
6847dd7cddfSDavid du Colombier 	p = skipwhite(p);
6857dd7cddfSDavid du Colombier 	s_free(m->messageid822);
6867dd7cddfSDavid du Colombier 	m->messageid822 = s_copy(p);
6877dd7cddfSDavid du Colombier 	p = s_to_c(m->messageid822);
6887dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6897dd7cddfSDavid du Colombier }
6907dd7cddfSDavid du Colombier 
6917dd7cddfSDavid du Colombier static int
6927dd7cddfSDavid du Colombier isattribute(char **pp, char *attr)
6937dd7cddfSDavid du Colombier {
6947dd7cddfSDavid du Colombier 	char *p;
6957dd7cddfSDavid du Colombier 	int n;
6967dd7cddfSDavid du Colombier 
6977dd7cddfSDavid du Colombier 	n = strlen(attr);
6987dd7cddfSDavid du Colombier 	p = *pp;
6997dd7cddfSDavid du Colombier 	if(cistrncmp(p, attr, n) != 0)
7007dd7cddfSDavid du Colombier 		return 0;
7017dd7cddfSDavid du Colombier 	p += n;
7027dd7cddfSDavid du Colombier 	while(*p == ' ')
7037dd7cddfSDavid du Colombier 		p++;
7047dd7cddfSDavid du Colombier 	if(*p++ != '=')
7057dd7cddfSDavid du Colombier 		return 0;
7067dd7cddfSDavid du Colombier 	while(*p == ' ')
7077dd7cddfSDavid du Colombier 		p++;
7087dd7cddfSDavid du Colombier 	*pp = p;
7097dd7cddfSDavid du Colombier 	return 1;
7107dd7cddfSDavid du Colombier }
7117dd7cddfSDavid du Colombier 
7127dd7cddfSDavid du Colombier static void
7137dd7cddfSDavid du Colombier ctype(Message *m, Header *h, char *p)
7147dd7cddfSDavid du Colombier {
7157dd7cddfSDavid du Colombier 	String *s;
7167dd7cddfSDavid du Colombier 
7177dd7cddfSDavid du Colombier 	p += h->len;
7187dd7cddfSDavid du Colombier 	p = skipwhite(p);
7197dd7cddfSDavid du Colombier 
7207dd7cddfSDavid du Colombier 	p = getstring(p, m->type, 1);
7217dd7cddfSDavid du Colombier 
7227dd7cddfSDavid du Colombier 	while(*p){
7237dd7cddfSDavid du Colombier 		if(isattribute(&p, "boundary")){
7247dd7cddfSDavid du Colombier 			s = s_new();
7257dd7cddfSDavid du Colombier 			p = getstring(p, s, 0);
7267dd7cddfSDavid du Colombier 			m->boundary = s_reset(m->boundary);
7277dd7cddfSDavid du Colombier 			s_append(m->boundary, "--");
7287dd7cddfSDavid du Colombier 			s_append(m->boundary, s_to_c(s));
7297dd7cddfSDavid du Colombier 			s_free(s);
7307dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "multipart", 9) == 0){
7317dd7cddfSDavid du Colombier 			/*
7327dd7cddfSDavid du Colombier 			 *  the first unbounded part of a multipart message,
7337dd7cddfSDavid du Colombier 			 *  the preamble, is not displayed or saved
7347dd7cddfSDavid du Colombier 			 */
7357dd7cddfSDavid du Colombier 		} else if(isattribute(&p, "name")){
7367dd7cddfSDavid du Colombier 			if(m->filename == nil)
7377dd7cddfSDavid du Colombier 				setfilename(m, p);
7387dd7cddfSDavid du Colombier 		} else if(isattribute(&p, "charset")){
7397dd7cddfSDavid du Colombier 			p = getstring(p, s_reset(m->charset), 0);
7407dd7cddfSDavid du Colombier 		}
7417dd7cddfSDavid du Colombier 
7427dd7cddfSDavid du Colombier 		p = skiptosemi(p);
7437dd7cddfSDavid du Colombier 	}
7447dd7cddfSDavid du Colombier }
7457dd7cddfSDavid du Colombier 
7467dd7cddfSDavid du Colombier static void
7477dd7cddfSDavid du Colombier cencoding(Message *m, Header *h, char *p)
7487dd7cddfSDavid du Colombier {
7497dd7cddfSDavid du Colombier 	p += h->len;
7507dd7cddfSDavid du Colombier 	p = skipwhite(p);
7517dd7cddfSDavid du Colombier 	if(cistrncmp(p, "base64", 6) == 0)
7527dd7cddfSDavid du Colombier 		m->encoding = Ebase64;
7537dd7cddfSDavid du Colombier 	else if(cistrncmp(p, "quoted-printable", 16) == 0)
7547dd7cddfSDavid du Colombier 		m->encoding = Equoted;
7557dd7cddfSDavid du Colombier }
7567dd7cddfSDavid du Colombier 
7577dd7cddfSDavid du Colombier static void
7587dd7cddfSDavid du Colombier cdisposition(Message *m, Header *h, char *p)
7597dd7cddfSDavid du Colombier {
7607dd7cddfSDavid du Colombier 	p += h->len;
7617dd7cddfSDavid du Colombier 	p = skipwhite(p);
7627dd7cddfSDavid du Colombier 	while(*p){
7637dd7cddfSDavid du Colombier 		if(cistrncmp(p, "inline", 6) == 0){
7647dd7cddfSDavid du Colombier 			m->disposition = Dinline;
7657dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "attachment", 10) == 0){
7667dd7cddfSDavid du Colombier 			m->disposition = Dfile;
7677dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "filename=", 9) == 0){
7687dd7cddfSDavid du Colombier 			p += 9;
7697dd7cddfSDavid du Colombier 			setfilename(m, p);
7707dd7cddfSDavid du Colombier 		}
7717dd7cddfSDavid du Colombier 		p = skiptosemi(p);
7727dd7cddfSDavid du Colombier 	}
7737dd7cddfSDavid du Colombier 
7747dd7cddfSDavid du Colombier }
7757dd7cddfSDavid du Colombier 
7767dd7cddfSDavid du Colombier ulong msgallocd, msgfreed;
7777dd7cddfSDavid du Colombier 
7787dd7cddfSDavid du Colombier Message*
7797dd7cddfSDavid du Colombier newmessage(Message *parent)
7807dd7cddfSDavid du Colombier {
7817dd7cddfSDavid du Colombier 	static int id;
7827dd7cddfSDavid du Colombier 	Message *m;
7837dd7cddfSDavid du Colombier 
7847dd7cddfSDavid du Colombier 	msgallocd++;
7857dd7cddfSDavid du Colombier 
7867dd7cddfSDavid du Colombier 	m = emalloc(sizeof(*m));
7877dd7cddfSDavid du Colombier 	memset(m, 0, sizeof(*m));
7887dd7cddfSDavid du Colombier 	m->disposition = Dnone;
7897dd7cddfSDavid du Colombier 	m->type = s_copy("text/plain");
7907dd7cddfSDavid du Colombier 	m->charset = s_copy("iso-8859-1");
7917dd7cddfSDavid du Colombier 	m->id = newid();
7927dd7cddfSDavid du Colombier 	if(parent)
7937dd7cddfSDavid du Colombier 		sprint(m->name, "%d", ++(parent->subname));
7947dd7cddfSDavid du Colombier 	if(parent == nil)
7957dd7cddfSDavid du Colombier 		parent = m;
7967dd7cddfSDavid du Colombier 	m->whole = parent;
7977dd7cddfSDavid du Colombier 	m->hlen = -1;
7987dd7cddfSDavid du Colombier 	return m;
7997dd7cddfSDavid du Colombier }
8007dd7cddfSDavid du Colombier 
8017dd7cddfSDavid du Colombier // delete a message from a mailbox
8027dd7cddfSDavid du Colombier void
8037dd7cddfSDavid du Colombier delmessage(Mailbox *mb, Message *m)
8047dd7cddfSDavid du Colombier {
8057dd7cddfSDavid du Colombier 	Message **l;
8067dd7cddfSDavid du Colombier 	int i;
8077dd7cddfSDavid du Colombier 
8087dd7cddfSDavid du Colombier 	mb->vers++;
8097dd7cddfSDavid du Colombier 	msgfreed++;
8107dd7cddfSDavid du Colombier 
8117dd7cddfSDavid du Colombier 	if(m->whole != m){
8127dd7cddfSDavid du Colombier 		// unchain from parent
8137dd7cddfSDavid du Colombier 		for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
8147dd7cddfSDavid du Colombier 			;
8157dd7cddfSDavid du Colombier 		if(*l != nil)
8167dd7cddfSDavid du Colombier 			*l = m->next;
8177dd7cddfSDavid du Colombier 
8187dd7cddfSDavid du Colombier 		// clear out of name lookup hash table
8197dd7cddfSDavid du Colombier 		if(m->whole->whole == m->whole)
8209a747e4fSDavid du Colombier 			hfree(PATH(mb->id, Qmbox), m->name);
8217dd7cddfSDavid du Colombier 		else
8229a747e4fSDavid du Colombier 			hfree(PATH(m->whole->id, Qdir), m->name);
8237dd7cddfSDavid du Colombier 		for(i = 0; i < Qmax; i++)
8249a747e4fSDavid du Colombier 			hfree(PATH(m->id, Qdir), dirtab[i]);
8257dd7cddfSDavid du Colombier 	}
8267dd7cddfSDavid du Colombier 
8277dd7cddfSDavid du Colombier 	/* recurse through sub-parts */
8287dd7cddfSDavid du Colombier 	while(m->part)
8297dd7cddfSDavid du Colombier 		delmessage(mb, m->part);
8307dd7cddfSDavid du Colombier 
8317dd7cddfSDavid du Colombier 	/* free memory */
8327dd7cddfSDavid du Colombier 	if(m->mallocd)
8337dd7cddfSDavid du Colombier 		free(m->start);
8347dd7cddfSDavid du Colombier 	if(m->hallocd)
8357dd7cddfSDavid du Colombier 		free(m->header);
8367dd7cddfSDavid du Colombier 	if(m->ballocd)
8377dd7cddfSDavid du Colombier 		free(m->body);
8387dd7cddfSDavid du Colombier 	s_free(m->unixfrom);
8397dd7cddfSDavid du Colombier 	s_free(m->unixdate);
8409a747e4fSDavid du Colombier 	s_free(m->unixheader);
8417dd7cddfSDavid du Colombier 	s_free(m->from822);
8427dd7cddfSDavid du Colombier 	s_free(m->sender822);
8437dd7cddfSDavid du Colombier 	s_free(m->to822);
8447dd7cddfSDavid du Colombier 	s_free(m->bcc822);
84559cc4ca5SDavid du Colombier 	s_free(m->cc822);
8467dd7cddfSDavid du Colombier 	s_free(m->replyto822);
8477dd7cddfSDavid du Colombier 	s_free(m->date822);
8487dd7cddfSDavid du Colombier 	s_free(m->inreplyto822);
84959cc4ca5SDavid du Colombier 	s_free(m->subject822);
8507dd7cddfSDavid du Colombier 	s_free(m->messageid822);
8517dd7cddfSDavid du Colombier 	s_free(m->addrs);
8527dd7cddfSDavid du Colombier 	s_free(m->mimeversion);
85359cc4ca5SDavid du Colombier 	s_free(m->sdigest);
8547dd7cddfSDavid du Colombier 	s_free(m->boundary);
8557dd7cddfSDavid du Colombier 	s_free(m->type);
8567dd7cddfSDavid du Colombier 	s_free(m->charset);
8577dd7cddfSDavid du Colombier 	s_free(m->filename);
8587dd7cddfSDavid du Colombier 
8597dd7cddfSDavid du Colombier 	free(m);
8607dd7cddfSDavid du Colombier }
8617dd7cddfSDavid du Colombier 
8627dd7cddfSDavid du Colombier // mark messages (identified by path) for deletion
8637dd7cddfSDavid du Colombier void
8647dd7cddfSDavid du Colombier delmessages(int ac, char **av)
8657dd7cddfSDavid du Colombier {
8667dd7cddfSDavid du Colombier 	Mailbox *mb;
8677dd7cddfSDavid du Colombier 	Message *m;
8687dd7cddfSDavid du Colombier 	int i, needwrite;
8697dd7cddfSDavid du Colombier 
8707dd7cddfSDavid du Colombier 	qlock(&mbllock);
8717dd7cddfSDavid du Colombier 	for(mb = mbl; mb != nil; mb = mb->next)
8727dd7cddfSDavid du Colombier 		if(strcmp(av[0], mb->name) == 0){
8737dd7cddfSDavid du Colombier 			qlock(mb);
8747dd7cddfSDavid du Colombier 			break;
8757dd7cddfSDavid du Colombier 		}
8767dd7cddfSDavid du Colombier 	qunlock(&mbllock);
8777dd7cddfSDavid du Colombier 	if(mb == nil)
8787dd7cddfSDavid du Colombier 		return;
8797dd7cddfSDavid du Colombier 
8807dd7cddfSDavid du Colombier 	needwrite = 0;
8817dd7cddfSDavid du Colombier 	for(i = 1; i < ac; i++){
8827dd7cddfSDavid du Colombier 		for(m = mb->root->part; m != nil; m = m->next)
8837dd7cddfSDavid du Colombier 			if(strcmp(m->name, av[i]) == 0){
8847dd7cddfSDavid du Colombier 				if(!m->deleted){
8857dd7cddfSDavid du Colombier 					mailplumb(mb, m, 1);
8867dd7cddfSDavid du Colombier 					needwrite = 1;
8877dd7cddfSDavid du Colombier 					m->deleted = 1;
88859cc4ca5SDavid du Colombier 					logmsg("deleting", m);
8897dd7cddfSDavid du Colombier 				}
8907dd7cddfSDavid du Colombier 				break;
8917dd7cddfSDavid du Colombier 			}
8927dd7cddfSDavid du Colombier 	}
8937dd7cddfSDavid du Colombier 	if(needwrite)
8947dd7cddfSDavid du Colombier 		syncmbox(mb, 1);
8957dd7cddfSDavid du Colombier 	qunlock(mb);
8967dd7cddfSDavid du Colombier }
8977dd7cddfSDavid du Colombier 
8987dd7cddfSDavid du Colombier /*
8997dd7cddfSDavid du Colombier  *  the following are called with the mailbox qlocked
9007dd7cddfSDavid du Colombier  */
9017dd7cddfSDavid du Colombier void
9027dd7cddfSDavid du Colombier msgincref(Message *m)
9037dd7cddfSDavid du Colombier {
9047dd7cddfSDavid du Colombier 	m->refs++;
9057dd7cddfSDavid du Colombier }
9067dd7cddfSDavid du Colombier void
9077dd7cddfSDavid du Colombier msgdecref(Mailbox *mb, Message *m)
9087dd7cddfSDavid du Colombier {
9097dd7cddfSDavid du Colombier 	m->refs--;
9107dd7cddfSDavid du Colombier 	if(m->refs == 0 && m->deleted)
9117dd7cddfSDavid du Colombier 		syncmbox(mb, 1);
9127dd7cddfSDavid du Colombier }
9137dd7cddfSDavid du Colombier 
9147dd7cddfSDavid du Colombier /*
9157dd7cddfSDavid du Colombier  *  the following are called with mbllock'd
9167dd7cddfSDavid du Colombier  */
9177dd7cddfSDavid du Colombier void
9187dd7cddfSDavid du Colombier mboxincref(Mailbox *mb)
9197dd7cddfSDavid du Colombier {
9209a747e4fSDavid du Colombier 	assert(mb->refs > 0);
9217dd7cddfSDavid du Colombier 	mb->refs++;
9227dd7cddfSDavid du Colombier }
9237dd7cddfSDavid du Colombier void
9247dd7cddfSDavid du Colombier mboxdecref(Mailbox *mb)
9257dd7cddfSDavid du Colombier {
9269a747e4fSDavid du Colombier 	assert(mb->refs > 0);
9277dd7cddfSDavid du Colombier 	qlock(mb);
9287dd7cddfSDavid du Colombier 	mb->refs--;
9297dd7cddfSDavid du Colombier 	if(mb->refs == 0){
9307dd7cddfSDavid du Colombier 		delmessage(mb, mb->root);
93180ee5cbfSDavid du Colombier 		if(mb->ctl)
9329a747e4fSDavid du Colombier 			hfree(PATH(mb->id, Qmbox), "ctl");
93380ee5cbfSDavid du Colombier 		if(mb->close)
93480ee5cbfSDavid du Colombier 			(*mb->close)(mb);
9357dd7cddfSDavid du Colombier 		free(mb);
9367dd7cddfSDavid du Colombier 	} else
9377dd7cddfSDavid du Colombier 		qunlock(mb);
9387dd7cddfSDavid du Colombier }
9397dd7cddfSDavid du Colombier 
9407dd7cddfSDavid du Colombier int
9417dd7cddfSDavid du Colombier cistrncmp(char *a, char *b, int n)
9427dd7cddfSDavid du Colombier {
9437dd7cddfSDavid du Colombier 	while(n-- > 0){
9447dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
9457dd7cddfSDavid du Colombier 			return -1;
9467dd7cddfSDavid du Colombier 	}
9477dd7cddfSDavid du Colombier 	return 0;
9487dd7cddfSDavid du Colombier }
9497dd7cddfSDavid du Colombier 
9507dd7cddfSDavid du Colombier int
9517dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
9527dd7cddfSDavid du Colombier {
9537dd7cddfSDavid du Colombier 	for(;;){
9547dd7cddfSDavid du Colombier 		if(tolower(*a) != tolower(*b++))
9557dd7cddfSDavid du Colombier 			return -1;
9567dd7cddfSDavid du Colombier 		if(*a++ == 0)
9577dd7cddfSDavid du Colombier 			break;
9587dd7cddfSDavid du Colombier 	}
9597dd7cddfSDavid du Colombier 	return 0;
9607dd7cddfSDavid du Colombier }
9617dd7cddfSDavid du Colombier 
9627dd7cddfSDavid du Colombier static char*
9637dd7cddfSDavid du Colombier skipwhite(char *p)
9647dd7cddfSDavid du Colombier {
9657dd7cddfSDavid du Colombier 	while(isspace(*p))
9667dd7cddfSDavid du Colombier 		p++;
9677dd7cddfSDavid du Colombier 	return p;
9687dd7cddfSDavid du Colombier }
9697dd7cddfSDavid du Colombier 
9707dd7cddfSDavid du Colombier static char*
9717dd7cddfSDavid du Colombier skiptosemi(char *p)
9727dd7cddfSDavid du Colombier {
9737dd7cddfSDavid du Colombier 	while(*p && *p != ';')
9747dd7cddfSDavid du Colombier 		p++;
9757dd7cddfSDavid du Colombier 	while(*p == ';' || isspace(*p))
9767dd7cddfSDavid du Colombier 		p++;
9777dd7cddfSDavid du Colombier 	return p;
9787dd7cddfSDavid du Colombier }
9797dd7cddfSDavid du Colombier 
9807dd7cddfSDavid du Colombier static char*
9817dd7cddfSDavid du Colombier getstring(char *p, String *s, int dolower)
9827dd7cddfSDavid du Colombier {
9837dd7cddfSDavid du Colombier 	s = s_reset(s);
9847dd7cddfSDavid du Colombier 	p = skipwhite(p);
9857dd7cddfSDavid du Colombier 	if(*p == '"'){
9867dd7cddfSDavid du Colombier 		p++;
9877dd7cddfSDavid du Colombier 		for(;*p && *p != '"'; p++)
9887dd7cddfSDavid du Colombier 			if(dolower)
9897dd7cddfSDavid du Colombier 				s_putc(s, tolower(*p));
9907dd7cddfSDavid du Colombier 			else
9917dd7cddfSDavid du Colombier 				s_putc(s, *p);
9927dd7cddfSDavid du Colombier 		if(*p == '"')
9937dd7cddfSDavid du Colombier 			p++;
9947dd7cddfSDavid du Colombier 		s_terminate(s);
9957dd7cddfSDavid du Colombier 
9967dd7cddfSDavid du Colombier 		return p;
9977dd7cddfSDavid du Colombier 	}
9987dd7cddfSDavid du Colombier 
9999a747e4fSDavid du Colombier 	for(; *p && !isspace(*p) && *p != ';'; p++)
10007dd7cddfSDavid du Colombier 		if(dolower)
10017dd7cddfSDavid du Colombier 			s_putc(s, tolower(*p));
10027dd7cddfSDavid du Colombier 		else
10037dd7cddfSDavid du Colombier 			s_putc(s, *p);
10047dd7cddfSDavid du Colombier 	s_terminate(s);
10057dd7cddfSDavid du Colombier 
10067dd7cddfSDavid du Colombier 	return p;
10077dd7cddfSDavid du Colombier }
10087dd7cddfSDavid du Colombier 
10097dd7cddfSDavid du Colombier static void
10107dd7cddfSDavid du Colombier setfilename(Message *m, char *p)
10117dd7cddfSDavid du Colombier {
10127dd7cddfSDavid du Colombier 	m->filename = s_reset(m->filename);
10137dd7cddfSDavid du Colombier 	getstring(p, m->filename, 0);
10147dd7cddfSDavid du Colombier 	for(p = s_to_c(m->filename); *p; p++)
10157dd7cddfSDavid du Colombier 		if(*p == ' ' || *p == '\t' || *p == ';')
10167dd7cddfSDavid du Colombier 			*p = '_';
10177dd7cddfSDavid du Colombier }
10187dd7cddfSDavid du Colombier 
10197dd7cddfSDavid du Colombier //
10207dd7cddfSDavid du Colombier // undecode message body
10217dd7cddfSDavid du Colombier //
10227dd7cddfSDavid du Colombier void
10237dd7cddfSDavid du Colombier decode(Message *m)
10247dd7cddfSDavid du Colombier {
10257dd7cddfSDavid du Colombier 	int i, len;
10267dd7cddfSDavid du Colombier 	char *x;
10277dd7cddfSDavid du Colombier 
10287dd7cddfSDavid du Colombier 	if(m->decoded)
10297dd7cddfSDavid du Colombier 		return;
10307dd7cddfSDavid du Colombier 	switch(m->encoding){
10317dd7cddfSDavid du Colombier 	case Ebase64:
10327dd7cddfSDavid du Colombier 		len = m->bend - m->body;
10337dd7cddfSDavid du Colombier 		i = (len*3)/4+1;	// room for max chars + null
10347dd7cddfSDavid du Colombier 		x = emalloc(i);
10357dd7cddfSDavid du Colombier 		len = dec64((uchar*)x, i, m->body, len);
10367dd7cddfSDavid du Colombier 		if(m->ballocd)
10377dd7cddfSDavid du Colombier 			free(m->body);
10387dd7cddfSDavid du Colombier 		m->body = x;
10397dd7cddfSDavid du Colombier 		m->bend = x + len;
10407dd7cddfSDavid du Colombier 		m->ballocd = 1;
10417dd7cddfSDavid du Colombier 		break;
10427dd7cddfSDavid du Colombier 	case Equoted:
10437dd7cddfSDavid du Colombier 		len = m->bend - m->body;
10447dd7cddfSDavid du Colombier 		x = emalloc(len+2);	// room for null and possible extra nl
104517dd33a2SDavid du Colombier 		len = decquoted(x, m->body, m->bend, 0);
10467dd7cddfSDavid du Colombier 		if(m->ballocd)
10477dd7cddfSDavid du Colombier 			free(m->body);
10487dd7cddfSDavid du Colombier 		m->body = x;
10497dd7cddfSDavid du Colombier 		m->bend = x + len;
10507dd7cddfSDavid du Colombier 		m->ballocd = 1;
10517dd7cddfSDavid du Colombier 		break;
10527dd7cddfSDavid du Colombier 	default:
10537dd7cddfSDavid du Colombier 		break;
10547dd7cddfSDavid du Colombier 	}
10557dd7cddfSDavid du Colombier 	m->decoded = 1;
10567dd7cddfSDavid du Colombier }
10577dd7cddfSDavid du Colombier 
10587dd7cddfSDavid du Colombier // convert latin1 to utf
10597dd7cddfSDavid du Colombier void
10607dd7cddfSDavid du Colombier convert(Message *m)
10617dd7cddfSDavid du Colombier {
10627dd7cddfSDavid du Colombier 	int len;
10637dd7cddfSDavid du Colombier 	char *x;
10647dd7cddfSDavid du Colombier 
10657dd7cddfSDavid du Colombier 	// don't convert if we're not a leaf, not text, or already converted
10667dd7cddfSDavid du Colombier 	if(m->converted)
10677dd7cddfSDavid du Colombier 		return;
10687dd7cddfSDavid du Colombier 	if(m->part != nil)
10697dd7cddfSDavid du Colombier 		return;
10707dd7cddfSDavid du Colombier 	if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
10717dd7cddfSDavid du Colombier 		return;
10727dd7cddfSDavid du Colombier 
1073*816336a7SDavid du Colombier 	len = xtoutf(s_to_c(m->charset), &x, m->body, m->bend);
10747dd7cddfSDavid du Colombier 	if(len > 0){
10757dd7cddfSDavid du Colombier 		if(m->ballocd)
10767dd7cddfSDavid du Colombier 			free(m->body);
10777dd7cddfSDavid du Colombier 		m->body = x;
10787dd7cddfSDavid du Colombier 		m->bend = x + len;
10797dd7cddfSDavid du Colombier 		m->ballocd = 1;
10807dd7cddfSDavid du Colombier 	}
10817dd7cddfSDavid du Colombier 	m->converted = 1;
10827dd7cddfSDavid du Colombier }
10837dd7cddfSDavid du Colombier 
10847dd7cddfSDavid du Colombier static int
10857dd7cddfSDavid du Colombier hex2int(int x)
10867dd7cddfSDavid du Colombier {
10877dd7cddfSDavid du Colombier 	if(x >= '0' && x <= '9')
10887dd7cddfSDavid du Colombier 		return x - '0';
10897dd7cddfSDavid du Colombier 	if(x >= 'A' && x <= 'F')
10907dd7cddfSDavid du Colombier 		return (x - 'A') + 10;
10917dd7cddfSDavid du Colombier 	if(x >= 'a' && x <= 'f')
10927dd7cddfSDavid du Colombier 		return (x - 'a') + 10;
10937dd7cddfSDavid du Colombier 	return 0;
10947dd7cddfSDavid du Colombier }
10957dd7cddfSDavid du Colombier 
1096*816336a7SDavid du Colombier // underscores are translated in 2047 headers (uscores=1)
1097*816336a7SDavid du Colombier // but not in the body (uscores=0)
10987dd7cddfSDavid du Colombier static char*
109917dd33a2SDavid du Colombier decquotedline(char *out, char *in, char *e, int uscores)
11007dd7cddfSDavid du Colombier {
11017dd7cddfSDavid du Colombier 	int c, soft;
11027dd7cddfSDavid du Colombier 
11037dd7cddfSDavid du Colombier 	/* dump trailing white space */
11047dd7cddfSDavid du Colombier 	while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
11057dd7cddfSDavid du Colombier 		e--;
11067dd7cddfSDavid du Colombier 
11077dd7cddfSDavid du Colombier 	/* trailing '=' means no newline */
11087dd7cddfSDavid du Colombier 	if(*e == '='){
11097dd7cddfSDavid du Colombier 		soft = 1;
11107dd7cddfSDavid du Colombier 		e--;
11117dd7cddfSDavid du Colombier 	} else
11127dd7cddfSDavid du Colombier 		soft = 0;
11137dd7cddfSDavid du Colombier 
11147dd7cddfSDavid du Colombier 	while(in <= e){
11157dd7cddfSDavid du Colombier 		c = (*in++) & 0xff;
1116*816336a7SDavid du Colombier 		switch(c){
1117*816336a7SDavid du Colombier 		case '_':
1118*816336a7SDavid du Colombier 			if(uscores){
1119*816336a7SDavid du Colombier 				*out++ = ' ';
1120*816336a7SDavid du Colombier 				break;
1121*816336a7SDavid du Colombier 			}
1122*816336a7SDavid du Colombier 		default:
11237dd7cddfSDavid du Colombier 			*out++ = c;
11247dd7cddfSDavid du Colombier 			break;
1125*816336a7SDavid du Colombier 		case '=':
11267dd7cddfSDavid du Colombier 			c = hex2int(*in++)<<4;
11277dd7cddfSDavid du Colombier 			c |= hex2int(*in++);
11287dd7cddfSDavid du Colombier 			*out++ = c;
11297dd7cddfSDavid du Colombier 			break;
11307dd7cddfSDavid du Colombier 		}
11317dd7cddfSDavid du Colombier 	}
11327dd7cddfSDavid du Colombier 	if(!soft)
11337dd7cddfSDavid du Colombier 		*out++ = '\n';
11347dd7cddfSDavid du Colombier 	*out = 0;
11357dd7cddfSDavid du Colombier 
11367dd7cddfSDavid du Colombier 	return out;
11377dd7cddfSDavid du Colombier }
11387dd7cddfSDavid du Colombier 
11397dd7cddfSDavid du Colombier int
114017dd33a2SDavid du Colombier decquoted(char *out, char *in, char *e, int uscores)
11417dd7cddfSDavid du Colombier {
11427dd7cddfSDavid du Colombier 	char *p, *nl;
11437dd7cddfSDavid du Colombier 
11447dd7cddfSDavid du Colombier 	p = out;
11457dd7cddfSDavid du Colombier 	while((nl = strchr(in, '\n')) != nil && nl < e){
114617dd33a2SDavid du Colombier 		p = decquotedline(p, in, nl, uscores);
11477dd7cddfSDavid du Colombier 		in = nl + 1;
11487dd7cddfSDavid du Colombier 	}
11497dd7cddfSDavid du Colombier 	if(in < e)
115017dd33a2SDavid du Colombier 		p = decquotedline(p, in, e-1, uscores);
11517dd7cddfSDavid du Colombier 
11527dd7cddfSDavid du Colombier 	// make sure we end with a new line
11537dd7cddfSDavid du Colombier 	if(*(p-1) != '\n'){
11547dd7cddfSDavid du Colombier 		*p++ = '\n';
11557dd7cddfSDavid du Colombier 		*p = 0;
11567dd7cddfSDavid du Colombier 	}
11577dd7cddfSDavid du Colombier 
11587dd7cddfSDavid du Colombier 	return p - out;
11597dd7cddfSDavid du Colombier }
11607dd7cddfSDavid du Colombier 
11617dd7cddfSDavid du Colombier static char*
11627dd7cddfSDavid du Colombier lowercase(char *p)
11637dd7cddfSDavid du Colombier {
11647dd7cddfSDavid du Colombier 	char *op;
11657dd7cddfSDavid du Colombier 	int c;
11667dd7cddfSDavid du Colombier 
11677dd7cddfSDavid du Colombier 	for(op = p; c = *p; p++)
11687dd7cddfSDavid du Colombier 		if(isupper(c))
11697dd7cddfSDavid du Colombier 			*p = tolower(c);
11707dd7cddfSDavid du Colombier 	return op;
11717dd7cddfSDavid du Colombier }
11727dd7cddfSDavid du Colombier 
11737dd7cddfSDavid du Colombier // translate latin1 directly since it fits neatly in utf
1174*816336a7SDavid du Colombier static int
1175*816336a7SDavid du Colombier latin1toutf(char **out, char *in, char *e)
11767dd7cddfSDavid du Colombier {
1177*816336a7SDavid du Colombier 	int n;
11787dd7cddfSDavid du Colombier 	char *p;
1179*816336a7SDavid du Colombier 	Rune r;
11807dd7cddfSDavid du Colombier 
1181*816336a7SDavid du Colombier 	n = 0;
1182*816336a7SDavid du Colombier 	for(p = in; p < e; p++)
1183*816336a7SDavid du Colombier 		if(*p & 0x80)
1184*816336a7SDavid du Colombier 			n++;
1185*816336a7SDavid du Colombier 	if(n == 0)
1186*816336a7SDavid du Colombier 		return 0;
1187*816336a7SDavid du Colombier 
1188*816336a7SDavid du Colombier 	n += e-in;
1189*816336a7SDavid du Colombier 	*out = p = malloc(n+1);
1190*816336a7SDavid du Colombier 	if(p == nil)
1191*816336a7SDavid du Colombier 		return 0;
1192*816336a7SDavid du Colombier 
11937dd7cddfSDavid du Colombier 	for(; in < e; in++){
1194*816336a7SDavid du Colombier 		r = (uchar)*in;
11957dd7cddfSDavid du Colombier 		p += runetochar(p, &r);
11967dd7cddfSDavid du Colombier 	}
11977dd7cddfSDavid du Colombier 	*p = 0;
1198*816336a7SDavid du Colombier 	return p - *out;
11997dd7cddfSDavid du Colombier }
12007dd7cddfSDavid du Colombier 
1201*816336a7SDavid du Colombier // translate any thing using the tcs program
12027dd7cddfSDavid du Colombier int
12037dd7cddfSDavid du Colombier xtoutf(char *charset, char **out, char *in, char *e)
12047dd7cddfSDavid du Colombier {
12057dd7cddfSDavid du Colombier 	char *av[4];
12067dd7cddfSDavid du Colombier 	int totcs[2];
12077dd7cddfSDavid du Colombier 	int fromtcs[2];
12087dd7cddfSDavid du Colombier 	int n, len, sofar;
12097dd7cddfSDavid du Colombier 	char *p;
12107dd7cddfSDavid du Colombier 
1211*816336a7SDavid du Colombier 	// might not need to convert
1212*816336a7SDavid du Colombier 	if(cistrcmp(charset, "us-ascii") == 0 || cistrcmp(charset, "utf-8") == 0)
1213*816336a7SDavid du Colombier 		return 0;
1214*816336a7SDavid du Colombier 	if(cistrcmp(charset, "iso-8859-1") == 0)
1215*816336a7SDavid du Colombier 		return latin1toutf(out, in, e);
1216*816336a7SDavid du Colombier 
12177dd7cddfSDavid du Colombier 	len = e-in+1;
12187dd7cddfSDavid du Colombier 	sofar = 0;
12197dd7cddfSDavid du Colombier 	*out = p = malloc(len+1);
12207dd7cddfSDavid du Colombier 	if(p == nil)
12217dd7cddfSDavid du Colombier 		return 0;
12227dd7cddfSDavid du Colombier 
12237dd7cddfSDavid du Colombier 	av[0] = charset;
12247dd7cddfSDavid du Colombier 	av[1] = "-f";
12257dd7cddfSDavid du Colombier 	av[2] = charset;
12267dd7cddfSDavid du Colombier 	av[3] = 0;
12277dd7cddfSDavid du Colombier 	if(pipe(totcs) < 0)
1228*816336a7SDavid du Colombier 		goto error;
12297dd7cddfSDavid du Colombier 	if(pipe(fromtcs) < 0){
12307dd7cddfSDavid du Colombier 		close(totcs[0]); close(totcs[1]);
1231*816336a7SDavid du Colombier 		goto error;
12327dd7cddfSDavid du Colombier 	}
12337dd7cddfSDavid du Colombier 	switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
12347dd7cddfSDavid du Colombier 	case -1:
12357dd7cddfSDavid du Colombier 		close(fromtcs[0]); close(fromtcs[1]);
12367dd7cddfSDavid du Colombier 		close(totcs[0]); close(totcs[1]);
1237*816336a7SDavid du Colombier 		goto error;
12387dd7cddfSDavid du Colombier 	case 0:
12397dd7cddfSDavid du Colombier 		close(fromtcs[0]); close(totcs[1]);
12407dd7cddfSDavid du Colombier 		dup(fromtcs[1], 1);
12417dd7cddfSDavid du Colombier 		dup(totcs[0], 0);
12427dd7cddfSDavid du Colombier 		close(fromtcs[1]); close(totcs[0]);
12439a747e4fSDavid du Colombier 		dup(open("/dev/null", OWRITE), 2);
12447dd7cddfSDavid du Colombier 		exec("/bin/tcs", av);
12457dd7cddfSDavid du Colombier 		_exits(0);
12467dd7cddfSDavid du Colombier 	default:
12477dd7cddfSDavid du Colombier 		close(fromtcs[1]); close(totcs[0]);
12487dd7cddfSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
12497dd7cddfSDavid du Colombier 		case -1:
12507dd7cddfSDavid du Colombier 			close(fromtcs[0]); close(totcs[1]);
1251*816336a7SDavid du Colombier 			goto error;
12527dd7cddfSDavid du Colombier 		case 0:
12537dd7cddfSDavid du Colombier 			close(fromtcs[0]);
12547dd7cddfSDavid du Colombier 			while(in < e){
12557dd7cddfSDavid du Colombier 				n = write(totcs[1], in, e-in);
12567dd7cddfSDavid du Colombier 				if(n <= 0)
12577dd7cddfSDavid du Colombier 					break;
12587dd7cddfSDavid du Colombier 				in += n;
12597dd7cddfSDavid du Colombier 			}
12607dd7cddfSDavid du Colombier 			close(totcs[1]);
12617dd7cddfSDavid du Colombier 			_exits(0);
12627dd7cddfSDavid du Colombier 		default:
12637dd7cddfSDavid du Colombier 			close(totcs[1]);
12647dd7cddfSDavid du Colombier 			for(;;){
12657dd7cddfSDavid du Colombier 				n = read(fromtcs[0], &p[sofar], len-sofar);
12667dd7cddfSDavid du Colombier 				if(n <= 0)
12677dd7cddfSDavid du Colombier 					break;
12687dd7cddfSDavid du Colombier 				sofar += n;
12697dd7cddfSDavid du Colombier 				p[sofar] = 0;
12707dd7cddfSDavid du Colombier 				if(sofar == len){
12717dd7cddfSDavid du Colombier 					len += 1024;
1272*816336a7SDavid du Colombier 					p = realloc(p, len+1);
12737dd7cddfSDavid du Colombier 					if(p == nil)
1274*816336a7SDavid du Colombier 						goto error;
1275*816336a7SDavid du Colombier 					*out = p;
12767dd7cddfSDavid du Colombier 				}
12777dd7cddfSDavid du Colombier 			}
12787dd7cddfSDavid du Colombier 			close(fromtcs[0]);
12797dd7cddfSDavid du Colombier 			break;
12807dd7cddfSDavid du Colombier 		}
12817dd7cddfSDavid du Colombier 		break;
12827dd7cddfSDavid du Colombier 	}
1283*816336a7SDavid du Colombier 	if(sofar == 0)
1284*816336a7SDavid du Colombier 		goto error;
12857dd7cddfSDavid du Colombier 	return sofar;
12867dd7cddfSDavid du Colombier 
1287*816336a7SDavid du Colombier error:
1288*816336a7SDavid du Colombier 	free(*out);
1289*816336a7SDavid du Colombier 	*out = nil;
1290*816336a7SDavid du Colombier 	return 0;
12917dd7cddfSDavid du Colombier }
12927dd7cddfSDavid du Colombier 
12937dd7cddfSDavid du Colombier void *
12947dd7cddfSDavid du Colombier emalloc(ulong n)
12957dd7cddfSDavid du Colombier {
12967dd7cddfSDavid du Colombier 	void *p;
12977dd7cddfSDavid du Colombier 
12987dd7cddfSDavid du Colombier 	p = mallocz(n, 1);
12997dd7cddfSDavid du Colombier 	if(!p){
130080ee5cbfSDavid du Colombier 		fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
13017dd7cddfSDavid du Colombier 		exits("out of memory");
13027dd7cddfSDavid du Colombier 	}
130380ee5cbfSDavid du Colombier 	setmalloctag(p, getcallerpc(&n));
13047dd7cddfSDavid du Colombier 	return p;
13057dd7cddfSDavid du Colombier }
13067dd7cddfSDavid du Colombier 
13077dd7cddfSDavid du Colombier void *
13087dd7cddfSDavid du Colombier erealloc(void *p, ulong n)
13097dd7cddfSDavid du Colombier {
13103ff48bf5SDavid du Colombier 	if(n == 0)
13113ff48bf5SDavid du Colombier 		n = 1;
13127dd7cddfSDavid du Colombier 	p = realloc(p, n);
13137dd7cddfSDavid du Colombier 	if(!p){
131480ee5cbfSDavid du Colombier 		fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
13157dd7cddfSDavid du Colombier 		exits("out of memory");
13167dd7cddfSDavid du Colombier 	}
131780ee5cbfSDavid du Colombier 	setrealloctag(p, getcallerpc(&p));
13187dd7cddfSDavid du Colombier 	return p;
13197dd7cddfSDavid du Colombier }
13207dd7cddfSDavid du Colombier 
13217dd7cddfSDavid du Colombier void
13227dd7cddfSDavid du Colombier mailplumb(Mailbox *mb, Message *m, int delete)
13237dd7cddfSDavid du Colombier {
13247dd7cddfSDavid du Colombier 	Plumbmsg p;
13257dd7cddfSDavid du Colombier 	Plumbattr a[7];
13267dd7cddfSDavid du Colombier 	char buf[256];
13277dd7cddfSDavid du Colombier 	int ai;
13287dd7cddfSDavid du Colombier 	char lenstr[10], *from, *subject, *date;
13297dd7cddfSDavid du Colombier 	static int fd = -1;
13307dd7cddfSDavid du Colombier 
13317dd7cddfSDavid du Colombier 	if(m->subject822 == nil)
13327dd7cddfSDavid du Colombier 		subject = "";
13337dd7cddfSDavid du Colombier 	else
13347dd7cddfSDavid du Colombier 		subject = s_to_c(m->subject822);
13357dd7cddfSDavid du Colombier 
13367dd7cddfSDavid du Colombier 	if(m->from822 != nil)
13377dd7cddfSDavid du Colombier 		from = s_to_c(m->from822);
13387dd7cddfSDavid du Colombier 	else if(m->unixfrom != nil)
13397dd7cddfSDavid du Colombier 		from = s_to_c(m->unixfrom);
13407dd7cddfSDavid du Colombier 	else
13417dd7cddfSDavid du Colombier 		from = "";
13427dd7cddfSDavid du Colombier 
13437dd7cddfSDavid du Colombier 	if(m->unixdate != nil)
13447dd7cddfSDavid du Colombier 		date = s_to_c(m->unixdate);
13457dd7cddfSDavid du Colombier 	else
13467dd7cddfSDavid du Colombier 		date = "";
13477dd7cddfSDavid du Colombier 
13487dd7cddfSDavid du Colombier 	sprint(lenstr, "%ld", m->end-m->start);
13497dd7cddfSDavid du Colombier 
13507dd7cddfSDavid du Colombier 	if(biffing && !delete)
13517dd7cddfSDavid du Colombier 		print("[ %s / %s / %s ]\n", from, subject, lenstr);
13527dd7cddfSDavid du Colombier 
13537dd7cddfSDavid du Colombier 	if(!plumbing)
13547dd7cddfSDavid du Colombier 		return;
13557dd7cddfSDavid du Colombier 
13567dd7cddfSDavid du Colombier 	if(fd < 0)
13577dd7cddfSDavid du Colombier 		fd = plumbopen("send", OWRITE);
13587dd7cddfSDavid du Colombier 	if(fd < 0)
13597dd7cddfSDavid du Colombier 		return;
13607dd7cddfSDavid du Colombier 
13617dd7cddfSDavid du Colombier 	p.src = "mailfs";
13627dd7cddfSDavid du Colombier 	p.dst = "seemail";
13637dd7cddfSDavid du Colombier 	p.wdir = "/mail/fs";
13647dd7cddfSDavid du Colombier 	p.type = "text";
13657dd7cddfSDavid du Colombier 
13667dd7cddfSDavid du Colombier 	ai = 0;
13677dd7cddfSDavid du Colombier 	a[ai].name = "filetype";
13687dd7cddfSDavid du Colombier 	a[ai].value = "mail";
13697dd7cddfSDavid du Colombier 
13707dd7cddfSDavid du Colombier 	a[++ai].name = "sender";
13717dd7cddfSDavid du Colombier 	a[ai].value = from;
13727dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
13737dd7cddfSDavid du Colombier 
13747dd7cddfSDavid du Colombier 	a[++ai].name = "length";
13757dd7cddfSDavid du Colombier 	a[ai].value = lenstr;
13767dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
13777dd7cddfSDavid du Colombier 
13787dd7cddfSDavid du Colombier 	a[++ai].name = "mailtype";
13797dd7cddfSDavid du Colombier 	a[ai].value = delete?"delete":"new";
13807dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
13817dd7cddfSDavid du Colombier 
13827dd7cddfSDavid du Colombier 	a[++ai].name = "date";
13837dd7cddfSDavid du Colombier 	a[ai].value = date;
13847dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
13857dd7cddfSDavid du Colombier 
13869a747e4fSDavid du Colombier 	if(m->sdigest){
13877dd7cddfSDavid du Colombier 		a[++ai].name = "digest";
13887dd7cddfSDavid du Colombier 		a[ai].value = s_to_c(m->sdigest);
13897dd7cddfSDavid du Colombier 		a[ai-1].next = &a[ai];
13909a747e4fSDavid du Colombier 	}
13917dd7cddfSDavid du Colombier 
13927dd7cddfSDavid du Colombier 	a[ai].next = nil;
13937dd7cddfSDavid du Colombier 
13947dd7cddfSDavid du Colombier 	p.attr = a;
13957dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/%s/%s",
13967dd7cddfSDavid du Colombier 		mntpt, mb->name, m->name);
13977dd7cddfSDavid du Colombier 	p.ndata = strlen(buf);
13987dd7cddfSDavid du Colombier 	p.data = buf;
13997dd7cddfSDavid du Colombier 
14007dd7cddfSDavid du Colombier 	plumbsend(fd, &p);
14017dd7cddfSDavid du Colombier }
14027dd7cddfSDavid du Colombier 
14037dd7cddfSDavid du Colombier //
14047dd7cddfSDavid du Colombier // count the number of lines in the body (for imap4)
14057dd7cddfSDavid du Colombier //
14067dd7cddfSDavid du Colombier void
14077dd7cddfSDavid du Colombier countlines(Message *m)
14087dd7cddfSDavid du Colombier {
14097dd7cddfSDavid du Colombier 	int i;
14107dd7cddfSDavid du Colombier 	char *p;
14117dd7cddfSDavid du Colombier 
14127dd7cddfSDavid du Colombier 	i = 0;
14137dd7cddfSDavid du Colombier 	for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
14147dd7cddfSDavid du Colombier 		i++;
14157dd7cddfSDavid du Colombier 	sprint(m->lines, "%d", i);
14167dd7cddfSDavid du Colombier }
141759cc4ca5SDavid du Colombier 
141859cc4ca5SDavid du Colombier char *LOG = "fs";
141959cc4ca5SDavid du Colombier 
142059cc4ca5SDavid du Colombier void
142159cc4ca5SDavid du Colombier logmsg(char *s, Message *m)
142259cc4ca5SDavid du Colombier {
142359cc4ca5SDavid du Colombier 	int pid;
142459cc4ca5SDavid du Colombier 
142559cc4ca5SDavid du Colombier 	if(!logging)
142659cc4ca5SDavid du Colombier 		return;
142759cc4ca5SDavid du Colombier 	pid = getpid();
142859cc4ca5SDavid du Colombier 	if(m == nil)
142959cc4ca5SDavid du Colombier 		syslog(0, LOG, "%s.%d: %s", user, pid, s);
143059cc4ca5SDavid du Colombier 	else
143159cc4ca5SDavid du Colombier 		syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
143259cc4ca5SDavid du Colombier 			user, pid, s,
143359cc4ca5SDavid du Colombier 			m->from822 ? s_to_c(m->from822) : "?",
143459cc4ca5SDavid du Colombier 			s_to_c(m->sdigest));
143559cc4ca5SDavid du Colombier }
143680ee5cbfSDavid du Colombier 
14373ff48bf5SDavid du Colombier /*
14383ff48bf5SDavid du Colombier  *  squeeze nulls out of the body
14393ff48bf5SDavid du Colombier  */
14403ff48bf5SDavid du Colombier static void
14413ff48bf5SDavid du Colombier nullsqueeze(Message *m)
14423ff48bf5SDavid du Colombier {
14433ff48bf5SDavid du Colombier 	char *p, *q;
14443ff48bf5SDavid du Colombier 
14457a02f3c0SDavid du Colombier 	q = memchr(m->body, 0, m->end-m->body);
14463ff48bf5SDavid du Colombier 	if(q == nil)
14477a02f3c0SDavid du Colombier 		return;
14487a02f3c0SDavid du Colombier 
14497a02f3c0SDavid du Colombier 	for(p = m->body; q < m->end; q++){
14507a02f3c0SDavid du Colombier 		if(*q == 0)
14517a02f3c0SDavid du Colombier 			continue;
14527a02f3c0SDavid du Colombier 		*p++ = *q;
14533ff48bf5SDavid du Colombier 	}
14547a02f3c0SDavid du Colombier 	m->bend = m->rbend = m->end = p;
14553ff48bf5SDavid du Colombier }
14563ff48bf5SDavid du Colombier 
14573ff48bf5SDavid du Colombier 
145880ee5cbfSDavid du Colombier //
145980ee5cbfSDavid du Colombier // convert an RFC822 date into a Unix style date
146080ee5cbfSDavid du Colombier // for when the Unix From line isn't there (e.g. POP3).
146180ee5cbfSDavid du Colombier // enough client programs depend on having a Unix date
146280ee5cbfSDavid du Colombier // that it's easiest to write this conversion code once, right here.
146380ee5cbfSDavid du Colombier //
146480ee5cbfSDavid du Colombier // people don't follow RFC822 particularly closely,
146580ee5cbfSDavid du Colombier // so we use strtotm, which is a bunch of heuristics.
146680ee5cbfSDavid du Colombier //
146780ee5cbfSDavid du Colombier 
146880ee5cbfSDavid du Colombier extern int strtotm(char*, Tm*);
146980ee5cbfSDavid du Colombier String*
147080ee5cbfSDavid du Colombier date822tounix(char *s)
147180ee5cbfSDavid du Colombier {
147280ee5cbfSDavid du Colombier 	char *p, *q;
147380ee5cbfSDavid du Colombier 	Tm tm;
147480ee5cbfSDavid du Colombier 
147580ee5cbfSDavid du Colombier 	if(strtotm(s, &tm) < 0)
147680ee5cbfSDavid du Colombier 		return nil;
147780ee5cbfSDavid du Colombier 
147880ee5cbfSDavid du Colombier 	p = asctime(&tm);
147980ee5cbfSDavid du Colombier 	if(q = strchr(p, '\n'))
148080ee5cbfSDavid du Colombier 		*q = '\0';
148180ee5cbfSDavid du Colombier 	return s_copy(p);
148280ee5cbfSDavid du Colombier }
148380ee5cbfSDavid du Colombier 
1484