xref: /plan9/sys/src/cmd/upas/fs/mbox.c (revision 5d459b5a09e427ae1acd4e6afcf028853c73946e)
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	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;
719a747e4fSDavid du Colombier char		stdmbox[Pathlen];
7280ee5cbfSDavid du Colombier 
7380ee5cbfSDavid du Colombier char *Enotme = "path not served by this file server";
747dd7cddfSDavid du Colombier 
757dd7cddfSDavid du Colombier enum
767dd7cddfSDavid du Colombier {
777dd7cddfSDavid du Colombier 	Chunksize = 1024,
787dd7cddfSDavid du Colombier };
797dd7cddfSDavid du Colombier 
8080ee5cbfSDavid du Colombier Mailboxinit *boxinit[] = {
8180ee5cbfSDavid du Colombier 	imap4mbox,
8280ee5cbfSDavid du Colombier 	pop3mbox,
8380ee5cbfSDavid du Colombier 	plan9mbox,
8480ee5cbfSDavid du Colombier };
8580ee5cbfSDavid du Colombier 
8680ee5cbfSDavid du Colombier char*
8780ee5cbfSDavid du Colombier syncmbox(Mailbox *mb, int doplumb)
8880ee5cbfSDavid du Colombier {
8980ee5cbfSDavid du Colombier 	return (*mb->sync)(mb, doplumb);
9080ee5cbfSDavid du Colombier }
9180ee5cbfSDavid du Colombier 
927dd7cddfSDavid du Colombier /* create a new mailbox */
937dd7cddfSDavid du Colombier char*
947dd7cddfSDavid du Colombier newmbox(char *path, char *name, int std)
957dd7cddfSDavid du Colombier {
967dd7cddfSDavid du Colombier 	Mailbox *mb, **l;
977dd7cddfSDavid du Colombier 	char *p, *rv;
9880ee5cbfSDavid du Colombier 	int i;
9980ee5cbfSDavid du Colombier 
10080ee5cbfSDavid du Colombier 	initheaders();
10180ee5cbfSDavid du Colombier 
10280ee5cbfSDavid du Colombier 	if(stdmbox[0] == 0)
10380ee5cbfSDavid du Colombier 		snprint(stdmbox, sizeof(stdmbox), "/mail/box/%s/mbox", user);
1047dd7cddfSDavid du Colombier 
1057dd7cddfSDavid du Colombier 	mb = emalloc(sizeof(*mb));
1067dd7cddfSDavid du Colombier 	strncpy(mb->path, path, sizeof(mb->path)-1);
1077dd7cddfSDavid du Colombier 	if(name == nil){
1087dd7cddfSDavid du Colombier 		p = strrchr(path, '/');
1097dd7cddfSDavid du Colombier 		if(p == nil)
1107dd7cddfSDavid du Colombier 			p = path;
1117dd7cddfSDavid du Colombier 		else
1127dd7cddfSDavid du Colombier 			p++;
1137dd7cddfSDavid du Colombier 		if(*p == 0){
1147dd7cddfSDavid du Colombier 			free(mb);
1157dd7cddfSDavid du Colombier 			return "bad mbox name";
1167dd7cddfSDavid du Colombier 		}
1177dd7cddfSDavid du Colombier 		strncpy(mb->name, p, sizeof(mb->name)-1);
1187dd7cddfSDavid du Colombier 	} else {
1197dd7cddfSDavid du Colombier 		strncpy(mb->name, name, sizeof(mb->name)-1);
1207dd7cddfSDavid du Colombier 	}
1217dd7cddfSDavid du Colombier 
12280ee5cbfSDavid du Colombier 	rv = nil;
12380ee5cbfSDavid du Colombier 	// check for a mailbox type
12480ee5cbfSDavid du Colombier 	for(i=0; i<nelem(boxinit); i++)
12580ee5cbfSDavid du Colombier 		if((rv = (*boxinit[i])(mb, path)) != Enotme)
12680ee5cbfSDavid du Colombier 			break;
12780ee5cbfSDavid du Colombier 	if(i == nelem(boxinit)){
12880ee5cbfSDavid du Colombier 		free(mb);
12980ee5cbfSDavid du Colombier 		return "bad path";
13080ee5cbfSDavid du Colombier 	}
13180ee5cbfSDavid du Colombier 
13280ee5cbfSDavid du Colombier 	// on error, give up
133*5d459b5aSDavid du Colombier 	if(rv){
134*5d459b5aSDavid du Colombier 		free(mb);
13580ee5cbfSDavid du Colombier 		return rv;
136*5d459b5aSDavid du Colombier 	}
13780ee5cbfSDavid du Colombier 
1387dd7cddfSDavid du Colombier 	// make sure name isn't taken
1397dd7cddfSDavid du Colombier 	qlock(&mbllock);
14059cc4ca5SDavid du Colombier 	for(l = &mbl; *l != nil; l = &(*l)->next){
1417dd7cddfSDavid du Colombier 		if(strcmp((*l)->name, mb->name) == 0){
14259cc4ca5SDavid du Colombier 			if(strcmp(path, (*l)->path) == 0)
1437dd7cddfSDavid du Colombier 				rv = nil;
1447dd7cddfSDavid du Colombier 			else
1457dd7cddfSDavid du Colombier 				rv = "mbox name in use";
14680ee5cbfSDavid du Colombier 			if(mb->close)
14780ee5cbfSDavid du Colombier 				(*mb->close)(mb);
1487dd7cddfSDavid du Colombier 			free(mb);
1497dd7cddfSDavid du Colombier 			qunlock(&mbllock);
1507dd7cddfSDavid du Colombier 			return rv;
1517dd7cddfSDavid du Colombier 		}
15259cc4ca5SDavid du Colombier 	}
15359cc4ca5SDavid du Colombier 
15459cc4ca5SDavid du Colombier 	// all mailboxes in /mail/box/$user are locked using /mail/box/$user/mbox
15559cc4ca5SDavid du Colombier 	p = strrchr(stdmbox, '/');
15659cc4ca5SDavid du Colombier 	mb->dolock = strncmp(mb->path, stdmbox, p - stdmbox) == 0;
15759cc4ca5SDavid du Colombier 
1587dd7cddfSDavid du Colombier 	mb->refs = 1;
1597dd7cddfSDavid du Colombier 	mb->next = nil;
1607dd7cddfSDavid du Colombier 	mb->id = newid();
1617dd7cddfSDavid du Colombier 	mb->root = newmessage(nil);
1627dd7cddfSDavid du Colombier 	mb->std = std;
1637dd7cddfSDavid du Colombier 	*l = mb;
1647dd7cddfSDavid du Colombier 	qunlock(&mbllock);
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier 	qlock(mb);
16780ee5cbfSDavid du Colombier 	if(mb->ctl){
1689a747e4fSDavid du Colombier 		henter(PATH(mb->id, Qmbox), "ctl",
1699a747e4fSDavid du Colombier 			(Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb);
17080ee5cbfSDavid du Colombier 	}
1717dd7cddfSDavid du Colombier 	rv = syncmbox(mb, 0);
1727dd7cddfSDavid du Colombier 	qunlock(mb);
1737dd7cddfSDavid du Colombier 
1747dd7cddfSDavid du Colombier 	return rv;
1757dd7cddfSDavid du Colombier }
1767dd7cddfSDavid du Colombier 
1777dd7cddfSDavid du Colombier // close the named mailbox
1787dd7cddfSDavid du Colombier void
1797dd7cddfSDavid du Colombier freembox(char *name)
1807dd7cddfSDavid du Colombier {
181*5d459b5aSDavid du Colombier 	Mailbox **l, *mb;
1827dd7cddfSDavid du Colombier 
1837dd7cddfSDavid du Colombier 	qlock(&mbllock);
184*5d459b5aSDavid du Colombier 	for(l=&mbl; *l != nil; l=&(*l)->next){
185*5d459b5aSDavid du Colombier 		if(strcmp(name, (*l)->name) == 0){
186*5d459b5aSDavid du Colombier 			mb = *l;
187*5d459b5aSDavid du Colombier 			*l = mb->next;
1887dd7cddfSDavid du Colombier 			mboxdecref(mb);
1897dd7cddfSDavid du Colombier 			break;
1907dd7cddfSDavid du Colombier 		}
191*5d459b5aSDavid du Colombier 	}
1929a747e4fSDavid du Colombier 	hfree(PATH(0, Qtop), name);
1937dd7cddfSDavid du Colombier 	qunlock(&mbllock);
1947dd7cddfSDavid du Colombier }
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier static void
1977dd7cddfSDavid du Colombier initheaders(void)
1987dd7cddfSDavid du Colombier {
1997dd7cddfSDavid du Colombier 	Header *h;
2007dd7cddfSDavid du Colombier 	static int already;
2017dd7cddfSDavid du Colombier 
2027dd7cddfSDavid du Colombier 	if(already)
2037dd7cddfSDavid du Colombier 		return;
2047dd7cddfSDavid du Colombier 	already = 1;
2057dd7cddfSDavid du Colombier 
2067dd7cddfSDavid du Colombier 	for(h = head; h->type != nil; h++)
2077dd7cddfSDavid du Colombier 		h->len = strlen(h->type);
2087dd7cddfSDavid du Colombier }
2097dd7cddfSDavid du Colombier 
2107dd7cddfSDavid du Colombier /*
2117dd7cddfSDavid du Colombier  *  parse a Unix style header
2127dd7cddfSDavid du Colombier  */
21380ee5cbfSDavid du Colombier void
2147dd7cddfSDavid du Colombier parseunix(Message *m)
2157dd7cddfSDavid du Colombier {
2167dd7cddfSDavid du Colombier 	char *p;
2177dd7cddfSDavid du Colombier 	String *h;
2187dd7cddfSDavid du Colombier 
2197dd7cddfSDavid du Colombier 	h = s_new();
2207dd7cddfSDavid du Colombier 	for(p = m->start + 5; *p && *p != '\r' && *p != '\n'; p++)
2217dd7cddfSDavid du Colombier 		s_putc(h, *p);
2227dd7cddfSDavid du Colombier 	s_terminate(h);
2237dd7cddfSDavid du Colombier 	s_restart(h);
2247dd7cddfSDavid du Colombier 
2257dd7cddfSDavid du Colombier 	m->unixfrom = s_parse(h, s_reset(m->unixfrom));
2267dd7cddfSDavid du Colombier 	m->unixdate = s_append(s_reset(m->unixdate), h->ptr);
2277dd7cddfSDavid du Colombier 
2287dd7cddfSDavid du Colombier 	s_free(h);
2297dd7cddfSDavid du Colombier }
2307dd7cddfSDavid du Colombier 
2317dd7cddfSDavid du Colombier /*
2327dd7cddfSDavid du Colombier  *  parse a message
2337dd7cddfSDavid du Colombier  */
23480ee5cbfSDavid du Colombier void
23580ee5cbfSDavid du Colombier parseheaders(Message *m, int justmime, Mailbox *mb)
2367dd7cddfSDavid du Colombier {
2377dd7cddfSDavid du Colombier 	String *hl;
2387dd7cddfSDavid du Colombier 	Header *h;
23980ee5cbfSDavid du Colombier 	char *p, *q;
2407dd7cddfSDavid du Colombier 	int i;
2417dd7cddfSDavid du Colombier 
2427dd7cddfSDavid du Colombier 	if(m->whole == m->whole->whole){
2439a747e4fSDavid du Colombier 		henter(PATH(mb->id, Qmbox), m->name,
2449a747e4fSDavid du Colombier 			(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
2457dd7cddfSDavid du Colombier 	} else {
2469a747e4fSDavid du Colombier 		henter(PATH(m->whole->id, Qdir), m->name,
2479a747e4fSDavid du Colombier 			(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
2487dd7cddfSDavid du Colombier 	}
2497dd7cddfSDavid du Colombier 	for(i = 0; i < Qmax; i++)
2509a747e4fSDavid du Colombier 		henter(PATH(m->id, Qdir), dirtab[i],
2519a747e4fSDavid du Colombier 			(Qid){PATH(m->id, i), 0, QTFILE}, m, mb);
2527dd7cddfSDavid du Colombier 
2537dd7cddfSDavid du Colombier 	// parse mime headers
2547dd7cddfSDavid du Colombier 	p = m->header;
2557dd7cddfSDavid du Colombier 	hl = s_new();
2567dd7cddfSDavid du Colombier 	while(headerline(&p, hl)){
2577dd7cddfSDavid du Colombier 		if(justmime)
2587dd7cddfSDavid du Colombier 			h = &head[Mhead];
2597dd7cddfSDavid du Colombier 		else
2607dd7cddfSDavid du Colombier 			h = head;
2617dd7cddfSDavid du Colombier 		for(; h->type; h++){
2627dd7cddfSDavid du Colombier 			if(cistrncmp(s_to_c(hl), h->type, h->len) == 0){
2637dd7cddfSDavid du Colombier 				(*h->f)(m, h, s_to_c(hl));
2647dd7cddfSDavid du Colombier 				break;
2657dd7cddfSDavid du Colombier 			}
2667dd7cddfSDavid du Colombier 		}
2677dd7cddfSDavid du Colombier 		s_reset(hl);
2687dd7cddfSDavid du Colombier 	}
2697dd7cddfSDavid du Colombier 	s_free(hl);
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 	// the blank line isn't really part of the body or header
2727dd7cddfSDavid du Colombier 	if(justmime){
2737dd7cddfSDavid du Colombier 		m->mhend = p;
2747dd7cddfSDavid du Colombier 		m->hend = m->header;
2757dd7cddfSDavid du Colombier 	} else {
2767dd7cddfSDavid du Colombier 		m->hend = p;
2777dd7cddfSDavid du Colombier 	}
2787dd7cddfSDavid du Colombier 	if(*p == '\n')
2797dd7cddfSDavid du Colombier 		p++;
2807dd7cddfSDavid du Colombier 	m->rbody = m->body = p;
2817dd7cddfSDavid 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 	//
28980ee5cbfSDavid du Colombier 	if(m->unixdate == nil){
29080ee5cbfSDavid du Colombier 		// look for the date in the first Received: line.
29180ee5cbfSDavid du Colombier 		// it's likely to be the right time zone (it's
29280ee5cbfSDavid du Colombier 	 	// the local system) and in a convenient format.
29380ee5cbfSDavid du Colombier 		if(cistrncmp(m->header, "received:", 9)==0){
29480ee5cbfSDavid du Colombier 			if((q = strchr(m->header, ';'))
29580ee5cbfSDavid du Colombier 			&& (p = strchr(q, '\n'))){
29680ee5cbfSDavid du Colombier 				*p = '\0';
29780ee5cbfSDavid du Colombier 				m->unixdate = date822tounix(q+1);
29880ee5cbfSDavid du Colombier 				*p = '\n';
29980ee5cbfSDavid du Colombier 			}
30080ee5cbfSDavid du Colombier 		}
30180ee5cbfSDavid du Colombier 
30280ee5cbfSDavid du Colombier 		// fall back on the rfc822 date
30380ee5cbfSDavid du Colombier 		if(m->unixdate==nil && m->date822)
30480ee5cbfSDavid du Colombier 			m->unixdate = date822tounix(s_to_c(m->date822));
30580ee5cbfSDavid du Colombier 	}
30680ee5cbfSDavid du Colombier 
3079a747e4fSDavid du Colombier 	if(m->unixheader != nil)
3089a747e4fSDavid du Colombier 		s_free(m->unixheader);
30980ee5cbfSDavid du Colombier 	m->unixheader = s_copy("From ");
31080ee5cbfSDavid du Colombier 	if(m->unixfrom)
31180ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->unixfrom));
31280ee5cbfSDavid du Colombier 	else if(m->from822)
31380ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->from822));
31480ee5cbfSDavid du Colombier 	else
31580ee5cbfSDavid du Colombier 		s_append(m->unixheader, "???");
31680ee5cbfSDavid du Colombier 
31780ee5cbfSDavid du Colombier 	s_append(m->unixheader, " ");
31880ee5cbfSDavid du Colombier 	if(m->unixdate)
31980ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->unixdate));
32080ee5cbfSDavid du Colombier 	else
32180ee5cbfSDavid du Colombier 		s_append(m->unixheader, "Thu Jan  1 00:00:00 EST 1970");
32280ee5cbfSDavid du Colombier 
32380ee5cbfSDavid du Colombier 	s_append(m->unixheader, "\n");
32480ee5cbfSDavid du Colombier }
32580ee5cbfSDavid du Colombier 
3269a747e4fSDavid du Colombier String*
3279a747e4fSDavid du Colombier promote(String **sp)
3289a747e4fSDavid du Colombier {
3299a747e4fSDavid du Colombier 	String *s;
3309a747e4fSDavid du Colombier 
3319a747e4fSDavid du Colombier 	if(*sp != nil)
3329a747e4fSDavid du Colombier 		s = s_clone(*sp);
3339a747e4fSDavid du Colombier 	else
3349a747e4fSDavid du Colombier 		s = nil;
3359a747e4fSDavid du Colombier 	return s;
3369a747e4fSDavid du Colombier }
3379a747e4fSDavid du Colombier 
33880ee5cbfSDavid du Colombier void
33980ee5cbfSDavid du Colombier parsebody(Message *m, Mailbox *mb)
34080ee5cbfSDavid du Colombier {
3419a747e4fSDavid du Colombier 	Message *nm;
3429a747e4fSDavid du Colombier 
3437dd7cddfSDavid du Colombier 	// recurse
3447dd7cddfSDavid du Colombier 	if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){
3457dd7cddfSDavid du Colombier 		parseattachments(m, mb);
3467dd7cddfSDavid du Colombier 	} else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
3477dd7cddfSDavid du Colombier 		decode(m);
3487dd7cddfSDavid du Colombier 		parseattachments(m, mb);
3499a747e4fSDavid du Colombier 		nm = m->part;
3509a747e4fSDavid du Colombier 
3519a747e4fSDavid du Colombier 		// promote headers
352*5d459b5aSDavid du Colombier 		if(m->replyto822 == nil && m->from822 == nil && m->sender822 == nil){
3539a747e4fSDavid du Colombier 			m->from822 = promote(&nm->from822);
3549a747e4fSDavid du Colombier 			m->to822 = promote(&nm->to822);
3559a747e4fSDavid du Colombier 			m->date822 = promote(&nm->date822);
3569a747e4fSDavid du Colombier 			m->sender822 = promote(&nm->sender822);
3579a747e4fSDavid du Colombier 			m->replyto822 = promote(&nm->replyto822);
3589a747e4fSDavid du Colombier 			m->subject822 = promote(&nm->subject822);
3599a747e4fSDavid du Colombier 			m->unixdate = promote(&nm->unixdate);
3607dd7cddfSDavid du Colombier 		}
3617dd7cddfSDavid du Colombier 	}
362*5d459b5aSDavid du Colombier }
3637dd7cddfSDavid du Colombier 
36480ee5cbfSDavid du Colombier void
36580ee5cbfSDavid du Colombier parse(Message *m, int justmime, Mailbox *mb)
36680ee5cbfSDavid du Colombier {
36780ee5cbfSDavid du Colombier 	parseheaders(m, justmime, mb);
36880ee5cbfSDavid du Colombier 	parsebody(m, mb);
36980ee5cbfSDavid du Colombier }
37080ee5cbfSDavid du Colombier 
3717dd7cddfSDavid du Colombier static void
3727dd7cddfSDavid du Colombier parseattachments(Message *m, Mailbox *mb)
3737dd7cddfSDavid du Colombier {
3747dd7cddfSDavid du Colombier 	Message *nm, **l;
3757dd7cddfSDavid du Colombier 	char *p, *x;
3767dd7cddfSDavid du Colombier 
3777dd7cddfSDavid du Colombier 	// if there's a boundary, recurse...
3787dd7cddfSDavid du Colombier 	if(m->boundary != nil){
3797dd7cddfSDavid du Colombier 		p = m->body;
3807dd7cddfSDavid du Colombier 		nm = nil;
3817dd7cddfSDavid du Colombier 		l = &m->part;
3827dd7cddfSDavid du Colombier 		for(;;){
3837dd7cddfSDavid du Colombier 			x = strstr(p, s_to_c(m->boundary));
3847dd7cddfSDavid du Colombier 			if(x == nil || (x != m->body && *(x-1) != '\n')){
3857dd7cddfSDavid du Colombier 				if(nm != nil)
3867dd7cddfSDavid du Colombier 					nm->rbend = nm->bend = nm->end = m->bend;
3877dd7cddfSDavid du Colombier 				break;
3887dd7cddfSDavid du Colombier 			}
3897dd7cddfSDavid du Colombier 			if(nm != nil)
3907dd7cddfSDavid du Colombier 				nm->rbend = nm->bend = nm->end = x;
3917dd7cddfSDavid du Colombier 			x += strlen(s_to_c(m->boundary));
3927dd7cddfSDavid du Colombier 
3937dd7cddfSDavid du Colombier 			/* is this the last part? ignore anything after it */
3947dd7cddfSDavid du Colombier 			if(strncmp(x, "--", 2) == 0)
3957dd7cddfSDavid du Colombier 				break;
3967dd7cddfSDavid du Colombier 
3977dd7cddfSDavid du Colombier 			p = strchr(x, '\n');
3987dd7cddfSDavid du Colombier 			if(p == nil)
3997dd7cddfSDavid du Colombier 				break;
4007dd7cddfSDavid du Colombier 			nm = newmessage(m);
4017dd7cddfSDavid du Colombier 			nm->start = nm->header = nm->body = nm->rbody = ++p;
4027dd7cddfSDavid du Colombier 			nm->mheader = nm->header;
4037dd7cddfSDavid du Colombier 			*l = nm;
4047dd7cddfSDavid du Colombier 			l = &nm->next;
4057dd7cddfSDavid du Colombier 		}
4067dd7cddfSDavid du Colombier 		for(nm = m->part; nm != nil; nm = nm->next)
4077dd7cddfSDavid du Colombier 			parse(nm, 1, mb);
4089a747e4fSDavid du Colombier 		return;
4097dd7cddfSDavid du Colombier 	}
4109a747e4fSDavid du Colombier 
4119a747e4fSDavid du Colombier 	// if we've got an rfc822 message, recurse...
4129a747e4fSDavid du Colombier 	if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
4137dd7cddfSDavid du Colombier 		nm = newmessage(m);
4147dd7cddfSDavid du Colombier 		m->part = nm;
4157dd7cddfSDavid du Colombier 		nm->start = nm->header = nm->body = nm->rbody = m->body;
4167dd7cddfSDavid du Colombier 		nm->end = nm->bend = nm->rbend = m->bend;
4177dd7cddfSDavid du Colombier 		parse(nm, 0, mb);
4187dd7cddfSDavid du Colombier 	}
4197dd7cddfSDavid du Colombier }
4207dd7cddfSDavid du Colombier 
4217dd7cddfSDavid du Colombier /*
4227dd7cddfSDavid du Colombier  *  pick up a header line
4237dd7cddfSDavid du Colombier  */
4247dd7cddfSDavid du Colombier static int
4257dd7cddfSDavid du Colombier headerline(char **pp, String *hl)
4267dd7cddfSDavid du Colombier {
4277dd7cddfSDavid du Colombier 	char *p, *x;
4287dd7cddfSDavid du Colombier 
4297dd7cddfSDavid du Colombier 	s_reset(hl);
4307dd7cddfSDavid du Colombier 	p = *pp;
4317dd7cddfSDavid du Colombier 	x = strpbrk(p, ":\n");
4327dd7cddfSDavid du Colombier 	if(x == nil || *x == '\n')
4337dd7cddfSDavid du Colombier 		return 0;
4347dd7cddfSDavid du Colombier 	for(;;){
4357dd7cddfSDavid du Colombier 		x = strchr(p, '\n');
4367dd7cddfSDavid du Colombier 		if(x == nil)
4377dd7cddfSDavid du Colombier 			x = p + strlen(p);
4387dd7cddfSDavid du Colombier 		s_nappend(hl, p, x-p);
4397dd7cddfSDavid du Colombier 		p = x;
440223a736eSDavid du Colombier 		if(*p != '\n' || *++p != ' ' && *p != '\t')
4417dd7cddfSDavid du Colombier 			break;
442223a736eSDavid du Colombier 		while(*p == ' ' || *p == '\t')
443223a736eSDavid du Colombier 			p++;
444223a736eSDavid du Colombier 		s_putc(hl, ' ');
4457dd7cddfSDavid du Colombier 	}
4467dd7cddfSDavid du Colombier 	*pp = p;
4477dd7cddfSDavid du Colombier 	return 1;
4487dd7cddfSDavid du Colombier }
4497dd7cddfSDavid du Colombier 
4507dd7cddfSDavid du Colombier static String*
4517dd7cddfSDavid du Colombier addr822(char *p)
4527dd7cddfSDavid du Colombier {
4537dd7cddfSDavid du Colombier 	String *s, *list;
4547dd7cddfSDavid du Colombier 	int incomment, addrdone, inanticomment, quoted;
4557dd7cddfSDavid du Colombier 	int n;
4567dd7cddfSDavid du Colombier 	int c;
4577dd7cddfSDavid du Colombier 
4587dd7cddfSDavid du Colombier 	list = s_new();
4597dd7cddfSDavid du Colombier 	s = s_new();
4607dd7cddfSDavid du Colombier 	quoted = incomment = addrdone = inanticomment = 0;
4617dd7cddfSDavid du Colombier 	n = 0;
4627dd7cddfSDavid du Colombier 	for(; *p; p++){
4637dd7cddfSDavid du Colombier 		c = *p;
4647dd7cddfSDavid du Colombier 
4657dd7cddfSDavid du Colombier 		// whitespace is ignored
4667dd7cddfSDavid du Colombier 		if(!quoted && isspace(c) || c == '\r')
4677dd7cddfSDavid du Colombier 			continue;
4687dd7cddfSDavid du Colombier 
4697dd7cddfSDavid du Colombier 		// strings are always treated as atoms
4707dd7cddfSDavid du Colombier 		if(!quoted && c == '"'){
4717dd7cddfSDavid du Colombier 			if(!addrdone && !incomment)
4727dd7cddfSDavid du Colombier 				s_putc(s, c);
4737dd7cddfSDavid du Colombier 			for(p++; *p; p++){
4747dd7cddfSDavid du Colombier 				if(!addrdone && !incomment)
4757dd7cddfSDavid du Colombier 					s_putc(s, *p);
4767dd7cddfSDavid du Colombier 				if(!quoted && *p == '"')
4777dd7cddfSDavid du Colombier 					break;
4787dd7cddfSDavid du Colombier 				if(*p == '\\')
4797dd7cddfSDavid du Colombier 					quoted = 1;
4807dd7cddfSDavid du Colombier 				else
4817dd7cddfSDavid du Colombier 					quoted = 0;
4827dd7cddfSDavid du Colombier 			}
4837dd7cddfSDavid du Colombier 			if(*p == 0)
4847dd7cddfSDavid du Colombier 				break;
4857dd7cddfSDavid du Colombier 			quoted = 0;
4867dd7cddfSDavid du Colombier 			continue;
4877dd7cddfSDavid du Colombier 		}
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 		// ignore everything in an expicit comment
4907dd7cddfSDavid du Colombier 		if(!quoted && c == '('){
4917dd7cddfSDavid du Colombier 			incomment = 1;
4927dd7cddfSDavid du Colombier 			continue;
4937dd7cddfSDavid du Colombier 		}
4947dd7cddfSDavid du Colombier 		if(incomment){
4957dd7cddfSDavid du Colombier 			if(!quoted && c == ')')
4967dd7cddfSDavid du Colombier 				incomment = 0;
4977dd7cddfSDavid du Colombier 			quoted = 0;
4987dd7cddfSDavid du Colombier 			continue;
4997dd7cddfSDavid du Colombier 		}
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier 		// anticomments makes everything outside of them comments
5027dd7cddfSDavid du Colombier 		if(!quoted && c == '<' && !inanticomment){
5037dd7cddfSDavid du Colombier 			inanticomment = 1;
5047dd7cddfSDavid du Colombier 			s = s_reset(s);
5057dd7cddfSDavid du Colombier 			continue;
5067dd7cddfSDavid du Colombier 		}
5077dd7cddfSDavid du Colombier 		if(!quoted && c == '>' && inanticomment){
5087dd7cddfSDavid du Colombier 			addrdone = 1;
5097dd7cddfSDavid du Colombier 			inanticomment = 0;
5107dd7cddfSDavid du Colombier 			continue;
5117dd7cddfSDavid du Colombier 		}
5127dd7cddfSDavid du Colombier 
5137dd7cddfSDavid du Colombier 		// commas separate addresses
5147dd7cddfSDavid du Colombier 		if(!quoted && c == ',' && !inanticomment){
5157dd7cddfSDavid du Colombier 			s_terminate(s);
5167dd7cddfSDavid du Colombier 			addrdone = 0;
5177dd7cddfSDavid du Colombier 			if(n++ != 0)
5187dd7cddfSDavid du Colombier 				s_append(list, " ");
5197dd7cddfSDavid du Colombier 			s_append(list, s_to_c(s));
5207dd7cddfSDavid du Colombier 			s = s_reset(s);
5217dd7cddfSDavid du Colombier 			continue;
5227dd7cddfSDavid du Colombier 		}
5237dd7cddfSDavid du Colombier 
5247dd7cddfSDavid du Colombier 		// what's left is part of the address
5257dd7cddfSDavid du Colombier 		s_putc(s, c);
5267dd7cddfSDavid du Colombier 
5277dd7cddfSDavid du Colombier 		// quoted characters are recognized only as characters
5287dd7cddfSDavid du Colombier 		if(c == '\\')
5297dd7cddfSDavid du Colombier 			quoted = 1;
5307dd7cddfSDavid du Colombier 		else
5317dd7cddfSDavid du Colombier 			quoted = 0;
5327dd7cddfSDavid du Colombier 
5337dd7cddfSDavid du Colombier 	}
5347dd7cddfSDavid du Colombier 
5357dd7cddfSDavid du Colombier 	if(*s_to_c(s) != 0){
5367dd7cddfSDavid du Colombier 		s_terminate(s);
5377dd7cddfSDavid du Colombier 		if(n++ != 0)
5387dd7cddfSDavid du Colombier 			s_append(list, " ");
5397dd7cddfSDavid du Colombier 		s_append(list, s_to_c(s));
5407dd7cddfSDavid du Colombier 	}
5417dd7cddfSDavid du Colombier 	s_free(s);
5427dd7cddfSDavid du Colombier 
5437dd7cddfSDavid du Colombier 	if(n == 0){
5447dd7cddfSDavid du Colombier 		s_free(list);
5457dd7cddfSDavid du Colombier 		return nil;
5467dd7cddfSDavid du Colombier 	}
5477dd7cddfSDavid du Colombier 	return list;
5487dd7cddfSDavid du Colombier }
5497dd7cddfSDavid du Colombier 
5507dd7cddfSDavid du Colombier static void
5517dd7cddfSDavid du Colombier to822(Message *m, Header *h, char *p)
5527dd7cddfSDavid du Colombier {
5537dd7cddfSDavid du Colombier 	p += strlen(h->type);
5547dd7cddfSDavid du Colombier 	s_free(m->to822);
5557dd7cddfSDavid du Colombier 	m->to822 = addr822(p);
5567dd7cddfSDavid du Colombier }
5577dd7cddfSDavid du Colombier 
5587dd7cddfSDavid du Colombier static void
5597dd7cddfSDavid du Colombier cc822(Message *m, Header *h, char *p)
5607dd7cddfSDavid du Colombier {
5617dd7cddfSDavid du Colombier 	p += strlen(h->type);
5627dd7cddfSDavid du Colombier 	s_free(m->cc822);
5637dd7cddfSDavid du Colombier 	m->cc822 = addr822(p);
5647dd7cddfSDavid du Colombier }
5657dd7cddfSDavid du Colombier 
5667dd7cddfSDavid du Colombier static void
5677dd7cddfSDavid du Colombier bcc822(Message *m, Header *h, char *p)
5687dd7cddfSDavid du Colombier {
5697dd7cddfSDavid du Colombier 	p += strlen(h->type);
5707dd7cddfSDavid du Colombier 	s_free(m->bcc822);
5717dd7cddfSDavid du Colombier 	m->bcc822 = addr822(p);
5727dd7cddfSDavid du Colombier }
5737dd7cddfSDavid du Colombier 
5747dd7cddfSDavid du Colombier static void
5757dd7cddfSDavid du Colombier from822(Message *m, Header *h, char *p)
5767dd7cddfSDavid du Colombier {
5777dd7cddfSDavid du Colombier 	p += strlen(h->type);
5787dd7cddfSDavid du Colombier 	s_free(m->from822);
5797dd7cddfSDavid du Colombier 	m->from822 = addr822(p);
5807dd7cddfSDavid du Colombier }
5817dd7cddfSDavid du Colombier 
5827dd7cddfSDavid du Colombier static void
5837dd7cddfSDavid du Colombier sender822(Message *m, Header *h, char *p)
5847dd7cddfSDavid du Colombier {
5857dd7cddfSDavid du Colombier 	p += strlen(h->type);
5867dd7cddfSDavid du Colombier 	s_free(m->sender822);
5877dd7cddfSDavid du Colombier 	m->sender822 = addr822(p);
5887dd7cddfSDavid du Colombier }
5897dd7cddfSDavid du Colombier 
5907dd7cddfSDavid du Colombier static void
5917dd7cddfSDavid du Colombier replyto822(Message *m, Header *h, char *p)
5927dd7cddfSDavid du Colombier {
5937dd7cddfSDavid du Colombier 	p += strlen(h->type);
5947dd7cddfSDavid du Colombier 	s_free(m->replyto822);
5957dd7cddfSDavid du Colombier 	m->replyto822 = addr822(p);
5967dd7cddfSDavid du Colombier }
5977dd7cddfSDavid du Colombier 
5987dd7cddfSDavid du Colombier static void
5997dd7cddfSDavid du Colombier mimeversion(Message *m, Header *h, char *p)
6007dd7cddfSDavid du Colombier {
6017dd7cddfSDavid du Colombier 	p += strlen(h->type);
6027dd7cddfSDavid du Colombier 	s_free(m->mimeversion);
6037dd7cddfSDavid du Colombier 	m->mimeversion = addr822(p);
6047dd7cddfSDavid du Colombier }
6057dd7cddfSDavid du Colombier 
6067dd7cddfSDavid du Colombier static void
6077dd7cddfSDavid du Colombier killtrailingwhite(char *p)
6087dd7cddfSDavid du Colombier {
6097dd7cddfSDavid du Colombier 	char *e;
6107dd7cddfSDavid du Colombier 
6117dd7cddfSDavid du Colombier 	e = p + strlen(p) - 1;
6127dd7cddfSDavid du Colombier 	while(e > p && isspace(*e))
6137dd7cddfSDavid du Colombier 		*e-- = 0;
6147dd7cddfSDavid du Colombier }
6157dd7cddfSDavid du Colombier 
6167dd7cddfSDavid du Colombier static void
6177dd7cddfSDavid du Colombier date822(Message *m, Header *h, char *p)
6187dd7cddfSDavid du Colombier {
6197dd7cddfSDavid du Colombier 	p += strlen(h->type);
6207dd7cddfSDavid du Colombier 	p = skipwhite(p);
6217dd7cddfSDavid du Colombier 	s_free(m->date822);
6227dd7cddfSDavid du Colombier 	m->date822 = s_copy(p);
6237dd7cddfSDavid du Colombier 	p = s_to_c(m->date822);
6247dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6257dd7cddfSDavid du Colombier }
6267dd7cddfSDavid du Colombier 
6277dd7cddfSDavid du Colombier static void
6287dd7cddfSDavid du Colombier subject822(Message *m, Header *h, char *p)
6297dd7cddfSDavid du Colombier {
6307dd7cddfSDavid du Colombier 	p += strlen(h->type);
6317dd7cddfSDavid du Colombier 	p = skipwhite(p);
6327dd7cddfSDavid du Colombier 	s_free(m->subject822);
6337dd7cddfSDavid du Colombier 	m->subject822 = s_copy(p);
6347dd7cddfSDavid du Colombier 	p = s_to_c(m->subject822);
6357dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6367dd7cddfSDavid du Colombier }
6377dd7cddfSDavid du Colombier 
6387dd7cddfSDavid du Colombier static void
6397dd7cddfSDavid du Colombier inreplyto822(Message *m, Header *h, char *p)
6407dd7cddfSDavid du Colombier {
6417dd7cddfSDavid du Colombier 	p += strlen(h->type);
6427dd7cddfSDavid du Colombier 	p = skipwhite(p);
6437dd7cddfSDavid du Colombier 	s_free(m->inreplyto822);
6447dd7cddfSDavid du Colombier 	m->inreplyto822 = s_copy(p);
6457dd7cddfSDavid du Colombier 	p = s_to_c(m->inreplyto822);
6467dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6477dd7cddfSDavid du Colombier }
6487dd7cddfSDavid du Colombier 
6497dd7cddfSDavid du Colombier static void
6507dd7cddfSDavid du Colombier messageid822(Message *m, Header *h, char *p)
6517dd7cddfSDavid du Colombier {
6527dd7cddfSDavid du Colombier 	p += strlen(h->type);
6537dd7cddfSDavid du Colombier 	p = skipwhite(p);
6547dd7cddfSDavid du Colombier 	s_free(m->messageid822);
6557dd7cddfSDavid du Colombier 	m->messageid822 = s_copy(p);
6567dd7cddfSDavid du Colombier 	p = s_to_c(m->messageid822);
6577dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6587dd7cddfSDavid du Colombier }
6597dd7cddfSDavid du Colombier 
6607dd7cddfSDavid du Colombier static int
6617dd7cddfSDavid du Colombier isattribute(char **pp, char *attr)
6627dd7cddfSDavid du Colombier {
6637dd7cddfSDavid du Colombier 	char *p;
6647dd7cddfSDavid du Colombier 	int n;
6657dd7cddfSDavid du Colombier 
6667dd7cddfSDavid du Colombier 	n = strlen(attr);
6677dd7cddfSDavid du Colombier 	p = *pp;
6687dd7cddfSDavid du Colombier 	if(cistrncmp(p, attr, n) != 0)
6697dd7cddfSDavid du Colombier 		return 0;
6707dd7cddfSDavid du Colombier 	p += n;
6717dd7cddfSDavid du Colombier 	while(*p == ' ')
6727dd7cddfSDavid du Colombier 		p++;
6737dd7cddfSDavid du Colombier 	if(*p++ != '=')
6747dd7cddfSDavid du Colombier 		return 0;
6757dd7cddfSDavid du Colombier 	while(*p == ' ')
6767dd7cddfSDavid du Colombier 		p++;
6777dd7cddfSDavid du Colombier 	*pp = p;
6787dd7cddfSDavid du Colombier 	return 1;
6797dd7cddfSDavid du Colombier }
6807dd7cddfSDavid du Colombier 
6817dd7cddfSDavid du Colombier static void
6827dd7cddfSDavid du Colombier ctype(Message *m, Header *h, char *p)
6837dd7cddfSDavid du Colombier {
6847dd7cddfSDavid du Colombier 	String *s;
6857dd7cddfSDavid du Colombier 
6867dd7cddfSDavid du Colombier 	p += h->len;
6877dd7cddfSDavid du Colombier 	p = skipwhite(p);
6887dd7cddfSDavid du Colombier 
6897dd7cddfSDavid du Colombier 	p = getstring(p, m->type, 1);
6907dd7cddfSDavid du Colombier 
6917dd7cddfSDavid du Colombier 	while(*p){
6927dd7cddfSDavid du Colombier 		if(isattribute(&p, "boundary")){
6937dd7cddfSDavid du Colombier 			s = s_new();
6947dd7cddfSDavid du Colombier 			p = getstring(p, s, 0);
6957dd7cddfSDavid du Colombier 			m->boundary = s_reset(m->boundary);
6967dd7cddfSDavid du Colombier 			s_append(m->boundary, "--");
6977dd7cddfSDavid du Colombier 			s_append(m->boundary, s_to_c(s));
6987dd7cddfSDavid du Colombier 			s_free(s);
6997dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "multipart", 9) == 0){
7007dd7cddfSDavid du Colombier 			/*
7017dd7cddfSDavid du Colombier 			 *  the first unbounded part of a multipart message,
7027dd7cddfSDavid du Colombier 			 *  the preamble, is not displayed or saved
7037dd7cddfSDavid du Colombier 			 */
7047dd7cddfSDavid du Colombier 		} else if(isattribute(&p, "name")){
7057dd7cddfSDavid du Colombier 			if(m->filename == nil)
7067dd7cddfSDavid du Colombier 				setfilename(m, p);
7077dd7cddfSDavid du Colombier 		} else if(isattribute(&p, "charset")){
7087dd7cddfSDavid du Colombier 			p = getstring(p, s_reset(m->charset), 0);
7097dd7cddfSDavid du Colombier 		}
7107dd7cddfSDavid du Colombier 
7117dd7cddfSDavid du Colombier 		p = skiptosemi(p);
7127dd7cddfSDavid du Colombier 	}
7137dd7cddfSDavid du Colombier }
7147dd7cddfSDavid du Colombier 
7157dd7cddfSDavid du Colombier static void
7167dd7cddfSDavid du Colombier cencoding(Message *m, Header *h, char *p)
7177dd7cddfSDavid du Colombier {
7187dd7cddfSDavid du Colombier 	p += h->len;
7197dd7cddfSDavid du Colombier 	p = skipwhite(p);
7207dd7cddfSDavid du Colombier 	if(cistrncmp(p, "base64", 6) == 0)
7217dd7cddfSDavid du Colombier 		m->encoding = Ebase64;
7227dd7cddfSDavid du Colombier 	else if(cistrncmp(p, "quoted-printable", 16) == 0)
7237dd7cddfSDavid du Colombier 		m->encoding = Equoted;
7247dd7cddfSDavid du Colombier }
7257dd7cddfSDavid du Colombier 
7267dd7cddfSDavid du Colombier static void
7277dd7cddfSDavid du Colombier cdisposition(Message *m, Header *h, char *p)
7287dd7cddfSDavid du Colombier {
7297dd7cddfSDavid du Colombier 	p += h->len;
7307dd7cddfSDavid du Colombier 	p = skipwhite(p);
7317dd7cddfSDavid du Colombier 	while(*p){
7327dd7cddfSDavid du Colombier 		if(cistrncmp(p, "inline", 6) == 0){
7337dd7cddfSDavid du Colombier 			m->disposition = Dinline;
7347dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "attachment", 10) == 0){
7357dd7cddfSDavid du Colombier 			m->disposition = Dfile;
7367dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "filename=", 9) == 0){
7377dd7cddfSDavid du Colombier 			p += 9;
7387dd7cddfSDavid du Colombier 			setfilename(m, p);
7397dd7cddfSDavid du Colombier 		}
7407dd7cddfSDavid du Colombier 		p = skiptosemi(p);
7417dd7cddfSDavid du Colombier 	}
7427dd7cddfSDavid du Colombier 
7437dd7cddfSDavid du Colombier }
7447dd7cddfSDavid du Colombier 
7457dd7cddfSDavid du Colombier ulong msgallocd, msgfreed;
7467dd7cddfSDavid du Colombier 
7477dd7cddfSDavid du Colombier Message*
7487dd7cddfSDavid du Colombier newmessage(Message *parent)
7497dd7cddfSDavid du Colombier {
7507dd7cddfSDavid du Colombier 	static int id;
7517dd7cddfSDavid du Colombier 	Message *m;
7527dd7cddfSDavid du Colombier 
7537dd7cddfSDavid du Colombier 	msgallocd++;
7547dd7cddfSDavid du Colombier 
7557dd7cddfSDavid du Colombier 	m = emalloc(sizeof(*m));
7567dd7cddfSDavid du Colombier 	memset(m, 0, sizeof(*m));
7577dd7cddfSDavid du Colombier 	m->disposition = Dnone;
7587dd7cddfSDavid du Colombier 	m->type = s_copy("text/plain");
7597dd7cddfSDavid du Colombier 	m->charset = s_copy("iso-8859-1");
7607dd7cddfSDavid du Colombier 	m->id = newid();
7617dd7cddfSDavid du Colombier 	if(parent)
7627dd7cddfSDavid du Colombier 		sprint(m->name, "%d", ++(parent->subname));
7637dd7cddfSDavid du Colombier 	if(parent == nil)
7647dd7cddfSDavid du Colombier 		parent = m;
7657dd7cddfSDavid du Colombier 	m->whole = parent;
7667dd7cddfSDavid du Colombier 	m->hlen = -1;
7677dd7cddfSDavid du Colombier 	return m;
7687dd7cddfSDavid du Colombier }
7697dd7cddfSDavid du Colombier 
7707dd7cddfSDavid du Colombier // delete a message from a mailbox
7717dd7cddfSDavid du Colombier void
7727dd7cddfSDavid du Colombier delmessage(Mailbox *mb, Message *m)
7737dd7cddfSDavid du Colombier {
7747dd7cddfSDavid du Colombier 	Message **l;
7757dd7cddfSDavid du Colombier 	int i;
7767dd7cddfSDavid du Colombier 
7777dd7cddfSDavid du Colombier 	mb->vers++;
7787dd7cddfSDavid du Colombier 	msgfreed++;
7797dd7cddfSDavid du Colombier 
7807dd7cddfSDavid du Colombier 	if(m->whole != m){
7817dd7cddfSDavid du Colombier 		// unchain from parent
7827dd7cddfSDavid du Colombier 		for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
7837dd7cddfSDavid du Colombier 			;
7847dd7cddfSDavid du Colombier 		if(*l != nil)
7857dd7cddfSDavid du Colombier 			*l = m->next;
7867dd7cddfSDavid du Colombier 
7877dd7cddfSDavid du Colombier 		// clear out of name lookup hash table
7887dd7cddfSDavid du Colombier 		if(m->whole->whole == m->whole)
7899a747e4fSDavid du Colombier 			hfree(PATH(mb->id, Qmbox), m->name);
7907dd7cddfSDavid du Colombier 		else
7919a747e4fSDavid du Colombier 			hfree(PATH(m->whole->id, Qdir), m->name);
7927dd7cddfSDavid du Colombier 		for(i = 0; i < Qmax; i++)
7939a747e4fSDavid du Colombier 			hfree(PATH(m->id, Qdir), dirtab[i]);
7947dd7cddfSDavid du Colombier 	}
7957dd7cddfSDavid du Colombier 
7967dd7cddfSDavid du Colombier 	/* recurse through sub-parts */
7977dd7cddfSDavid du Colombier 	while(m->part)
7987dd7cddfSDavid du Colombier 		delmessage(mb, m->part);
7997dd7cddfSDavid du Colombier 
8007dd7cddfSDavid du Colombier 	/* free memory */
8017dd7cddfSDavid du Colombier 	if(m->mallocd)
8027dd7cddfSDavid du Colombier 		free(m->start);
8037dd7cddfSDavid du Colombier 	if(m->hallocd)
8047dd7cddfSDavid du Colombier 		free(m->header);
8057dd7cddfSDavid du Colombier 	if(m->ballocd)
8067dd7cddfSDavid du Colombier 		free(m->body);
8077dd7cddfSDavid du Colombier 	s_free(m->unixfrom);
8087dd7cddfSDavid du Colombier 	s_free(m->unixdate);
8099a747e4fSDavid du Colombier 	s_free(m->unixheader);
8107dd7cddfSDavid du Colombier 	s_free(m->from822);
8117dd7cddfSDavid du Colombier 	s_free(m->sender822);
8127dd7cddfSDavid du Colombier 	s_free(m->to822);
8137dd7cddfSDavid du Colombier 	s_free(m->bcc822);
81459cc4ca5SDavid du Colombier 	s_free(m->cc822);
8157dd7cddfSDavid du Colombier 	s_free(m->replyto822);
8167dd7cddfSDavid du Colombier 	s_free(m->date822);
8177dd7cddfSDavid du Colombier 	s_free(m->inreplyto822);
81859cc4ca5SDavid du Colombier 	s_free(m->subject822);
8197dd7cddfSDavid du Colombier 	s_free(m->messageid822);
8207dd7cddfSDavid du Colombier 	s_free(m->addrs);
8217dd7cddfSDavid du Colombier 	s_free(m->mimeversion);
82259cc4ca5SDavid du Colombier 	s_free(m->sdigest);
8237dd7cddfSDavid du Colombier 	s_free(m->boundary);
8247dd7cddfSDavid du Colombier 	s_free(m->type);
8257dd7cddfSDavid du Colombier 	s_free(m->charset);
8267dd7cddfSDavid du Colombier 	s_free(m->filename);
8277dd7cddfSDavid du Colombier 
8287dd7cddfSDavid du Colombier 	free(m);
8297dd7cddfSDavid du Colombier }
8307dd7cddfSDavid du Colombier 
8317dd7cddfSDavid du Colombier // mark messages (identified by path) for deletion
8327dd7cddfSDavid du Colombier void
8337dd7cddfSDavid du Colombier delmessages(int ac, char **av)
8347dd7cddfSDavid du Colombier {
8357dd7cddfSDavid du Colombier 	Mailbox *mb;
8367dd7cddfSDavid du Colombier 	Message *m;
8377dd7cddfSDavid du Colombier 	int i, needwrite;
8387dd7cddfSDavid du Colombier 
8397dd7cddfSDavid du Colombier 	qlock(&mbllock);
8407dd7cddfSDavid du Colombier 	for(mb = mbl; mb != nil; mb = mb->next)
8417dd7cddfSDavid du Colombier 		if(strcmp(av[0], mb->name) == 0){
8427dd7cddfSDavid du Colombier 			qlock(mb);
8437dd7cddfSDavid du Colombier 			break;
8447dd7cddfSDavid du Colombier 		}
8457dd7cddfSDavid du Colombier 	qunlock(&mbllock);
8467dd7cddfSDavid du Colombier 	if(mb == nil)
8477dd7cddfSDavid du Colombier 		return;
8487dd7cddfSDavid du Colombier 
8497dd7cddfSDavid du Colombier 	needwrite = 0;
8507dd7cddfSDavid du Colombier 	for(i = 1; i < ac; i++){
8517dd7cddfSDavid du Colombier 		for(m = mb->root->part; m != nil; m = m->next)
8527dd7cddfSDavid du Colombier 			if(strcmp(m->name, av[i]) == 0){
8537dd7cddfSDavid du Colombier 				if(!m->deleted){
8547dd7cddfSDavid du Colombier 					mailplumb(mb, m, 1);
8557dd7cddfSDavid du Colombier 					needwrite = 1;
8567dd7cddfSDavid du Colombier 					m->deleted = 1;
85759cc4ca5SDavid du Colombier 					logmsg("deleting", m);
8587dd7cddfSDavid du Colombier 				}
8597dd7cddfSDavid du Colombier 				break;
8607dd7cddfSDavid du Colombier 			}
8617dd7cddfSDavid du Colombier 	}
8627dd7cddfSDavid du Colombier 	if(needwrite)
8637dd7cddfSDavid du Colombier 		syncmbox(mb, 1);
8647dd7cddfSDavid du Colombier 	qunlock(mb);
8657dd7cddfSDavid du Colombier }
8667dd7cddfSDavid du Colombier 
8677dd7cddfSDavid du Colombier /*
8687dd7cddfSDavid du Colombier  *  the following are called with the mailbox qlocked
8697dd7cddfSDavid du Colombier  */
8707dd7cddfSDavid du Colombier void
8717dd7cddfSDavid du Colombier msgincref(Message *m)
8727dd7cddfSDavid du Colombier {
8737dd7cddfSDavid du Colombier 	m->refs++;
8747dd7cddfSDavid du Colombier }
8757dd7cddfSDavid du Colombier void
8767dd7cddfSDavid du Colombier msgdecref(Mailbox *mb, Message *m)
8777dd7cddfSDavid du Colombier {
8787dd7cddfSDavid du Colombier 	m->refs--;
8797dd7cddfSDavid du Colombier 	if(m->refs == 0 && m->deleted)
8807dd7cddfSDavid du Colombier 		syncmbox(mb, 1);
8817dd7cddfSDavid du Colombier }
8827dd7cddfSDavid du Colombier 
8837dd7cddfSDavid du Colombier /*
8847dd7cddfSDavid du Colombier  *  the following are called with mbllock'd
8857dd7cddfSDavid du Colombier  */
8867dd7cddfSDavid du Colombier void
8877dd7cddfSDavid du Colombier mboxincref(Mailbox *mb)
8887dd7cddfSDavid du Colombier {
8899a747e4fSDavid du Colombier 	assert(mb->refs > 0);
8907dd7cddfSDavid du Colombier 	mb->refs++;
8917dd7cddfSDavid du Colombier }
8927dd7cddfSDavid du Colombier void
8937dd7cddfSDavid du Colombier mboxdecref(Mailbox *mb)
8947dd7cddfSDavid du Colombier {
8959a747e4fSDavid du Colombier 	assert(mb->refs > 0);
8967dd7cddfSDavid du Colombier 	qlock(mb);
8977dd7cddfSDavid du Colombier 	mb->refs--;
8987dd7cddfSDavid du Colombier 	if(mb->refs == 0){
8997dd7cddfSDavid du Colombier 		delmessage(mb, mb->root);
90080ee5cbfSDavid du Colombier 		if(mb->ctl)
9019a747e4fSDavid du Colombier 			hfree(PATH(mb->id, Qmbox), "ctl");
90280ee5cbfSDavid du Colombier 		if(mb->close)
90380ee5cbfSDavid du Colombier 			(*mb->close)(mb);
9047dd7cddfSDavid du Colombier 		free(mb);
9057dd7cddfSDavid du Colombier 	} else
9067dd7cddfSDavid du Colombier 		qunlock(mb);
9077dd7cddfSDavid du Colombier }
9087dd7cddfSDavid du Colombier 
9097dd7cddfSDavid du Colombier int
9107dd7cddfSDavid du Colombier cistrncmp(char *a, char *b, int n)
9117dd7cddfSDavid du Colombier {
9127dd7cddfSDavid du Colombier 	while(n-- > 0){
9137dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
9147dd7cddfSDavid du Colombier 			return -1;
9157dd7cddfSDavid du Colombier 	}
9167dd7cddfSDavid du Colombier 	return 0;
9177dd7cddfSDavid du Colombier }
9187dd7cddfSDavid du Colombier 
9197dd7cddfSDavid du Colombier int
9207dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
9217dd7cddfSDavid du Colombier {
9227dd7cddfSDavid du Colombier 	for(;;){
9237dd7cddfSDavid du Colombier 		if(tolower(*a) != tolower(*b++))
9247dd7cddfSDavid du Colombier 			return -1;
9257dd7cddfSDavid du Colombier 		if(*a++ == 0)
9267dd7cddfSDavid du Colombier 			break;
9277dd7cddfSDavid du Colombier 	}
9287dd7cddfSDavid du Colombier 	return 0;
9297dd7cddfSDavid du Colombier }
9307dd7cddfSDavid du Colombier 
9317dd7cddfSDavid du Colombier static char*
9327dd7cddfSDavid du Colombier skipwhite(char *p)
9337dd7cddfSDavid du Colombier {
9347dd7cddfSDavid du Colombier 	while(isspace(*p))
9357dd7cddfSDavid du Colombier 		p++;
9367dd7cddfSDavid du Colombier 	return p;
9377dd7cddfSDavid du Colombier }
9387dd7cddfSDavid du Colombier 
9397dd7cddfSDavid du Colombier static char*
9407dd7cddfSDavid du Colombier skiptosemi(char *p)
9417dd7cddfSDavid du Colombier {
9427dd7cddfSDavid du Colombier 	while(*p && *p != ';')
9437dd7cddfSDavid du Colombier 		p++;
9447dd7cddfSDavid du Colombier 	while(*p == ';' || isspace(*p))
9457dd7cddfSDavid du Colombier 		p++;
9467dd7cddfSDavid du Colombier 	return p;
9477dd7cddfSDavid du Colombier }
9487dd7cddfSDavid du Colombier 
9497dd7cddfSDavid du Colombier static char*
9507dd7cddfSDavid du Colombier getstring(char *p, String *s, int dolower)
9517dd7cddfSDavid du Colombier {
9527dd7cddfSDavid du Colombier 	s = s_reset(s);
9537dd7cddfSDavid du Colombier 	p = skipwhite(p);
9547dd7cddfSDavid du Colombier 	if(*p == '"'){
9557dd7cddfSDavid du Colombier 		p++;
9567dd7cddfSDavid du Colombier 		for(;*p && *p != '"'; p++)
9577dd7cddfSDavid du Colombier 			if(dolower)
9587dd7cddfSDavid du Colombier 				s_putc(s, tolower(*p));
9597dd7cddfSDavid du Colombier 			else
9607dd7cddfSDavid du Colombier 				s_putc(s, *p);
9617dd7cddfSDavid du Colombier 		if(*p == '"')
9627dd7cddfSDavid du Colombier 			p++;
9637dd7cddfSDavid du Colombier 		s_terminate(s);
9647dd7cddfSDavid du Colombier 
9657dd7cddfSDavid du Colombier 		return p;
9667dd7cddfSDavid du Colombier 	}
9677dd7cddfSDavid du Colombier 
9689a747e4fSDavid du Colombier 	for(; *p && !isspace(*p) && *p != ';'; p++)
9697dd7cddfSDavid du Colombier 		if(dolower)
9707dd7cddfSDavid du Colombier 			s_putc(s, tolower(*p));
9717dd7cddfSDavid du Colombier 		else
9727dd7cddfSDavid du Colombier 			s_putc(s, *p);
9737dd7cddfSDavid du Colombier 	s_terminate(s);
9747dd7cddfSDavid du Colombier 
9757dd7cddfSDavid du Colombier 	return p;
9767dd7cddfSDavid du Colombier }
9777dd7cddfSDavid du Colombier 
9787dd7cddfSDavid du Colombier static void
9797dd7cddfSDavid du Colombier setfilename(Message *m, char *p)
9807dd7cddfSDavid du Colombier {
9817dd7cddfSDavid du Colombier 	m->filename = s_reset(m->filename);
9827dd7cddfSDavid du Colombier 	getstring(p, m->filename, 0);
9837dd7cddfSDavid du Colombier 	for(p = s_to_c(m->filename); *p; p++)
9847dd7cddfSDavid du Colombier 		if(*p == ' ' || *p == '\t' || *p == ';')
9857dd7cddfSDavid du Colombier 			*p = '_';
9867dd7cddfSDavid du Colombier }
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier //
9897dd7cddfSDavid du Colombier // undecode message body
9907dd7cddfSDavid du Colombier //
9917dd7cddfSDavid du Colombier void
9927dd7cddfSDavid du Colombier decode(Message *m)
9937dd7cddfSDavid du Colombier {
9947dd7cddfSDavid du Colombier 	int i, len;
9957dd7cddfSDavid du Colombier 	char *x;
9967dd7cddfSDavid du Colombier 
9977dd7cddfSDavid du Colombier 	if(m->decoded)
9987dd7cddfSDavid du Colombier 		return;
9997dd7cddfSDavid du Colombier 	switch(m->encoding){
10007dd7cddfSDavid du Colombier 	case Ebase64:
10017dd7cddfSDavid du Colombier 		len = m->bend - m->body;
10027dd7cddfSDavid du Colombier 		i = (len*3)/4+1;	// room for max chars + null
10037dd7cddfSDavid du Colombier 		x = emalloc(i);
10047dd7cddfSDavid du Colombier 		len = dec64((uchar*)x, i, m->body, len);
10057dd7cddfSDavid du Colombier 		if(m->ballocd)
10067dd7cddfSDavid du Colombier 			free(m->body);
10077dd7cddfSDavid du Colombier 		m->body = x;
10087dd7cddfSDavid du Colombier 		m->bend = x + len;
10097dd7cddfSDavid du Colombier 		m->ballocd = 1;
10107dd7cddfSDavid du Colombier 		break;
10117dd7cddfSDavid du Colombier 	case Equoted:
10127dd7cddfSDavid du Colombier 		len = m->bend - m->body;
10137dd7cddfSDavid du Colombier 		x = emalloc(len+2);	// room for null and possible extra nl
10147dd7cddfSDavid du Colombier 		len = decquoted(x, m->body, m->bend);
10157dd7cddfSDavid du Colombier 		if(m->ballocd)
10167dd7cddfSDavid du Colombier 			free(m->body);
10177dd7cddfSDavid du Colombier 		m->body = x;
10187dd7cddfSDavid du Colombier 		m->bend = x + len;
10197dd7cddfSDavid du Colombier 		m->ballocd = 1;
10207dd7cddfSDavid du Colombier 		break;
10217dd7cddfSDavid du Colombier 	default:
10227dd7cddfSDavid du Colombier 		break;
10237dd7cddfSDavid du Colombier 	}
10247dd7cddfSDavid du Colombier 	m->decoded = 1;
10257dd7cddfSDavid du Colombier }
10267dd7cddfSDavid du Colombier 
10277dd7cddfSDavid du Colombier // convert latin1 to utf
10287dd7cddfSDavid du Colombier void
10297dd7cddfSDavid du Colombier convert(Message *m)
10307dd7cddfSDavid du Colombier {
10317dd7cddfSDavid du Colombier 	int len;
10327dd7cddfSDavid du Colombier 	char *x;
10337dd7cddfSDavid du Colombier 
10347dd7cddfSDavid du Colombier 	// don't convert if we're not a leaf, not text, or already converted
10357dd7cddfSDavid du Colombier 	if(m->converted)
10367dd7cddfSDavid du Colombier 		return;
10377dd7cddfSDavid du Colombier 	if(m->part != nil)
10387dd7cddfSDavid du Colombier 		return;
10397dd7cddfSDavid du Colombier 	if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
10407dd7cddfSDavid du Colombier 		return;
10417dd7cddfSDavid du Colombier 
10427dd7cddfSDavid du Colombier 	if(cistrcmp(s_to_c(m->charset), "us-ascii") == 0 ||
10437dd7cddfSDavid du Colombier 	   cistrcmp(s_to_c(m->charset), "iso-8859-1") == 0){
10447dd7cddfSDavid du Colombier 		len = is8bit(m);
10457dd7cddfSDavid du Colombier 		if(len > 0){
10467dd7cddfSDavid du Colombier 			len = 2*len + m->bend - m->body + 1;
10477dd7cddfSDavid du Colombier 			x = emalloc(len);
10487dd7cddfSDavid du Colombier 			len = latin1toutf(x, m->body, m->bend);
10497dd7cddfSDavid du Colombier 			if(m->ballocd)
10507dd7cddfSDavid du Colombier 				free(m->body);
10517dd7cddfSDavid du Colombier 			m->body = x;
10527dd7cddfSDavid du Colombier 			m->bend = x + len;
10537dd7cddfSDavid du Colombier 			m->ballocd = 1;
10547dd7cddfSDavid du Colombier 		}
10557dd7cddfSDavid du Colombier 	} else if(cistrcmp(s_to_c(m->charset), "big5") == 0){
10567dd7cddfSDavid du Colombier 		len = xtoutf("big5", &x, m->body, m->bend);
10577dd7cddfSDavid du Colombier 		if(len != 0){
10587dd7cddfSDavid du Colombier 			if(m->ballocd)
10597dd7cddfSDavid du Colombier 				free(m->body);
10607dd7cddfSDavid du Colombier 			m->body = x;
10617dd7cddfSDavid du Colombier 			m->bend = x + len;
10627dd7cddfSDavid du Colombier 			m->ballocd = 1;
10637dd7cddfSDavid du Colombier 		}
1064*5d459b5aSDavid du Colombier 	} else if(cistrcmp(s_to_c(m->charset), "windows-1257") == 0
1065*5d459b5aSDavid du Colombier 			|| cistrcmp(s_to_c(m->charset), "windows-1252") == 0){
10667dd7cddfSDavid du Colombier 		len = is8bit(m);
10677dd7cddfSDavid du Colombier 		if(len > 0){
10687dd7cddfSDavid du Colombier 			len = 2*len + m->bend - m->body + 1;
10697dd7cddfSDavid du Colombier 			x = emalloc(len);
10707dd7cddfSDavid du Colombier 			len = windows1257toutf(x, m->body, m->bend);
10717dd7cddfSDavid du Colombier 			if(m->ballocd)
10727dd7cddfSDavid du Colombier 				free(m->body);
10737dd7cddfSDavid du Colombier 			m->body = x;
10747dd7cddfSDavid du Colombier 			m->bend = x + len;
10757dd7cddfSDavid du Colombier 			m->ballocd = 1;
10767dd7cddfSDavid du Colombier 		}
10777dd7cddfSDavid du Colombier 	}
10787dd7cddfSDavid du Colombier 
10797dd7cddfSDavid du Colombier 	m->converted = 1;
10807dd7cddfSDavid du Colombier }
10817dd7cddfSDavid du Colombier 
10827dd7cddfSDavid du Colombier enum
10837dd7cddfSDavid du Colombier {
10847dd7cddfSDavid du Colombier 	Self=	1,
10857dd7cddfSDavid du Colombier 	Hex=	2,
10867dd7cddfSDavid du Colombier };
10877dd7cddfSDavid du Colombier uchar	tableqp[256];
10887dd7cddfSDavid du Colombier 
10897dd7cddfSDavid du Colombier static void
10907dd7cddfSDavid du Colombier initquoted(void)
10917dd7cddfSDavid du Colombier {
10927dd7cddfSDavid du Colombier 	int c;
10937dd7cddfSDavid du Colombier 
10947dd7cddfSDavid du Colombier 	memset(tableqp, 0, 256);
10957dd7cddfSDavid du Colombier 	for(c = ' '; c <= '<'; c++)
10967dd7cddfSDavid du Colombier 		tableqp[c] = Self;
10977dd7cddfSDavid du Colombier 	for(c = '>'; c <= '~'; c++)
10987dd7cddfSDavid du Colombier 		tableqp[c] = Self;
10997dd7cddfSDavid du Colombier 	tableqp['\t'] = Self;
11007dd7cddfSDavid du Colombier 	tableqp['='] = Hex;
11017dd7cddfSDavid du Colombier }
11027dd7cddfSDavid du Colombier 
11037dd7cddfSDavid du Colombier static int
11047dd7cddfSDavid du Colombier hex2int(int x)
11057dd7cddfSDavid du Colombier {
11067dd7cddfSDavid du Colombier 	if(x >= '0' && x <= '9')
11077dd7cddfSDavid du Colombier 		return x - '0';
11087dd7cddfSDavid du Colombier 	if(x >= 'A' && x <= 'F')
11097dd7cddfSDavid du Colombier 		return (x - 'A') + 10;
11107dd7cddfSDavid du Colombier 	if(x >= 'a' && x <= 'f')
11117dd7cddfSDavid du Colombier 		return (x - 'a') + 10;
11127dd7cddfSDavid du Colombier 	return 0;
11137dd7cddfSDavid du Colombier }
11147dd7cddfSDavid du Colombier 
11157dd7cddfSDavid du Colombier static char*
11167dd7cddfSDavid du Colombier decquotedline(char *out, char *in, char *e)
11177dd7cddfSDavid du Colombier {
11187dd7cddfSDavid du Colombier 	int c, soft;
11197dd7cddfSDavid du Colombier 
11207dd7cddfSDavid du Colombier 	/* dump trailing white space */
11217dd7cddfSDavid du Colombier 	while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
11227dd7cddfSDavid du Colombier 		e--;
11237dd7cddfSDavid du Colombier 
11247dd7cddfSDavid du Colombier 	/* trailing '=' means no newline */
11257dd7cddfSDavid du Colombier 	if(*e == '='){
11267dd7cddfSDavid du Colombier 		soft = 1;
11277dd7cddfSDavid du Colombier 		e--;
11287dd7cddfSDavid du Colombier 	} else
11297dd7cddfSDavid du Colombier 		soft = 0;
11307dd7cddfSDavid du Colombier 
11317dd7cddfSDavid du Colombier 	while(in <= e){
11327dd7cddfSDavid du Colombier 		c = (*in++) & 0xff;
11337dd7cddfSDavid du Colombier 		switch(tableqp[c]){
11347dd7cddfSDavid du Colombier 		case Self:
11357dd7cddfSDavid du Colombier 			*out++ = c;
11367dd7cddfSDavid du Colombier 			break;
11377dd7cddfSDavid du Colombier 		case Hex:
11387dd7cddfSDavid du Colombier 			c = hex2int(*in++)<<4;
11397dd7cddfSDavid du Colombier 			c |= hex2int(*in++);
11407dd7cddfSDavid du Colombier 			*out++ = c;
11417dd7cddfSDavid du Colombier 			break;
11427dd7cddfSDavid du Colombier 		}
11437dd7cddfSDavid du Colombier 	}
11447dd7cddfSDavid du Colombier 	if(!soft)
11457dd7cddfSDavid du Colombier 		*out++ = '\n';
11467dd7cddfSDavid du Colombier 	*out = 0;
11477dd7cddfSDavid du Colombier 
11487dd7cddfSDavid du Colombier 	return out;
11497dd7cddfSDavid du Colombier }
11507dd7cddfSDavid du Colombier 
11517dd7cddfSDavid du Colombier int
11527dd7cddfSDavid du Colombier decquoted(char *out, char *in, char *e)
11537dd7cddfSDavid du Colombier {
11547dd7cddfSDavid du Colombier 	char *p, *nl;
11557dd7cddfSDavid du Colombier 
11567dd7cddfSDavid du Colombier 	if(tableqp[' '] == 0)
11577dd7cddfSDavid du Colombier 		initquoted();
11587dd7cddfSDavid du Colombier 
11597dd7cddfSDavid du Colombier 	p = out;
11607dd7cddfSDavid du Colombier 	while((nl = strchr(in, '\n')) != nil && nl < e){
11617dd7cddfSDavid du Colombier 		p = decquotedline(p, in, nl);
11627dd7cddfSDavid du Colombier 		in = nl + 1;
11637dd7cddfSDavid du Colombier 	}
11647dd7cddfSDavid du Colombier 	if(in < e)
11657dd7cddfSDavid du Colombier 		p = decquotedline(p, in, e-1);
11667dd7cddfSDavid du Colombier 
11677dd7cddfSDavid du Colombier 	// make sure we end with a new line
11687dd7cddfSDavid du Colombier 	if(*(p-1) != '\n'){
11697dd7cddfSDavid du Colombier 		*p++ = '\n';
11707dd7cddfSDavid du Colombier 		*p = 0;
11717dd7cddfSDavid du Colombier 	}
11727dd7cddfSDavid du Colombier 
11737dd7cddfSDavid du Colombier 	return p - out;
11747dd7cddfSDavid du Colombier }
11757dd7cddfSDavid du Colombier 
11767dd7cddfSDavid du Colombier static char*
11777dd7cddfSDavid du Colombier lowercase(char *p)
11787dd7cddfSDavid du Colombier {
11797dd7cddfSDavid du Colombier 	char *op;
11807dd7cddfSDavid du Colombier 	int c;
11817dd7cddfSDavid du Colombier 
11827dd7cddfSDavid du Colombier 	for(op = p; c = *p; p++)
11837dd7cddfSDavid du Colombier 		if(isupper(c))
11847dd7cddfSDavid du Colombier 			*p = tolower(c);
11857dd7cddfSDavid du Colombier 	return op;
11867dd7cddfSDavid du Colombier }
11877dd7cddfSDavid du Colombier 
11887dd7cddfSDavid du Colombier /*
11897dd7cddfSDavid du Colombier  *  return number of 8 bit characters
11907dd7cddfSDavid du Colombier  */
11917dd7cddfSDavid du Colombier static int
11927dd7cddfSDavid du Colombier is8bit(Message *m)
11937dd7cddfSDavid du Colombier {
11947dd7cddfSDavid du Colombier 	int count = 0;
11957dd7cddfSDavid du Colombier 	char *p;
11967dd7cddfSDavid du Colombier 
11977dd7cddfSDavid du Colombier 	for(p = m->body; p < m->bend; p++)
11987dd7cddfSDavid du Colombier 		if(*p & 0x80)
11997dd7cddfSDavid du Colombier 			count++;
12007dd7cddfSDavid du Colombier 	return count;
12017dd7cddfSDavid du Colombier }
12027dd7cddfSDavid du Colombier 
12037dd7cddfSDavid du Colombier // translate latin1 directly since it fits neatly in utf
12047dd7cddfSDavid du Colombier int
12057dd7cddfSDavid du Colombier latin1toutf(char *out, char *in, char *e)
12067dd7cddfSDavid du Colombier {
12077dd7cddfSDavid du Colombier 	Rune r;
12087dd7cddfSDavid du Colombier 	char *p;
12097dd7cddfSDavid du Colombier 
12107dd7cddfSDavid du Colombier 	p = out;
12117dd7cddfSDavid du Colombier 	for(; in < e; in++){
12127dd7cddfSDavid du Colombier 		r = (*in) & 0xff;
12137dd7cddfSDavid du Colombier 		p += runetochar(p, &r);
12147dd7cddfSDavid du Colombier 	}
12157dd7cddfSDavid du Colombier 	*p = 0;
12167dd7cddfSDavid du Colombier 	return p - out;
12177dd7cddfSDavid du Colombier }
12187dd7cddfSDavid du Colombier 
12197dd7cddfSDavid du Colombier // translate any thing else using the tcs program
12207dd7cddfSDavid du Colombier int
12217dd7cddfSDavid du Colombier xtoutf(char *charset, char **out, char *in, char *e)
12227dd7cddfSDavid du Colombier {
12237dd7cddfSDavid du Colombier 	char *av[4];
12247dd7cddfSDavid du Colombier 	int totcs[2];
12257dd7cddfSDavid du Colombier 	int fromtcs[2];
12267dd7cddfSDavid du Colombier 	int n, len, sofar;
12277dd7cddfSDavid du Colombier 	char *p;
12287dd7cddfSDavid du Colombier 
12297dd7cddfSDavid du Colombier 	len = e-in+1;
12307dd7cddfSDavid du Colombier 	sofar = 0;
12317dd7cddfSDavid du Colombier 	*out = p = malloc(len+1);
12327dd7cddfSDavid du Colombier 	if(p == nil)
12337dd7cddfSDavid du Colombier 		return 0;
12347dd7cddfSDavid du Colombier 
12357dd7cddfSDavid du Colombier 	av[0] = charset;
12367dd7cddfSDavid du Colombier 	av[1] = "-f";
12377dd7cddfSDavid du Colombier 	av[2] = charset;
12387dd7cddfSDavid du Colombier 	av[3] = 0;
12397dd7cddfSDavid du Colombier 	if(pipe(totcs) < 0)
12407dd7cddfSDavid du Colombier 		return 0;
12417dd7cddfSDavid du Colombier 	if(pipe(fromtcs) < 0){
12427dd7cddfSDavid du Colombier 		close(totcs[0]); close(totcs[1]);
12437dd7cddfSDavid du Colombier 		return 0;
12447dd7cddfSDavid du Colombier 	}
12457dd7cddfSDavid du Colombier 	switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
12467dd7cddfSDavid du Colombier 	case -1:
12477dd7cddfSDavid du Colombier 		close(fromtcs[0]); close(fromtcs[1]);
12487dd7cddfSDavid du Colombier 		close(totcs[0]); close(totcs[1]);
12497dd7cddfSDavid du Colombier 		return 0;
12507dd7cddfSDavid du Colombier 	case 0:
12517dd7cddfSDavid du Colombier 		close(fromtcs[0]); close(totcs[1]);
12527dd7cddfSDavid du Colombier 		dup(fromtcs[1], 1);
12537dd7cddfSDavid du Colombier 		dup(totcs[0], 0);
12547dd7cddfSDavid du Colombier 		close(fromtcs[1]); close(totcs[0]);
12559a747e4fSDavid du Colombier 		dup(open("/dev/null", OWRITE), 2);
12567dd7cddfSDavid du Colombier 		exec("/bin/tcs", av);
12577dd7cddfSDavid du Colombier 		_exits(0);
12587dd7cddfSDavid du Colombier 	default:
12597dd7cddfSDavid du Colombier 		close(fromtcs[1]); close(totcs[0]);
12607dd7cddfSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
12617dd7cddfSDavid du Colombier 		case -1:
12627dd7cddfSDavid du Colombier 			close(fromtcs[0]); close(totcs[1]);
12637dd7cddfSDavid du Colombier 			return 0;
12647dd7cddfSDavid du Colombier 		case 0:
12657dd7cddfSDavid du Colombier 			close(fromtcs[0]);
12667dd7cddfSDavid du Colombier 			while(in < e){
12677dd7cddfSDavid du Colombier 				n = write(totcs[1], in, e-in);
12687dd7cddfSDavid du Colombier 				if(n <= 0)
12697dd7cddfSDavid du Colombier 					break;
12707dd7cddfSDavid du Colombier 				in += n;
12717dd7cddfSDavid du Colombier 			}
12727dd7cddfSDavid du Colombier 			close(totcs[1]);
12737dd7cddfSDavid du Colombier 			_exits(0);
12747dd7cddfSDavid du Colombier 		default:
12757dd7cddfSDavid du Colombier 			close(totcs[1]);
12767dd7cddfSDavid du Colombier 			for(;;){
12777dd7cddfSDavid du Colombier 				n = read(fromtcs[0], &p[sofar], len-sofar);
12787dd7cddfSDavid du Colombier 				if(n <= 0)
12797dd7cddfSDavid du Colombier 					break;
12807dd7cddfSDavid du Colombier 				sofar += n;
12817dd7cddfSDavid du Colombier 				p[sofar] = 0;
12827dd7cddfSDavid du Colombier 				if(sofar == len){
12837dd7cddfSDavid du Colombier 					len += 1024;
12847dd7cddfSDavid du Colombier 					*out = p = realloc(p, len+1);
12857dd7cddfSDavid du Colombier 					if(p == nil)
12867dd7cddfSDavid du Colombier 						return 0;
12877dd7cddfSDavid du Colombier 				}
12887dd7cddfSDavid du Colombier 			}
12897dd7cddfSDavid du Colombier 			close(fromtcs[0]);
12907dd7cddfSDavid du Colombier 			break;
12917dd7cddfSDavid du Colombier 		}
12927dd7cddfSDavid du Colombier 		break;
12937dd7cddfSDavid du Colombier 	}
12947dd7cddfSDavid du Colombier 	return sofar;
12957dd7cddfSDavid du Colombier }
12967dd7cddfSDavid du Colombier 
12977dd7cddfSDavid du Colombier enum {
12987dd7cddfSDavid du Colombier 	Winstart= 0x7f,
12997dd7cddfSDavid du Colombier 	Winend= 0x9f,
13007dd7cddfSDavid du Colombier };
13017dd7cddfSDavid du Colombier 
13027dd7cddfSDavid du Colombier Rune winchars[] = {
13037dd7cddfSDavid du Colombier 	L'•',
13047dd7cddfSDavid du Colombier 	L'•', L'•', L'‚', L'ƒ', L'„', L'…', L'†', L'‡',
13057dd7cddfSDavid du Colombier 	L'ˆ', L'‰', L'Š', L'‹', L'Œ', L'•', L'•', L'•',
13067dd7cddfSDavid du Colombier 	L'•', L'‘', L'’', L'“', L'”', L'•', L'–', L'—',
13077dd7cddfSDavid du Colombier 	L'˜', L'™', L'š', L'›', L'œ', L'•', L'•', L'Ÿ',
13087dd7cddfSDavid du Colombier };
13097dd7cddfSDavid du Colombier 
13107dd7cddfSDavid du Colombier int
13117dd7cddfSDavid du Colombier windows1257toutf(char *out, char *in, char *e)
13127dd7cddfSDavid du Colombier {
13137dd7cddfSDavid du Colombier 	Rune r;
13147dd7cddfSDavid du Colombier 	char *p;
13157dd7cddfSDavid du Colombier 
13167dd7cddfSDavid du Colombier 	p = out;
13177dd7cddfSDavid du Colombier 	for(; in < e; in++){
13187dd7cddfSDavid du Colombier 		r = (*in) & 0xff;
13197dd7cddfSDavid du Colombier 		if(r >= 0x7f && r <= 0x9f)
13207dd7cddfSDavid du Colombier 			r = winchars[r-0x7f];
13217dd7cddfSDavid du Colombier 		p += runetochar(p, &r);
13227dd7cddfSDavid du Colombier 	}
13237dd7cddfSDavid du Colombier 	*p = 0;
13247dd7cddfSDavid du Colombier 	return p - out;
13257dd7cddfSDavid du Colombier }
13267dd7cddfSDavid du Colombier 
13277dd7cddfSDavid du Colombier void *
13287dd7cddfSDavid du Colombier emalloc(ulong n)
13297dd7cddfSDavid du Colombier {
13307dd7cddfSDavid du Colombier 	void *p;
13317dd7cddfSDavid du Colombier 
13327dd7cddfSDavid du Colombier 	p = mallocz(n, 1);
13337dd7cddfSDavid du Colombier 	if(!p){
133480ee5cbfSDavid du Colombier 		fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
13357dd7cddfSDavid du Colombier 		exits("out of memory");
13367dd7cddfSDavid du Colombier 	}
133780ee5cbfSDavid du Colombier 	setmalloctag(p, getcallerpc(&n));
13387dd7cddfSDavid du Colombier 	return p;
13397dd7cddfSDavid du Colombier }
13407dd7cddfSDavid du Colombier 
13417dd7cddfSDavid du Colombier void *
13427dd7cddfSDavid du Colombier erealloc(void *p, ulong n)
13437dd7cddfSDavid du Colombier {
13447dd7cddfSDavid du Colombier 	p = realloc(p, n);
13457dd7cddfSDavid du Colombier 	if(!p){
134680ee5cbfSDavid du Colombier 		fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
13477dd7cddfSDavid du Colombier 		exits("out of memory");
13487dd7cddfSDavid du Colombier 	}
134980ee5cbfSDavid du Colombier 	setrealloctag(p, getcallerpc(&p));
13507dd7cddfSDavid du Colombier 	return p;
13517dd7cddfSDavid du Colombier }
13527dd7cddfSDavid du Colombier 
13537dd7cddfSDavid du Colombier void
13547dd7cddfSDavid du Colombier mailplumb(Mailbox *mb, Message *m, int delete)
13557dd7cddfSDavid du Colombier {
13567dd7cddfSDavid du Colombier 	Plumbmsg p;
13577dd7cddfSDavid du Colombier 	Plumbattr a[7];
13587dd7cddfSDavid du Colombier 	char buf[256];
13597dd7cddfSDavid du Colombier 	int ai;
13607dd7cddfSDavid du Colombier 	char lenstr[10], *from, *subject, *date;
13617dd7cddfSDavid du Colombier 	static int fd = -1;
13627dd7cddfSDavid du Colombier 
13637dd7cddfSDavid du Colombier 	if(m->subject822 == nil)
13647dd7cddfSDavid du Colombier 		subject = "";
13657dd7cddfSDavid du Colombier 	else
13667dd7cddfSDavid du Colombier 		subject = s_to_c(m->subject822);
13677dd7cddfSDavid du Colombier 
13687dd7cddfSDavid du Colombier 	if(m->from822 != nil)
13697dd7cddfSDavid du Colombier 		from = s_to_c(m->from822);
13707dd7cddfSDavid du Colombier 	else if(m->unixfrom != nil)
13717dd7cddfSDavid du Colombier 		from = s_to_c(m->unixfrom);
13727dd7cddfSDavid du Colombier 	else
13737dd7cddfSDavid du Colombier 		from = "";
13747dd7cddfSDavid du Colombier 
13757dd7cddfSDavid du Colombier 	if(m->unixdate != nil)
13767dd7cddfSDavid du Colombier 		date = s_to_c(m->unixdate);
13777dd7cddfSDavid du Colombier 	else
13787dd7cddfSDavid du Colombier 		date = "";
13797dd7cddfSDavid du Colombier 
13807dd7cddfSDavid du Colombier 	sprint(lenstr, "%ld", m->end-m->start);
13817dd7cddfSDavid du Colombier 
13827dd7cddfSDavid du Colombier 	if(biffing && !delete)
13837dd7cddfSDavid du Colombier 		print("[ %s / %s / %s ]\n", from, subject, lenstr);
13847dd7cddfSDavid du Colombier 
13857dd7cddfSDavid du Colombier 	if(!plumbing)
13867dd7cddfSDavid du Colombier 		return;
13877dd7cddfSDavid du Colombier 
13887dd7cddfSDavid du Colombier 	if(fd < 0)
13897dd7cddfSDavid du Colombier 		fd = plumbopen("send", OWRITE);
13907dd7cddfSDavid du Colombier 	if(fd < 0)
13917dd7cddfSDavid du Colombier 		return;
13927dd7cddfSDavid du Colombier 
13937dd7cddfSDavid du Colombier 	p.src = "mailfs";
13947dd7cddfSDavid du Colombier 	p.dst = "seemail";
13957dd7cddfSDavid du Colombier 	p.wdir = "/mail/fs";
13967dd7cddfSDavid du Colombier 	p.type = "text";
13977dd7cddfSDavid du Colombier 
13987dd7cddfSDavid du Colombier 	ai = 0;
13997dd7cddfSDavid du Colombier 	a[ai].name = "filetype";
14007dd7cddfSDavid du Colombier 	a[ai].value = "mail";
14017dd7cddfSDavid du Colombier 
14027dd7cddfSDavid du Colombier 	a[++ai].name = "sender";
14037dd7cddfSDavid du Colombier 	a[ai].value = from;
14047dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
14057dd7cddfSDavid du Colombier 
14067dd7cddfSDavid du Colombier 	a[++ai].name = "length";
14077dd7cddfSDavid du Colombier 	a[ai].value = lenstr;
14087dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
14097dd7cddfSDavid du Colombier 
14107dd7cddfSDavid du Colombier 	a[++ai].name = "mailtype";
14117dd7cddfSDavid du Colombier 	a[ai].value = delete?"delete":"new";
14127dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
14137dd7cddfSDavid du Colombier 
14147dd7cddfSDavid du Colombier 	a[++ai].name = "date";
14157dd7cddfSDavid du Colombier 	a[ai].value = date;
14167dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
14177dd7cddfSDavid du Colombier 
14189a747e4fSDavid du Colombier 	if(m->sdigest){
14197dd7cddfSDavid du Colombier 		a[++ai].name = "digest";
14207dd7cddfSDavid du Colombier 		a[ai].value = s_to_c(m->sdigest);
14217dd7cddfSDavid du Colombier 		a[ai-1].next = &a[ai];
14229a747e4fSDavid du Colombier 	}
14237dd7cddfSDavid du Colombier 
14247dd7cddfSDavid du Colombier 	a[ai].next = nil;
14257dd7cddfSDavid du Colombier 
14267dd7cddfSDavid du Colombier 	p.attr = a;
14277dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/%s/%s",
14287dd7cddfSDavid du Colombier 		mntpt, mb->name, m->name);
14297dd7cddfSDavid du Colombier 	p.ndata = strlen(buf);
14307dd7cddfSDavid du Colombier 	p.data = buf;
14317dd7cddfSDavid du Colombier 
14327dd7cddfSDavid du Colombier 	plumbsend(fd, &p);
14337dd7cddfSDavid du Colombier }
14347dd7cddfSDavid du Colombier 
14357dd7cddfSDavid du Colombier //
14367dd7cddfSDavid du Colombier // count the number of lines in the body (for imap4)
14377dd7cddfSDavid du Colombier //
14387dd7cddfSDavid du Colombier void
14397dd7cddfSDavid du Colombier countlines(Message *m)
14407dd7cddfSDavid du Colombier {
14417dd7cddfSDavid du Colombier 	int i;
14427dd7cddfSDavid du Colombier 	char *p;
14437dd7cddfSDavid du Colombier 
14447dd7cddfSDavid du Colombier 	i = 0;
14457dd7cddfSDavid du Colombier 	for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
14467dd7cddfSDavid du Colombier 		i++;
14477dd7cddfSDavid du Colombier 	sprint(m->lines, "%d", i);
14487dd7cddfSDavid du Colombier }
144959cc4ca5SDavid du Colombier 
145059cc4ca5SDavid du Colombier char *LOG = "fs";
145159cc4ca5SDavid du Colombier 
145259cc4ca5SDavid du Colombier void
145359cc4ca5SDavid du Colombier logmsg(char *s, Message *m)
145459cc4ca5SDavid du Colombier {
145559cc4ca5SDavid du Colombier 	int pid;
145659cc4ca5SDavid du Colombier 
145759cc4ca5SDavid du Colombier 	if(!logging)
145859cc4ca5SDavid du Colombier 		return;
145959cc4ca5SDavid du Colombier 	pid = getpid();
146059cc4ca5SDavid du Colombier 	if(m == nil)
146159cc4ca5SDavid du Colombier 		syslog(0, LOG, "%s.%d: %s", user, pid, s);
146259cc4ca5SDavid du Colombier 	else
146359cc4ca5SDavid du Colombier 		syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
146459cc4ca5SDavid du Colombier 			user, pid, s,
146559cc4ca5SDavid du Colombier 			m->from822 ? s_to_c(m->from822) : "?",
146659cc4ca5SDavid du Colombier 			s_to_c(m->sdigest));
146759cc4ca5SDavid du Colombier }
146880ee5cbfSDavid du Colombier 
146980ee5cbfSDavid du Colombier //
147080ee5cbfSDavid du Colombier // convert an RFC822 date into a Unix style date
147180ee5cbfSDavid du Colombier // for when the Unix From line isn't there (e.g. POP3).
147280ee5cbfSDavid du Colombier // enough client programs depend on having a Unix date
147380ee5cbfSDavid du Colombier // that it's easiest to write this conversion code once, right here.
147480ee5cbfSDavid du Colombier //
147580ee5cbfSDavid du Colombier // people don't follow RFC822 particularly closely,
147680ee5cbfSDavid du Colombier // so we use strtotm, which is a bunch of heuristics.
147780ee5cbfSDavid du Colombier //
147880ee5cbfSDavid du Colombier 
147980ee5cbfSDavid du Colombier extern int strtotm(char*, Tm*);
148080ee5cbfSDavid du Colombier String*
148180ee5cbfSDavid du Colombier date822tounix(char *s)
148280ee5cbfSDavid du Colombier {
148380ee5cbfSDavid du Colombier 	char *p, *q;
148480ee5cbfSDavid du Colombier 	Tm tm;
148580ee5cbfSDavid du Colombier 
148680ee5cbfSDavid du Colombier 	if(strtotm(s, &tm) < 0)
148780ee5cbfSDavid du Colombier 		return nil;
148880ee5cbfSDavid du Colombier 
148980ee5cbfSDavid du Colombier 	p = asctime(&tm);
149080ee5cbfSDavid du Colombier 	if(q = strchr(p, '\n'))
149180ee5cbfSDavid du Colombier 		*q = '\0';
149280ee5cbfSDavid du Colombier 	return s_copy(p);
149380ee5cbfSDavid du Colombier }
149480ee5cbfSDavid du Colombier 
1495