xref: /plan9/sys/src/cmd/upas/fs/mbox.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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;
71*9a747e4fSDavid 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
13380ee5cbfSDavid du Colombier 	if(rv)
13480ee5cbfSDavid du Colombier 		return rv;
13580ee5cbfSDavid du Colombier 
1367dd7cddfSDavid du Colombier 	// make sure name isn't taken
1377dd7cddfSDavid du Colombier 	qlock(&mbllock);
13859cc4ca5SDavid du Colombier 	for(l = &mbl; *l != nil; l = &(*l)->next){
1397dd7cddfSDavid du Colombier 		if(strcmp((*l)->name, mb->name) == 0){
14059cc4ca5SDavid du Colombier 			if(strcmp(path, (*l)->path) == 0)
1417dd7cddfSDavid du Colombier 				rv = nil;
1427dd7cddfSDavid du Colombier 			else
1437dd7cddfSDavid du Colombier 				rv = "mbox name in use";
14480ee5cbfSDavid du Colombier 			if(mb->close)
14580ee5cbfSDavid du Colombier 				(*mb->close)(mb);
1467dd7cddfSDavid du Colombier 			free(mb);
1477dd7cddfSDavid du Colombier 			qunlock(&mbllock);
1487dd7cddfSDavid du Colombier 			return rv;
1497dd7cddfSDavid du Colombier 		}
15059cc4ca5SDavid du Colombier 	}
15159cc4ca5SDavid du Colombier 
15259cc4ca5SDavid du Colombier 	// all mailboxes in /mail/box/$user are locked using /mail/box/$user/mbox
15359cc4ca5SDavid du Colombier 	p = strrchr(stdmbox, '/');
15459cc4ca5SDavid du Colombier 	mb->dolock = strncmp(mb->path, stdmbox, p - stdmbox) == 0;
15559cc4ca5SDavid du Colombier 
1567dd7cddfSDavid du Colombier 	mb->refs = 1;
1577dd7cddfSDavid du Colombier 	mb->next = nil;
1587dd7cddfSDavid du Colombier 	mb->id = newid();
1597dd7cddfSDavid du Colombier 	mb->root = newmessage(nil);
1607dd7cddfSDavid du Colombier 	mb->std = std;
1617dd7cddfSDavid du Colombier 	*l = mb;
1627dd7cddfSDavid du Colombier 	qunlock(&mbllock);
1637dd7cddfSDavid du Colombier 
1647dd7cddfSDavid du Colombier 	qlock(mb);
16580ee5cbfSDavid du Colombier 	if(mb->ctl){
166*9a747e4fSDavid du Colombier 		henter(PATH(mb->id, Qmbox), "ctl",
167*9a747e4fSDavid du Colombier 			(Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb);
16880ee5cbfSDavid du Colombier 	}
1697dd7cddfSDavid du Colombier 	rv = syncmbox(mb, 0);
1707dd7cddfSDavid du Colombier 	qunlock(mb);
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier 	return rv;
1737dd7cddfSDavid du Colombier }
1747dd7cddfSDavid du Colombier 
1757dd7cddfSDavid du Colombier // close the named mailbox
1767dd7cddfSDavid du Colombier void
1777dd7cddfSDavid du Colombier freembox(char *name)
1787dd7cddfSDavid du Colombier {
1797dd7cddfSDavid du Colombier 	Mailbox *mb;
1807dd7cddfSDavid du Colombier 
1817dd7cddfSDavid du Colombier 	qlock(&mbllock);
1827dd7cddfSDavid du Colombier 	for(mb = mbl; mb != nil; mb = mb->next)
1837dd7cddfSDavid du Colombier 		if(strcmp(name, mb->name) == 0){
1847dd7cddfSDavid du Colombier 			mboxdecref(mb);
1857dd7cddfSDavid du Colombier 			break;
1867dd7cddfSDavid du Colombier 		}
187*9a747e4fSDavid 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
23080ee5cbfSDavid du Colombier parseheaders(Message *m, int justmime, Mailbox *mb)
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){
238*9a747e4fSDavid du Colombier 		henter(PATH(mb->id, Qmbox), m->name,
239*9a747e4fSDavid du Colombier 			(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
2407dd7cddfSDavid du Colombier 	} else {
241*9a747e4fSDavid du Colombier 		henter(PATH(m->whole->id, Qdir), m->name,
242*9a747e4fSDavid du Colombier 			(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
2437dd7cddfSDavid du Colombier 	}
2447dd7cddfSDavid du Colombier 	for(i = 0; i < Qmax; i++)
245*9a747e4fSDavid du Colombier 		henter(PATH(m->id, Qdir), dirtab[i],
246*9a747e4fSDavid 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 
27780ee5cbfSDavid du Colombier 	//
27880ee5cbfSDavid du Colombier 	// cobble together Unix-style from line
27980ee5cbfSDavid du Colombier 	// for local mailbox messages, we end up recreating the
28080ee5cbfSDavid du Colombier 	// original header.
28180ee5cbfSDavid du Colombier 	// for pop3 messages, the best we can do is
28280ee5cbfSDavid du Colombier 	// use the From: information and the RFC822 date.
28380ee5cbfSDavid du Colombier 	//
28480ee5cbfSDavid du Colombier 	if(m->unixdate == nil){
28580ee5cbfSDavid du Colombier 		// look for the date in the first Received: line.
28680ee5cbfSDavid du Colombier 		// it's likely to be the right time zone (it's
28780ee5cbfSDavid du Colombier 	 	// the local system) and in a convenient format.
28880ee5cbfSDavid du Colombier 		if(cistrncmp(m->header, "received:", 9)==0){
28980ee5cbfSDavid du Colombier 			if((q = strchr(m->header, ';'))
29080ee5cbfSDavid du Colombier 			&& (p = strchr(q, '\n'))){
29180ee5cbfSDavid du Colombier 				*p = '\0';
29280ee5cbfSDavid du Colombier 				m->unixdate = date822tounix(q+1);
29380ee5cbfSDavid du Colombier 				*p = '\n';
29480ee5cbfSDavid du Colombier 			}
29580ee5cbfSDavid du Colombier 		}
29680ee5cbfSDavid du Colombier 
29780ee5cbfSDavid du Colombier 		// fall back on the rfc822 date
29880ee5cbfSDavid du Colombier 		if(m->unixdate==nil && m->date822)
29980ee5cbfSDavid du Colombier 			m->unixdate = date822tounix(s_to_c(m->date822));
30080ee5cbfSDavid du Colombier 	}
30180ee5cbfSDavid du Colombier 
302*9a747e4fSDavid du Colombier 	if(m->unixheader != nil)
303*9a747e4fSDavid du Colombier 		s_free(m->unixheader);
30480ee5cbfSDavid du Colombier 	m->unixheader = s_copy("From ");
30580ee5cbfSDavid du Colombier 	if(m->unixfrom)
30680ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->unixfrom));
30780ee5cbfSDavid du Colombier 	else if(m->from822)
30880ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->from822));
30980ee5cbfSDavid du Colombier 	else
31080ee5cbfSDavid du Colombier 		s_append(m->unixheader, "???");
31180ee5cbfSDavid du Colombier 
31280ee5cbfSDavid du Colombier 	s_append(m->unixheader, " ");
31380ee5cbfSDavid du Colombier 	if(m->unixdate)
31480ee5cbfSDavid du Colombier 		s_append(m->unixheader, s_to_c(m->unixdate));
31580ee5cbfSDavid du Colombier 	else
31680ee5cbfSDavid du Colombier 		s_append(m->unixheader, "Thu Jan  1 00:00:00 EST 1970");
31780ee5cbfSDavid du Colombier 
31880ee5cbfSDavid du Colombier 	s_append(m->unixheader, "\n");
31980ee5cbfSDavid du Colombier }
32080ee5cbfSDavid du Colombier 
321*9a747e4fSDavid du Colombier String*
322*9a747e4fSDavid du Colombier promote(String **sp)
323*9a747e4fSDavid du Colombier {
324*9a747e4fSDavid du Colombier 	String *s;
325*9a747e4fSDavid du Colombier 
326*9a747e4fSDavid du Colombier 	if(*sp != nil)
327*9a747e4fSDavid du Colombier 		s = s_clone(*sp);
328*9a747e4fSDavid du Colombier 	else
329*9a747e4fSDavid du Colombier 		s = nil;
330*9a747e4fSDavid du Colombier 	return s;
331*9a747e4fSDavid du Colombier }
332*9a747e4fSDavid du Colombier 
33380ee5cbfSDavid du Colombier void
33480ee5cbfSDavid du Colombier parsebody(Message *m, Mailbox *mb)
33580ee5cbfSDavid du Colombier {
336*9a747e4fSDavid du Colombier 	Message *nm;
337*9a747e4fSDavid du Colombier 
3387dd7cddfSDavid du Colombier 	// recurse
3397dd7cddfSDavid du Colombier 	if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){
3407dd7cddfSDavid du Colombier 		parseattachments(m, mb);
3417dd7cddfSDavid du Colombier 	} else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
3427dd7cddfSDavid du Colombier 		decode(m);
3437dd7cddfSDavid du Colombier 		parseattachments(m, mb);
344*9a747e4fSDavid du Colombier 		nm = m->part;
345*9a747e4fSDavid du Colombier 
346*9a747e4fSDavid du Colombier 		// promote headers
347*9a747e4fSDavid du Colombier 		m->from822 = promote(&nm->from822);
348*9a747e4fSDavid du Colombier 		m->to822 = promote(&nm->to822);
349*9a747e4fSDavid du Colombier 		m->date822 = promote(&nm->date822);
350*9a747e4fSDavid du Colombier 		m->sender822 = promote(&nm->sender822);
351*9a747e4fSDavid du Colombier 		m->replyto822 = promote(&nm->replyto822);
352*9a747e4fSDavid du Colombier 		m->subject822 = promote(&nm->subject822);
353*9a747e4fSDavid du Colombier 		m->unixdate = promote(&nm->unixdate);
3547dd7cddfSDavid du Colombier 	}
3557dd7cddfSDavid du Colombier }
3567dd7cddfSDavid du Colombier 
35780ee5cbfSDavid du Colombier void
35880ee5cbfSDavid du Colombier parse(Message *m, int justmime, Mailbox *mb)
35980ee5cbfSDavid du Colombier {
36080ee5cbfSDavid du Colombier 	parseheaders(m, justmime, mb);
36180ee5cbfSDavid du Colombier 	parsebody(m, mb);
36280ee5cbfSDavid du Colombier }
36380ee5cbfSDavid du Colombier 
3647dd7cddfSDavid du Colombier static void
3657dd7cddfSDavid du Colombier parseattachments(Message *m, Mailbox *mb)
3667dd7cddfSDavid du Colombier {
3677dd7cddfSDavid du Colombier 	Message *nm, **l;
3687dd7cddfSDavid du Colombier 	char *p, *x;
3697dd7cddfSDavid du Colombier 
3707dd7cddfSDavid du Colombier 	// if there's a boundary, recurse...
3717dd7cddfSDavid du Colombier 	if(m->boundary != nil){
3727dd7cddfSDavid du Colombier 		p = m->body;
3737dd7cddfSDavid du Colombier 		nm = nil;
3747dd7cddfSDavid du Colombier 		l = &m->part;
3757dd7cddfSDavid du Colombier 		for(;;){
3767dd7cddfSDavid du Colombier 			x = strstr(p, s_to_c(m->boundary));
3777dd7cddfSDavid du Colombier 			if(x == nil || (x != m->body && *(x-1) != '\n')){
3787dd7cddfSDavid du Colombier 				if(nm != nil)
3797dd7cddfSDavid du Colombier 					nm->rbend = nm->bend = nm->end = m->bend;
3807dd7cddfSDavid du Colombier 				break;
3817dd7cddfSDavid du Colombier 			}
3827dd7cddfSDavid du Colombier 			if(nm != nil)
3837dd7cddfSDavid du Colombier 				nm->rbend = nm->bend = nm->end = x;
3847dd7cddfSDavid du Colombier 			x += strlen(s_to_c(m->boundary));
3857dd7cddfSDavid du Colombier 
3867dd7cddfSDavid du Colombier 			/* is this the last part? ignore anything after it */
3877dd7cddfSDavid du Colombier 			if(strncmp(x, "--", 2) == 0)
3887dd7cddfSDavid du Colombier 				break;
3897dd7cddfSDavid du Colombier 
3907dd7cddfSDavid du Colombier 			p = strchr(x, '\n');
3917dd7cddfSDavid du Colombier 			if(p == nil)
3927dd7cddfSDavid du Colombier 				break;
3937dd7cddfSDavid du Colombier 			nm = newmessage(m);
3947dd7cddfSDavid du Colombier 			nm->start = nm->header = nm->body = nm->rbody = ++p;
3957dd7cddfSDavid du Colombier 			nm->mheader = nm->header;
3967dd7cddfSDavid du Colombier 			*l = nm;
3977dd7cddfSDavid du Colombier 			l = &nm->next;
3987dd7cddfSDavid du Colombier 		}
3997dd7cddfSDavid du Colombier 		for(nm = m->part; nm != nil; nm = nm->next)
4007dd7cddfSDavid du Colombier 			parse(nm, 1, mb);
401*9a747e4fSDavid du Colombier 		return;
4027dd7cddfSDavid du Colombier 	}
403*9a747e4fSDavid du Colombier 
404*9a747e4fSDavid du Colombier 	// if we've got an rfc822 message, recurse...
405*9a747e4fSDavid du Colombier 	if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
4067dd7cddfSDavid du Colombier 		nm = newmessage(m);
4077dd7cddfSDavid du Colombier 		m->part = nm;
4087dd7cddfSDavid du Colombier 		nm->start = nm->header = nm->body = nm->rbody = m->body;
4097dd7cddfSDavid du Colombier 		nm->end = nm->bend = nm->rbend = m->bend;
4107dd7cddfSDavid du Colombier 		parse(nm, 0, mb);
4117dd7cddfSDavid du Colombier 	}
4127dd7cddfSDavid du Colombier }
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier /*
4157dd7cddfSDavid du Colombier  *  pick up a header line
4167dd7cddfSDavid du Colombier  */
4177dd7cddfSDavid du Colombier static int
4187dd7cddfSDavid du Colombier headerline(char **pp, String *hl)
4197dd7cddfSDavid du Colombier {
4207dd7cddfSDavid du Colombier 	char *p, *x;
4217dd7cddfSDavid du Colombier 
4227dd7cddfSDavid du Colombier 	s_reset(hl);
4237dd7cddfSDavid du Colombier 	p = *pp;
4247dd7cddfSDavid du Colombier 	x = strpbrk(p, ":\n");
4257dd7cddfSDavid du Colombier 	if(x == nil || *x == '\n')
4267dd7cddfSDavid du Colombier 		return 0;
4277dd7cddfSDavid du Colombier 	for(;;){
4287dd7cddfSDavid du Colombier 		x = strchr(p, '\n');
4297dd7cddfSDavid du Colombier 		if(x == nil)
4307dd7cddfSDavid du Colombier 			x = p + strlen(p);
4317dd7cddfSDavid du Colombier 		s_nappend(hl, p, x-p);
4327dd7cddfSDavid du Colombier 		p = x;
433223a736eSDavid du Colombier 		if(*p != '\n' || *++p != ' ' && *p != '\t')
4347dd7cddfSDavid du Colombier 			break;
435223a736eSDavid du Colombier 		while(*p == ' ' || *p == '\t')
436223a736eSDavid du Colombier 			p++;
437223a736eSDavid du Colombier 		s_putc(hl, ' ');
4387dd7cddfSDavid du Colombier 	}
4397dd7cddfSDavid du Colombier 	*pp = p;
4407dd7cddfSDavid du Colombier 	return 1;
4417dd7cddfSDavid du Colombier }
4427dd7cddfSDavid du Colombier 
4437dd7cddfSDavid du Colombier static String*
4447dd7cddfSDavid du Colombier addr822(char *p)
4457dd7cddfSDavid du Colombier {
4467dd7cddfSDavid du Colombier 	String *s, *list;
4477dd7cddfSDavid du Colombier 	int incomment, addrdone, inanticomment, quoted;
4487dd7cddfSDavid du Colombier 	int n;
4497dd7cddfSDavid du Colombier 	int c;
4507dd7cddfSDavid du Colombier 
4517dd7cddfSDavid du Colombier 	list = s_new();
4527dd7cddfSDavid du Colombier 	s = s_new();
4537dd7cddfSDavid du Colombier 	quoted = incomment = addrdone = inanticomment = 0;
4547dd7cddfSDavid du Colombier 	n = 0;
4557dd7cddfSDavid du Colombier 	for(; *p; p++){
4567dd7cddfSDavid du Colombier 		c = *p;
4577dd7cddfSDavid du Colombier 
4587dd7cddfSDavid du Colombier 		// whitespace is ignored
4597dd7cddfSDavid du Colombier 		if(!quoted && isspace(c) || c == '\r')
4607dd7cddfSDavid du Colombier 			continue;
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 		// strings are always treated as atoms
4637dd7cddfSDavid du Colombier 		if(!quoted && c == '"'){
4647dd7cddfSDavid du Colombier 			if(!addrdone && !incomment)
4657dd7cddfSDavid du Colombier 				s_putc(s, c);
4667dd7cddfSDavid du Colombier 			for(p++; *p; p++){
4677dd7cddfSDavid du Colombier 				if(!addrdone && !incomment)
4687dd7cddfSDavid du Colombier 					s_putc(s, *p);
4697dd7cddfSDavid du Colombier 				if(!quoted && *p == '"')
4707dd7cddfSDavid du Colombier 					break;
4717dd7cddfSDavid du Colombier 				if(*p == '\\')
4727dd7cddfSDavid du Colombier 					quoted = 1;
4737dd7cddfSDavid du Colombier 				else
4747dd7cddfSDavid du Colombier 					quoted = 0;
4757dd7cddfSDavid du Colombier 			}
4767dd7cddfSDavid du Colombier 			if(*p == 0)
4777dd7cddfSDavid du Colombier 				break;
4787dd7cddfSDavid du Colombier 			quoted = 0;
4797dd7cddfSDavid du Colombier 			continue;
4807dd7cddfSDavid du Colombier 		}
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier 		// ignore everything in an expicit comment
4837dd7cddfSDavid du Colombier 		if(!quoted && c == '('){
4847dd7cddfSDavid du Colombier 			incomment = 1;
4857dd7cddfSDavid du Colombier 			continue;
4867dd7cddfSDavid du Colombier 		}
4877dd7cddfSDavid du Colombier 		if(incomment){
4887dd7cddfSDavid du Colombier 			if(!quoted && c == ')')
4897dd7cddfSDavid du Colombier 				incomment = 0;
4907dd7cddfSDavid du Colombier 			quoted = 0;
4917dd7cddfSDavid du Colombier 			continue;
4927dd7cddfSDavid du Colombier 		}
4937dd7cddfSDavid du Colombier 
4947dd7cddfSDavid du Colombier 		// anticomments makes everything outside of them comments
4957dd7cddfSDavid du Colombier 		if(!quoted && c == '<' && !inanticomment){
4967dd7cddfSDavid du Colombier 			inanticomment = 1;
4977dd7cddfSDavid du Colombier 			s = s_reset(s);
4987dd7cddfSDavid du Colombier 			continue;
4997dd7cddfSDavid du Colombier 		}
5007dd7cddfSDavid du Colombier 		if(!quoted && c == '>' && inanticomment){
5017dd7cddfSDavid du Colombier 			addrdone = 1;
5027dd7cddfSDavid du Colombier 			inanticomment = 0;
5037dd7cddfSDavid du Colombier 			continue;
5047dd7cddfSDavid du Colombier 		}
5057dd7cddfSDavid du Colombier 
5067dd7cddfSDavid du Colombier 		// commas separate addresses
5077dd7cddfSDavid du Colombier 		if(!quoted && c == ',' && !inanticomment){
5087dd7cddfSDavid du Colombier 			s_terminate(s);
5097dd7cddfSDavid du Colombier 			addrdone = 0;
5107dd7cddfSDavid du Colombier 			if(n++ != 0)
5117dd7cddfSDavid du Colombier 				s_append(list, " ");
5127dd7cddfSDavid du Colombier 			s_append(list, s_to_c(s));
5137dd7cddfSDavid du Colombier 			s = s_reset(s);
5147dd7cddfSDavid du Colombier 			continue;
5157dd7cddfSDavid du Colombier 		}
5167dd7cddfSDavid du Colombier 
5177dd7cddfSDavid du Colombier 		// what's left is part of the address
5187dd7cddfSDavid du Colombier 		s_putc(s, c);
5197dd7cddfSDavid du Colombier 
5207dd7cddfSDavid du Colombier 		// quoted characters are recognized only as characters
5217dd7cddfSDavid du Colombier 		if(c == '\\')
5227dd7cddfSDavid du Colombier 			quoted = 1;
5237dd7cddfSDavid du Colombier 		else
5247dd7cddfSDavid du Colombier 			quoted = 0;
5257dd7cddfSDavid du Colombier 
5267dd7cddfSDavid du Colombier 	}
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 	if(*s_to_c(s) != 0){
5297dd7cddfSDavid du Colombier 		s_terminate(s);
5307dd7cddfSDavid du Colombier 		if(n++ != 0)
5317dd7cddfSDavid du Colombier 			s_append(list, " ");
5327dd7cddfSDavid du Colombier 		s_append(list, s_to_c(s));
5337dd7cddfSDavid du Colombier 	}
5347dd7cddfSDavid du Colombier 	s_free(s);
5357dd7cddfSDavid du Colombier 
5367dd7cddfSDavid du Colombier 	if(n == 0){
5377dd7cddfSDavid du Colombier 		s_free(list);
5387dd7cddfSDavid du Colombier 		return nil;
5397dd7cddfSDavid du Colombier 	}
5407dd7cddfSDavid du Colombier 	return list;
5417dd7cddfSDavid du Colombier }
5427dd7cddfSDavid du Colombier 
5437dd7cddfSDavid du Colombier static void
5447dd7cddfSDavid du Colombier to822(Message *m, Header *h, char *p)
5457dd7cddfSDavid du Colombier {
5467dd7cddfSDavid du Colombier 	p += strlen(h->type);
5477dd7cddfSDavid du Colombier 	s_free(m->to822);
5487dd7cddfSDavid du Colombier 	m->to822 = addr822(p);
5497dd7cddfSDavid du Colombier }
5507dd7cddfSDavid du Colombier 
5517dd7cddfSDavid du Colombier static void
5527dd7cddfSDavid du Colombier cc822(Message *m, Header *h, char *p)
5537dd7cddfSDavid du Colombier {
5547dd7cddfSDavid du Colombier 	p += strlen(h->type);
5557dd7cddfSDavid du Colombier 	s_free(m->cc822);
5567dd7cddfSDavid du Colombier 	m->cc822 = addr822(p);
5577dd7cddfSDavid du Colombier }
5587dd7cddfSDavid du Colombier 
5597dd7cddfSDavid du Colombier static void
5607dd7cddfSDavid du Colombier bcc822(Message *m, Header *h, char *p)
5617dd7cddfSDavid du Colombier {
5627dd7cddfSDavid du Colombier 	p += strlen(h->type);
5637dd7cddfSDavid du Colombier 	s_free(m->bcc822);
5647dd7cddfSDavid du Colombier 	m->bcc822 = addr822(p);
5657dd7cddfSDavid du Colombier }
5667dd7cddfSDavid du Colombier 
5677dd7cddfSDavid du Colombier static void
5687dd7cddfSDavid du Colombier from822(Message *m, Header *h, char *p)
5697dd7cddfSDavid du Colombier {
5707dd7cddfSDavid du Colombier 	p += strlen(h->type);
5717dd7cddfSDavid du Colombier 	s_free(m->from822);
5727dd7cddfSDavid du Colombier 	m->from822 = addr822(p);
5737dd7cddfSDavid du Colombier }
5747dd7cddfSDavid du Colombier 
5757dd7cddfSDavid du Colombier static void
5767dd7cddfSDavid du Colombier sender822(Message *m, Header *h, char *p)
5777dd7cddfSDavid du Colombier {
5787dd7cddfSDavid du Colombier 	p += strlen(h->type);
5797dd7cddfSDavid du Colombier 	s_free(m->sender822);
5807dd7cddfSDavid du Colombier 	m->sender822 = addr822(p);
5817dd7cddfSDavid du Colombier }
5827dd7cddfSDavid du Colombier 
5837dd7cddfSDavid du Colombier static void
5847dd7cddfSDavid du Colombier replyto822(Message *m, Header *h, char *p)
5857dd7cddfSDavid du Colombier {
5867dd7cddfSDavid du Colombier 	p += strlen(h->type);
5877dd7cddfSDavid du Colombier 	s_free(m->replyto822);
5887dd7cddfSDavid du Colombier 	m->replyto822 = addr822(p);
5897dd7cddfSDavid du Colombier }
5907dd7cddfSDavid du Colombier 
5917dd7cddfSDavid du Colombier static void
5927dd7cddfSDavid du Colombier mimeversion(Message *m, Header *h, char *p)
5937dd7cddfSDavid du Colombier {
5947dd7cddfSDavid du Colombier 	p += strlen(h->type);
5957dd7cddfSDavid du Colombier 	s_free(m->mimeversion);
5967dd7cddfSDavid du Colombier 	m->mimeversion = addr822(p);
5977dd7cddfSDavid du Colombier }
5987dd7cddfSDavid du Colombier 
5997dd7cddfSDavid du Colombier static void
6007dd7cddfSDavid du Colombier killtrailingwhite(char *p)
6017dd7cddfSDavid du Colombier {
6027dd7cddfSDavid du Colombier 	char *e;
6037dd7cddfSDavid du Colombier 
6047dd7cddfSDavid du Colombier 	e = p + strlen(p) - 1;
6057dd7cddfSDavid du Colombier 	while(e > p && isspace(*e))
6067dd7cddfSDavid du Colombier 		*e-- = 0;
6077dd7cddfSDavid du Colombier }
6087dd7cddfSDavid du Colombier 
6097dd7cddfSDavid du Colombier static void
6107dd7cddfSDavid du Colombier date822(Message *m, Header *h, char *p)
6117dd7cddfSDavid du Colombier {
6127dd7cddfSDavid du Colombier 	p += strlen(h->type);
6137dd7cddfSDavid du Colombier 	p = skipwhite(p);
6147dd7cddfSDavid du Colombier 	s_free(m->date822);
6157dd7cddfSDavid du Colombier 	m->date822 = s_copy(p);
6167dd7cddfSDavid du Colombier 	p = s_to_c(m->date822);
6177dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6187dd7cddfSDavid du Colombier }
6197dd7cddfSDavid du Colombier 
6207dd7cddfSDavid du Colombier static void
6217dd7cddfSDavid du Colombier subject822(Message *m, Header *h, char *p)
6227dd7cddfSDavid du Colombier {
6237dd7cddfSDavid du Colombier 	p += strlen(h->type);
6247dd7cddfSDavid du Colombier 	p = skipwhite(p);
6257dd7cddfSDavid du Colombier 	s_free(m->subject822);
6267dd7cddfSDavid du Colombier 	m->subject822 = s_copy(p);
6277dd7cddfSDavid du Colombier 	p = s_to_c(m->subject822);
6287dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6297dd7cddfSDavid du Colombier }
6307dd7cddfSDavid du Colombier 
6317dd7cddfSDavid du Colombier static void
6327dd7cddfSDavid du Colombier inreplyto822(Message *m, Header *h, char *p)
6337dd7cddfSDavid du Colombier {
6347dd7cddfSDavid du Colombier 	p += strlen(h->type);
6357dd7cddfSDavid du Colombier 	p = skipwhite(p);
6367dd7cddfSDavid du Colombier 	s_free(m->inreplyto822);
6377dd7cddfSDavid du Colombier 	m->inreplyto822 = s_copy(p);
6387dd7cddfSDavid du Colombier 	p = s_to_c(m->inreplyto822);
6397dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6407dd7cddfSDavid du Colombier }
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier static void
6437dd7cddfSDavid du Colombier messageid822(Message *m, Header *h, char *p)
6447dd7cddfSDavid du Colombier {
6457dd7cddfSDavid du Colombier 	p += strlen(h->type);
6467dd7cddfSDavid du Colombier 	p = skipwhite(p);
6477dd7cddfSDavid du Colombier 	s_free(m->messageid822);
6487dd7cddfSDavid du Colombier 	m->messageid822 = s_copy(p);
6497dd7cddfSDavid du Colombier 	p = s_to_c(m->messageid822);
6507dd7cddfSDavid du Colombier 	killtrailingwhite(p);
6517dd7cddfSDavid du Colombier }
6527dd7cddfSDavid du Colombier 
6537dd7cddfSDavid du Colombier static int
6547dd7cddfSDavid du Colombier isattribute(char **pp, char *attr)
6557dd7cddfSDavid du Colombier {
6567dd7cddfSDavid du Colombier 	char *p;
6577dd7cddfSDavid du Colombier 	int n;
6587dd7cddfSDavid du Colombier 
6597dd7cddfSDavid du Colombier 	n = strlen(attr);
6607dd7cddfSDavid du Colombier 	p = *pp;
6617dd7cddfSDavid du Colombier 	if(cistrncmp(p, attr, n) != 0)
6627dd7cddfSDavid du Colombier 		return 0;
6637dd7cddfSDavid du Colombier 	p += n;
6647dd7cddfSDavid du Colombier 	while(*p == ' ')
6657dd7cddfSDavid du Colombier 		p++;
6667dd7cddfSDavid du Colombier 	if(*p++ != '=')
6677dd7cddfSDavid du Colombier 		return 0;
6687dd7cddfSDavid du Colombier 	while(*p == ' ')
6697dd7cddfSDavid du Colombier 		p++;
6707dd7cddfSDavid du Colombier 	*pp = p;
6717dd7cddfSDavid du Colombier 	return 1;
6727dd7cddfSDavid du Colombier }
6737dd7cddfSDavid du Colombier 
6747dd7cddfSDavid du Colombier static void
6757dd7cddfSDavid du Colombier ctype(Message *m, Header *h, char *p)
6767dd7cddfSDavid du Colombier {
6777dd7cddfSDavid du Colombier 	String *s;
6787dd7cddfSDavid du Colombier 
6797dd7cddfSDavid du Colombier 	p += h->len;
6807dd7cddfSDavid du Colombier 	p = skipwhite(p);
6817dd7cddfSDavid du Colombier 
6827dd7cddfSDavid du Colombier 	p = getstring(p, m->type, 1);
6837dd7cddfSDavid du Colombier 
6847dd7cddfSDavid du Colombier 	while(*p){
6857dd7cddfSDavid du Colombier 		if(isattribute(&p, "boundary")){
6867dd7cddfSDavid du Colombier 			s = s_new();
6877dd7cddfSDavid du Colombier 			p = getstring(p, s, 0);
6887dd7cddfSDavid du Colombier 			m->boundary = s_reset(m->boundary);
6897dd7cddfSDavid du Colombier 			s_append(m->boundary, "--");
6907dd7cddfSDavid du Colombier 			s_append(m->boundary, s_to_c(s));
6917dd7cddfSDavid du Colombier 			s_free(s);
6927dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "multipart", 9) == 0){
6937dd7cddfSDavid du Colombier 			/*
6947dd7cddfSDavid du Colombier 			 *  the first unbounded part of a multipart message,
6957dd7cddfSDavid du Colombier 			 *  the preamble, is not displayed or saved
6967dd7cddfSDavid du Colombier 			 */
6977dd7cddfSDavid du Colombier 		} else if(isattribute(&p, "name")){
6987dd7cddfSDavid du Colombier 			if(m->filename == nil)
6997dd7cddfSDavid du Colombier 				setfilename(m, p);
7007dd7cddfSDavid du Colombier 		} else if(isattribute(&p, "charset")){
7017dd7cddfSDavid du Colombier 			p = getstring(p, s_reset(m->charset), 0);
7027dd7cddfSDavid du Colombier 		}
7037dd7cddfSDavid du Colombier 
7047dd7cddfSDavid du Colombier 		p = skiptosemi(p);
7057dd7cddfSDavid du Colombier 	}
7067dd7cddfSDavid du Colombier }
7077dd7cddfSDavid du Colombier 
7087dd7cddfSDavid du Colombier static void
7097dd7cddfSDavid du Colombier cencoding(Message *m, Header *h, char *p)
7107dd7cddfSDavid du Colombier {
7117dd7cddfSDavid du Colombier 	p += h->len;
7127dd7cddfSDavid du Colombier 	p = skipwhite(p);
7137dd7cddfSDavid du Colombier 	if(cistrncmp(p, "base64", 6) == 0)
7147dd7cddfSDavid du Colombier 		m->encoding = Ebase64;
7157dd7cddfSDavid du Colombier 	else if(cistrncmp(p, "quoted-printable", 16) == 0)
7167dd7cddfSDavid du Colombier 		m->encoding = Equoted;
7177dd7cddfSDavid du Colombier }
7187dd7cddfSDavid du Colombier 
7197dd7cddfSDavid du Colombier static void
7207dd7cddfSDavid du Colombier cdisposition(Message *m, Header *h, char *p)
7217dd7cddfSDavid du Colombier {
7227dd7cddfSDavid du Colombier 	p += h->len;
7237dd7cddfSDavid du Colombier 	p = skipwhite(p);
7247dd7cddfSDavid du Colombier 	while(*p){
7257dd7cddfSDavid du Colombier 		if(cistrncmp(p, "inline", 6) == 0){
7267dd7cddfSDavid du Colombier 			m->disposition = Dinline;
7277dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "attachment", 10) == 0){
7287dd7cddfSDavid du Colombier 			m->disposition = Dfile;
7297dd7cddfSDavid du Colombier 		} else if(cistrncmp(p, "filename=", 9) == 0){
7307dd7cddfSDavid du Colombier 			p += 9;
7317dd7cddfSDavid du Colombier 			setfilename(m, p);
7327dd7cddfSDavid du Colombier 		}
7337dd7cddfSDavid du Colombier 		p = skiptosemi(p);
7347dd7cddfSDavid du Colombier 	}
7357dd7cddfSDavid du Colombier 
7367dd7cddfSDavid du Colombier }
7377dd7cddfSDavid du Colombier 
7387dd7cddfSDavid du Colombier ulong msgallocd, msgfreed;
7397dd7cddfSDavid du Colombier 
7407dd7cddfSDavid du Colombier Message*
7417dd7cddfSDavid du Colombier newmessage(Message *parent)
7427dd7cddfSDavid du Colombier {
7437dd7cddfSDavid du Colombier 	static int id;
7447dd7cddfSDavid du Colombier 	Message *m;
7457dd7cddfSDavid du Colombier 
7467dd7cddfSDavid du Colombier 	msgallocd++;
7477dd7cddfSDavid du Colombier 
7487dd7cddfSDavid du Colombier 	m = emalloc(sizeof(*m));
7497dd7cddfSDavid du Colombier 	memset(m, 0, sizeof(*m));
7507dd7cddfSDavid du Colombier 	m->disposition = Dnone;
7517dd7cddfSDavid du Colombier 	m->type = s_copy("text/plain");
7527dd7cddfSDavid du Colombier 	m->charset = s_copy("iso-8859-1");
7537dd7cddfSDavid du Colombier 	m->id = newid();
7547dd7cddfSDavid du Colombier 	if(parent)
7557dd7cddfSDavid du Colombier 		sprint(m->name, "%d", ++(parent->subname));
7567dd7cddfSDavid du Colombier 	if(parent == nil)
7577dd7cddfSDavid du Colombier 		parent = m;
7587dd7cddfSDavid du Colombier 	m->whole = parent;
7597dd7cddfSDavid du Colombier 	m->hlen = -1;
7607dd7cddfSDavid du Colombier 	return m;
7617dd7cddfSDavid du Colombier }
7627dd7cddfSDavid du Colombier 
7637dd7cddfSDavid du Colombier // delete a message from a mailbox
7647dd7cddfSDavid du Colombier void
7657dd7cddfSDavid du Colombier delmessage(Mailbox *mb, Message *m)
7667dd7cddfSDavid du Colombier {
7677dd7cddfSDavid du Colombier 	Message **l;
7687dd7cddfSDavid du Colombier 	int i;
7697dd7cddfSDavid du Colombier 
7707dd7cddfSDavid du Colombier 	mb->vers++;
7717dd7cddfSDavid du Colombier 	msgfreed++;
7727dd7cddfSDavid du Colombier 
7737dd7cddfSDavid du Colombier 	if(m->whole != m){
7747dd7cddfSDavid du Colombier 		// unchain from parent
7757dd7cddfSDavid du Colombier 		for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
7767dd7cddfSDavid du Colombier 			;
7777dd7cddfSDavid du Colombier 		if(*l != nil)
7787dd7cddfSDavid du Colombier 			*l = m->next;
7797dd7cddfSDavid du Colombier 
7807dd7cddfSDavid du Colombier 		// clear out of name lookup hash table
7817dd7cddfSDavid du Colombier 		if(m->whole->whole == m->whole)
782*9a747e4fSDavid du Colombier 			hfree(PATH(mb->id, Qmbox), m->name);
7837dd7cddfSDavid du Colombier 		else
784*9a747e4fSDavid du Colombier 			hfree(PATH(m->whole->id, Qdir), m->name);
7857dd7cddfSDavid du Colombier 		for(i = 0; i < Qmax; i++)
786*9a747e4fSDavid du Colombier 			hfree(PATH(m->id, Qdir), dirtab[i]);
7877dd7cddfSDavid du Colombier 	}
7887dd7cddfSDavid du Colombier 
7897dd7cddfSDavid du Colombier 	/* recurse through sub-parts */
7907dd7cddfSDavid du Colombier 	while(m->part)
7917dd7cddfSDavid du Colombier 		delmessage(mb, m->part);
7927dd7cddfSDavid du Colombier 
7937dd7cddfSDavid du Colombier 	/* free memory */
7947dd7cddfSDavid du Colombier 	if(m->mallocd)
7957dd7cddfSDavid du Colombier 		free(m->start);
7967dd7cddfSDavid du Colombier 	if(m->hallocd)
7977dd7cddfSDavid du Colombier 		free(m->header);
7987dd7cddfSDavid du Colombier 	if(m->ballocd)
7997dd7cddfSDavid du Colombier 		free(m->body);
8007dd7cddfSDavid du Colombier 	s_free(m->unixfrom);
8017dd7cddfSDavid du Colombier 	s_free(m->unixdate);
802*9a747e4fSDavid du Colombier 	s_free(m->unixheader);
8037dd7cddfSDavid du Colombier 	s_free(m->from822);
8047dd7cddfSDavid du Colombier 	s_free(m->sender822);
8057dd7cddfSDavid du Colombier 	s_free(m->to822);
8067dd7cddfSDavid du Colombier 	s_free(m->bcc822);
80759cc4ca5SDavid du Colombier 	s_free(m->cc822);
8087dd7cddfSDavid du Colombier 	s_free(m->replyto822);
8097dd7cddfSDavid du Colombier 	s_free(m->date822);
8107dd7cddfSDavid du Colombier 	s_free(m->inreplyto822);
81159cc4ca5SDavid du Colombier 	s_free(m->subject822);
8127dd7cddfSDavid du Colombier 	s_free(m->messageid822);
8137dd7cddfSDavid du Colombier 	s_free(m->addrs);
8147dd7cddfSDavid du Colombier 	s_free(m->mimeversion);
81559cc4ca5SDavid du Colombier 	s_free(m->sdigest);
8167dd7cddfSDavid du Colombier 	s_free(m->boundary);
8177dd7cddfSDavid du Colombier 	s_free(m->type);
8187dd7cddfSDavid du Colombier 	s_free(m->charset);
8197dd7cddfSDavid du Colombier 	s_free(m->filename);
8207dd7cddfSDavid du Colombier 
8217dd7cddfSDavid du Colombier 	free(m);
8227dd7cddfSDavid du Colombier }
8237dd7cddfSDavid du Colombier 
8247dd7cddfSDavid du Colombier // mark messages (identified by path) for deletion
8257dd7cddfSDavid du Colombier void
8267dd7cddfSDavid du Colombier delmessages(int ac, char **av)
8277dd7cddfSDavid du Colombier {
8287dd7cddfSDavid du Colombier 	Mailbox *mb;
8297dd7cddfSDavid du Colombier 	Message *m;
8307dd7cddfSDavid du Colombier 	int i, needwrite;
8317dd7cddfSDavid du Colombier 
8327dd7cddfSDavid du Colombier 	qlock(&mbllock);
8337dd7cddfSDavid du Colombier 	for(mb = mbl; mb != nil; mb = mb->next)
8347dd7cddfSDavid du Colombier 		if(strcmp(av[0], mb->name) == 0){
8357dd7cddfSDavid du Colombier 			qlock(mb);
8367dd7cddfSDavid du Colombier 			break;
8377dd7cddfSDavid du Colombier 		}
8387dd7cddfSDavid du Colombier 	qunlock(&mbllock);
8397dd7cddfSDavid du Colombier 	if(mb == nil)
8407dd7cddfSDavid du Colombier 		return;
8417dd7cddfSDavid du Colombier 
8427dd7cddfSDavid du Colombier 	needwrite = 0;
8437dd7cddfSDavid du Colombier 	for(i = 1; i < ac; i++){
8447dd7cddfSDavid du Colombier 		for(m = mb->root->part; m != nil; m = m->next)
8457dd7cddfSDavid du Colombier 			if(strcmp(m->name, av[i]) == 0){
8467dd7cddfSDavid du Colombier 				if(!m->deleted){
8477dd7cddfSDavid du Colombier 					mailplumb(mb, m, 1);
8487dd7cddfSDavid du Colombier 					needwrite = 1;
8497dd7cddfSDavid du Colombier 					m->deleted = 1;
85059cc4ca5SDavid du Colombier 					logmsg("deleting", m);
8517dd7cddfSDavid du Colombier 				}
8527dd7cddfSDavid du Colombier 				break;
8537dd7cddfSDavid du Colombier 			}
8547dd7cddfSDavid du Colombier 	}
8557dd7cddfSDavid du Colombier 	if(needwrite)
8567dd7cddfSDavid du Colombier 		syncmbox(mb, 1);
8577dd7cddfSDavid du Colombier 	qunlock(mb);
8587dd7cddfSDavid du Colombier }
8597dd7cddfSDavid du Colombier 
8607dd7cddfSDavid du Colombier /*
8617dd7cddfSDavid du Colombier  *  the following are called with the mailbox qlocked
8627dd7cddfSDavid du Colombier  */
8637dd7cddfSDavid du Colombier void
8647dd7cddfSDavid du Colombier msgincref(Message *m)
8657dd7cddfSDavid du Colombier {
8667dd7cddfSDavid du Colombier 	m->refs++;
8677dd7cddfSDavid du Colombier }
8687dd7cddfSDavid du Colombier void
8697dd7cddfSDavid du Colombier msgdecref(Mailbox *mb, Message *m)
8707dd7cddfSDavid du Colombier {
8717dd7cddfSDavid du Colombier 	m->refs--;
8727dd7cddfSDavid du Colombier 	if(m->refs == 0 && m->deleted)
8737dd7cddfSDavid du Colombier 		syncmbox(mb, 1);
8747dd7cddfSDavid du Colombier }
8757dd7cddfSDavid du Colombier 
8767dd7cddfSDavid du Colombier /*
8777dd7cddfSDavid du Colombier  *  the following are called with mbllock'd
8787dd7cddfSDavid du Colombier  */
8797dd7cddfSDavid du Colombier void
8807dd7cddfSDavid du Colombier mboxincref(Mailbox *mb)
8817dd7cddfSDavid du Colombier {
882*9a747e4fSDavid du Colombier 	assert(mb->refs > 0);
8837dd7cddfSDavid du Colombier 	mb->refs++;
8847dd7cddfSDavid du Colombier }
8857dd7cddfSDavid du Colombier void
8867dd7cddfSDavid du Colombier mboxdecref(Mailbox *mb)
8877dd7cddfSDavid du Colombier {
8887dd7cddfSDavid du Colombier 	Mailbox **l;
8897dd7cddfSDavid du Colombier 
890*9a747e4fSDavid du Colombier 	assert(mb->refs > 0);
8917dd7cddfSDavid du Colombier 	qlock(mb);
8927dd7cddfSDavid du Colombier 	mb->refs--;
8937dd7cddfSDavid du Colombier 	if(mb->refs == 0){
8947dd7cddfSDavid du Colombier 		for(l = &mbl; *l != nil; l = &(*l)->next){
8957dd7cddfSDavid du Colombier 			if(*l == mb){
8967dd7cddfSDavid du Colombier 				*l = mb->next;
8977dd7cddfSDavid du Colombier 				break;
8987dd7cddfSDavid du Colombier 			}
8997dd7cddfSDavid du Colombier 		}
9007dd7cddfSDavid du Colombier 		delmessage(mb, mb->root);
9017dd7cddfSDavid du Colombier 		qunlock(mb);
90280ee5cbfSDavid du Colombier 		if(mb->ctl)
903*9a747e4fSDavid du Colombier 			hfree(PATH(mb->id, Qmbox), "ctl");
90480ee5cbfSDavid du Colombier 		if(mb->close)
90580ee5cbfSDavid du Colombier 			(*mb->close)(mb);
9067dd7cddfSDavid du Colombier 		free(mb);
9077dd7cddfSDavid du Colombier 	} else
9087dd7cddfSDavid du Colombier 		qunlock(mb);
9097dd7cddfSDavid du Colombier }
9107dd7cddfSDavid du Colombier 
9117dd7cddfSDavid du Colombier int
9127dd7cddfSDavid du Colombier cistrncmp(char *a, char *b, int n)
9137dd7cddfSDavid du Colombier {
9147dd7cddfSDavid du Colombier 	while(n-- > 0){
9157dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
9167dd7cddfSDavid du Colombier 			return -1;
9177dd7cddfSDavid du Colombier 	}
9187dd7cddfSDavid du Colombier 	return 0;
9197dd7cddfSDavid du Colombier }
9207dd7cddfSDavid du Colombier 
9217dd7cddfSDavid du Colombier int
9227dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
9237dd7cddfSDavid du Colombier {
9247dd7cddfSDavid du Colombier 	for(;;){
9257dd7cddfSDavid du Colombier 		if(tolower(*a) != tolower(*b++))
9267dd7cddfSDavid du Colombier 			return -1;
9277dd7cddfSDavid du Colombier 		if(*a++ == 0)
9287dd7cddfSDavid du Colombier 			break;
9297dd7cddfSDavid du Colombier 	}
9307dd7cddfSDavid du Colombier 	return 0;
9317dd7cddfSDavid du Colombier }
9327dd7cddfSDavid du Colombier 
9337dd7cddfSDavid du Colombier static char*
9347dd7cddfSDavid du Colombier skipwhite(char *p)
9357dd7cddfSDavid du Colombier {
9367dd7cddfSDavid du Colombier 	while(isspace(*p))
9377dd7cddfSDavid du Colombier 		p++;
9387dd7cddfSDavid du Colombier 	return p;
9397dd7cddfSDavid du Colombier }
9407dd7cddfSDavid du Colombier 
9417dd7cddfSDavid du Colombier static char*
9427dd7cddfSDavid du Colombier skiptosemi(char *p)
9437dd7cddfSDavid du Colombier {
9447dd7cddfSDavid du Colombier 	while(*p && *p != ';')
9457dd7cddfSDavid du Colombier 		p++;
9467dd7cddfSDavid du Colombier 	while(*p == ';' || isspace(*p))
9477dd7cddfSDavid du Colombier 		p++;
9487dd7cddfSDavid du Colombier 	return p;
9497dd7cddfSDavid du Colombier }
9507dd7cddfSDavid du Colombier 
9517dd7cddfSDavid du Colombier static char*
9527dd7cddfSDavid du Colombier getstring(char *p, String *s, int dolower)
9537dd7cddfSDavid du Colombier {
9547dd7cddfSDavid du Colombier 	s = s_reset(s);
9557dd7cddfSDavid du Colombier 	p = skipwhite(p);
9567dd7cddfSDavid du Colombier 	if(*p == '"'){
9577dd7cddfSDavid du Colombier 		p++;
9587dd7cddfSDavid du Colombier 		for(;*p && *p != '"'; p++)
9597dd7cddfSDavid du Colombier 			if(dolower)
9607dd7cddfSDavid du Colombier 				s_putc(s, tolower(*p));
9617dd7cddfSDavid du Colombier 			else
9627dd7cddfSDavid du Colombier 				s_putc(s, *p);
9637dd7cddfSDavid du Colombier 		if(*p == '"')
9647dd7cddfSDavid du Colombier 			p++;
9657dd7cddfSDavid du Colombier 		s_terminate(s);
9667dd7cddfSDavid du Colombier 
9677dd7cddfSDavid du Colombier 		return p;
9687dd7cddfSDavid du Colombier 	}
9697dd7cddfSDavid du Colombier 
970*9a747e4fSDavid du Colombier 	for(; *p && !isspace(*p) && *p != ';'; p++)
9717dd7cddfSDavid du Colombier 		if(dolower)
9727dd7cddfSDavid du Colombier 			s_putc(s, tolower(*p));
9737dd7cddfSDavid du Colombier 		else
9747dd7cddfSDavid du Colombier 			s_putc(s, *p);
9757dd7cddfSDavid du Colombier 	s_terminate(s);
9767dd7cddfSDavid du Colombier 
9777dd7cddfSDavid du Colombier 	return p;
9787dd7cddfSDavid du Colombier }
9797dd7cddfSDavid du Colombier 
9807dd7cddfSDavid du Colombier static void
9817dd7cddfSDavid du Colombier setfilename(Message *m, char *p)
9827dd7cddfSDavid du Colombier {
9837dd7cddfSDavid du Colombier 	m->filename = s_reset(m->filename);
9847dd7cddfSDavid du Colombier 	getstring(p, m->filename, 0);
9857dd7cddfSDavid du Colombier 	for(p = s_to_c(m->filename); *p; p++)
9867dd7cddfSDavid du Colombier 		if(*p == ' ' || *p == '\t' || *p == ';')
9877dd7cddfSDavid du Colombier 			*p = '_';
9887dd7cddfSDavid du Colombier }
9897dd7cddfSDavid du Colombier 
9907dd7cddfSDavid du Colombier //
9917dd7cddfSDavid du Colombier // undecode message body
9927dd7cddfSDavid du Colombier //
9937dd7cddfSDavid du Colombier void
9947dd7cddfSDavid du Colombier decode(Message *m)
9957dd7cddfSDavid du Colombier {
9967dd7cddfSDavid du Colombier 	int i, len;
9977dd7cddfSDavid du Colombier 	char *x;
9987dd7cddfSDavid du Colombier 
9997dd7cddfSDavid du Colombier 	if(m->decoded)
10007dd7cddfSDavid du Colombier 		return;
10017dd7cddfSDavid du Colombier 	switch(m->encoding){
10027dd7cddfSDavid du Colombier 	case Ebase64:
10037dd7cddfSDavid du Colombier 		len = m->bend - m->body;
10047dd7cddfSDavid du Colombier 		i = (len*3)/4+1;	// room for max chars + null
10057dd7cddfSDavid du Colombier 		x = emalloc(i);
10067dd7cddfSDavid du Colombier 		len = dec64((uchar*)x, i, m->body, len);
10077dd7cddfSDavid du Colombier 		if(m->ballocd)
10087dd7cddfSDavid du Colombier 			free(m->body);
10097dd7cddfSDavid du Colombier 		m->body = x;
10107dd7cddfSDavid du Colombier 		m->bend = x + len;
10117dd7cddfSDavid du Colombier 		m->ballocd = 1;
10127dd7cddfSDavid du Colombier 		break;
10137dd7cddfSDavid du Colombier 	case Equoted:
10147dd7cddfSDavid du Colombier 		len = m->bend - m->body;
10157dd7cddfSDavid du Colombier 		x = emalloc(len+2);	// room for null and possible extra nl
10167dd7cddfSDavid du Colombier 		len = decquoted(x, m->body, m->bend);
10177dd7cddfSDavid du Colombier 		if(m->ballocd)
10187dd7cddfSDavid du Colombier 			free(m->body);
10197dd7cddfSDavid du Colombier 		m->body = x;
10207dd7cddfSDavid du Colombier 		m->bend = x + len;
10217dd7cddfSDavid du Colombier 		m->ballocd = 1;
10227dd7cddfSDavid du Colombier 		break;
10237dd7cddfSDavid du Colombier 	default:
10247dd7cddfSDavid du Colombier 		break;
10257dd7cddfSDavid du Colombier 	}
10267dd7cddfSDavid du Colombier 	m->decoded = 1;
10277dd7cddfSDavid du Colombier }
10287dd7cddfSDavid du Colombier 
10297dd7cddfSDavid du Colombier // convert latin1 to utf
10307dd7cddfSDavid du Colombier void
10317dd7cddfSDavid du Colombier convert(Message *m)
10327dd7cddfSDavid du Colombier {
10337dd7cddfSDavid du Colombier 	int len;
10347dd7cddfSDavid du Colombier 	char *x;
10357dd7cddfSDavid du Colombier 
10367dd7cddfSDavid du Colombier 	// don't convert if we're not a leaf, not text, or already converted
10377dd7cddfSDavid du Colombier 	if(m->converted)
10387dd7cddfSDavid du Colombier 		return;
10397dd7cddfSDavid du Colombier 	if(m->part != nil)
10407dd7cddfSDavid du Colombier 		return;
10417dd7cddfSDavid du Colombier 	if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
10427dd7cddfSDavid du Colombier 		return;
10437dd7cddfSDavid du Colombier 
10447dd7cddfSDavid du Colombier 	if(cistrcmp(s_to_c(m->charset), "us-ascii") == 0 ||
10457dd7cddfSDavid du Colombier 	   cistrcmp(s_to_c(m->charset), "iso-8859-1") == 0){
10467dd7cddfSDavid du Colombier 		len = is8bit(m);
10477dd7cddfSDavid du Colombier 		if(len > 0){
10487dd7cddfSDavid du Colombier 			len = 2*len + m->bend - m->body + 1;
10497dd7cddfSDavid du Colombier 			x = emalloc(len);
10507dd7cddfSDavid du Colombier 			len = latin1toutf(x, m->body, m->bend);
10517dd7cddfSDavid du Colombier 			if(m->ballocd)
10527dd7cddfSDavid du Colombier 				free(m->body);
10537dd7cddfSDavid du Colombier 			m->body = x;
10547dd7cddfSDavid du Colombier 			m->bend = x + len;
10557dd7cddfSDavid du Colombier 			m->ballocd = 1;
10567dd7cddfSDavid du Colombier 		}
10577dd7cddfSDavid du Colombier 	} else if(cistrcmp(s_to_c(m->charset), "big5") == 0){
10587dd7cddfSDavid du Colombier 		len = xtoutf("big5", &x, m->body, m->bend);
10597dd7cddfSDavid du Colombier 		if(len != 0){
10607dd7cddfSDavid du Colombier 			if(m->ballocd)
10617dd7cddfSDavid du Colombier 				free(m->body);
10627dd7cddfSDavid du Colombier 			m->body = x;
10637dd7cddfSDavid du Colombier 			m->bend = x + len;
10647dd7cddfSDavid du Colombier 			m->ballocd = 1;
10657dd7cddfSDavid du Colombier 		}
10667dd7cddfSDavid du Colombier 	} else if(cistrcmp(s_to_c(m->charset), "windows-1257") == 0){
10677dd7cddfSDavid du Colombier 		len = is8bit(m);
10687dd7cddfSDavid du Colombier 		if(len > 0){
10697dd7cddfSDavid du Colombier 			len = 2*len + m->bend - m->body + 1;
10707dd7cddfSDavid du Colombier 			x = emalloc(len);
10717dd7cddfSDavid du Colombier 			len = windows1257toutf(x, m->body, m->bend);
10727dd7cddfSDavid du Colombier 			if(m->ballocd)
10737dd7cddfSDavid du Colombier 				free(m->body);
10747dd7cddfSDavid du Colombier 			m->body = x;
10757dd7cddfSDavid du Colombier 			m->bend = x + len;
10767dd7cddfSDavid du Colombier 			m->ballocd = 1;
10777dd7cddfSDavid du Colombier 		}
10787dd7cddfSDavid du Colombier 	}
10797dd7cddfSDavid du Colombier 
10807dd7cddfSDavid du Colombier 	m->converted = 1;
10817dd7cddfSDavid du Colombier }
10827dd7cddfSDavid du Colombier 
10837dd7cddfSDavid du Colombier enum
10847dd7cddfSDavid du Colombier {
10857dd7cddfSDavid du Colombier 	Self=	1,
10867dd7cddfSDavid du Colombier 	Hex=	2,
10877dd7cddfSDavid du Colombier };
10887dd7cddfSDavid du Colombier uchar	tableqp[256];
10897dd7cddfSDavid du Colombier 
10907dd7cddfSDavid du Colombier static void
10917dd7cddfSDavid du Colombier initquoted(void)
10927dd7cddfSDavid du Colombier {
10937dd7cddfSDavid du Colombier 	int c;
10947dd7cddfSDavid du Colombier 
10957dd7cddfSDavid du Colombier 	memset(tableqp, 0, 256);
10967dd7cddfSDavid du Colombier 	for(c = ' '; c <= '<'; c++)
10977dd7cddfSDavid du Colombier 		tableqp[c] = Self;
10987dd7cddfSDavid du Colombier 	for(c = '>'; c <= '~'; c++)
10997dd7cddfSDavid du Colombier 		tableqp[c] = Self;
11007dd7cddfSDavid du Colombier 	tableqp['\t'] = Self;
11017dd7cddfSDavid du Colombier 	tableqp['='] = Hex;
11027dd7cddfSDavid du Colombier }
11037dd7cddfSDavid du Colombier 
11047dd7cddfSDavid du Colombier static int
11057dd7cddfSDavid du Colombier hex2int(int x)
11067dd7cddfSDavid du Colombier {
11077dd7cddfSDavid du Colombier 	if(x >= '0' && x <= '9')
11087dd7cddfSDavid du Colombier 		return x - '0';
11097dd7cddfSDavid du Colombier 	if(x >= 'A' && x <= 'F')
11107dd7cddfSDavid du Colombier 		return (x - 'A') + 10;
11117dd7cddfSDavid du Colombier 	if(x >= 'a' && x <= 'f')
11127dd7cddfSDavid du Colombier 		return (x - 'a') + 10;
11137dd7cddfSDavid du Colombier 	return 0;
11147dd7cddfSDavid du Colombier }
11157dd7cddfSDavid du Colombier 
11167dd7cddfSDavid du Colombier static char*
11177dd7cddfSDavid du Colombier decquotedline(char *out, char *in, char *e)
11187dd7cddfSDavid du Colombier {
11197dd7cddfSDavid du Colombier 	int c, soft;
11207dd7cddfSDavid du Colombier 
11217dd7cddfSDavid du Colombier 	/* dump trailing white space */
11227dd7cddfSDavid du Colombier 	while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
11237dd7cddfSDavid du Colombier 		e--;
11247dd7cddfSDavid du Colombier 
11257dd7cddfSDavid du Colombier 	/* trailing '=' means no newline */
11267dd7cddfSDavid du Colombier 	if(*e == '='){
11277dd7cddfSDavid du Colombier 		soft = 1;
11287dd7cddfSDavid du Colombier 		e--;
11297dd7cddfSDavid du Colombier 	} else
11307dd7cddfSDavid du Colombier 		soft = 0;
11317dd7cddfSDavid du Colombier 
11327dd7cddfSDavid du Colombier 	while(in <= e){
11337dd7cddfSDavid du Colombier 		c = (*in++) & 0xff;
11347dd7cddfSDavid du Colombier 		switch(tableqp[c]){
11357dd7cddfSDavid du Colombier 		case Self:
11367dd7cddfSDavid du Colombier 			*out++ = c;
11377dd7cddfSDavid du Colombier 			break;
11387dd7cddfSDavid du Colombier 		case Hex:
11397dd7cddfSDavid du Colombier 			c = hex2int(*in++)<<4;
11407dd7cddfSDavid du Colombier 			c |= hex2int(*in++);
11417dd7cddfSDavid du Colombier 			*out++ = c;
11427dd7cddfSDavid du Colombier 			break;
11437dd7cddfSDavid du Colombier 		}
11447dd7cddfSDavid du Colombier 	}
11457dd7cddfSDavid du Colombier 	if(!soft)
11467dd7cddfSDavid du Colombier 		*out++ = '\n';
11477dd7cddfSDavid du Colombier 	*out = 0;
11487dd7cddfSDavid du Colombier 
11497dd7cddfSDavid du Colombier 	return out;
11507dd7cddfSDavid du Colombier }
11517dd7cddfSDavid du Colombier 
11527dd7cddfSDavid du Colombier int
11537dd7cddfSDavid du Colombier decquoted(char *out, char *in, char *e)
11547dd7cddfSDavid du Colombier {
11557dd7cddfSDavid du Colombier 	char *p, *nl;
11567dd7cddfSDavid du Colombier 
11577dd7cddfSDavid du Colombier 	if(tableqp[' '] == 0)
11587dd7cddfSDavid du Colombier 		initquoted();
11597dd7cddfSDavid du Colombier 
11607dd7cddfSDavid du Colombier 	p = out;
11617dd7cddfSDavid du Colombier 	while((nl = strchr(in, '\n')) != nil && nl < e){
11627dd7cddfSDavid du Colombier 		p = decquotedline(p, in, nl);
11637dd7cddfSDavid du Colombier 		in = nl + 1;
11647dd7cddfSDavid du Colombier 	}
11657dd7cddfSDavid du Colombier 	if(in < e)
11667dd7cddfSDavid du Colombier 		p = decquotedline(p, in, e-1);
11677dd7cddfSDavid du Colombier 
11687dd7cddfSDavid du Colombier 	// make sure we end with a new line
11697dd7cddfSDavid du Colombier 	if(*(p-1) != '\n'){
11707dd7cddfSDavid du Colombier 		*p++ = '\n';
11717dd7cddfSDavid du Colombier 		*p = 0;
11727dd7cddfSDavid du Colombier 	}
11737dd7cddfSDavid du Colombier 
11747dd7cddfSDavid du Colombier 	return p - out;
11757dd7cddfSDavid du Colombier }
11767dd7cddfSDavid du Colombier 
11777dd7cddfSDavid du Colombier static char*
11787dd7cddfSDavid du Colombier lowercase(char *p)
11797dd7cddfSDavid du Colombier {
11807dd7cddfSDavid du Colombier 	char *op;
11817dd7cddfSDavid du Colombier 	int c;
11827dd7cddfSDavid du Colombier 
11837dd7cddfSDavid du Colombier 	for(op = p; c = *p; p++)
11847dd7cddfSDavid du Colombier 		if(isupper(c))
11857dd7cddfSDavid du Colombier 			*p = tolower(c);
11867dd7cddfSDavid du Colombier 	return op;
11877dd7cddfSDavid du Colombier }
11887dd7cddfSDavid du Colombier 
11897dd7cddfSDavid du Colombier /*
11907dd7cddfSDavid du Colombier  *  return number of 8 bit characters
11917dd7cddfSDavid du Colombier  */
11927dd7cddfSDavid du Colombier static int
11937dd7cddfSDavid du Colombier is8bit(Message *m)
11947dd7cddfSDavid du Colombier {
11957dd7cddfSDavid du Colombier 	int count = 0;
11967dd7cddfSDavid du Colombier 	char *p;
11977dd7cddfSDavid du Colombier 
11987dd7cddfSDavid du Colombier 	for(p = m->body; p < m->bend; p++)
11997dd7cddfSDavid du Colombier 		if(*p & 0x80)
12007dd7cddfSDavid du Colombier 			count++;
12017dd7cddfSDavid du Colombier 	return count;
12027dd7cddfSDavid du Colombier }
12037dd7cddfSDavid du Colombier 
12047dd7cddfSDavid du Colombier // translate latin1 directly since it fits neatly in utf
12057dd7cddfSDavid du Colombier int
12067dd7cddfSDavid du Colombier latin1toutf(char *out, char *in, char *e)
12077dd7cddfSDavid du Colombier {
12087dd7cddfSDavid du Colombier 	Rune r;
12097dd7cddfSDavid du Colombier 	char *p;
12107dd7cddfSDavid du Colombier 
12117dd7cddfSDavid du Colombier 	p = out;
12127dd7cddfSDavid du Colombier 	for(; in < e; in++){
12137dd7cddfSDavid du Colombier 		r = (*in) & 0xff;
12147dd7cddfSDavid du Colombier 		p += runetochar(p, &r);
12157dd7cddfSDavid du Colombier 	}
12167dd7cddfSDavid du Colombier 	*p = 0;
12177dd7cddfSDavid du Colombier 	return p - out;
12187dd7cddfSDavid du Colombier }
12197dd7cddfSDavid du Colombier 
12207dd7cddfSDavid du Colombier // translate any thing else using the tcs program
12217dd7cddfSDavid du Colombier int
12227dd7cddfSDavid du Colombier xtoutf(char *charset, char **out, char *in, char *e)
12237dd7cddfSDavid du Colombier {
12247dd7cddfSDavid du Colombier 	char *av[4];
12257dd7cddfSDavid du Colombier 	int totcs[2];
12267dd7cddfSDavid du Colombier 	int fromtcs[2];
12277dd7cddfSDavid du Colombier 	int n, len, sofar;
12287dd7cddfSDavid du Colombier 	char *p;
12297dd7cddfSDavid du Colombier 
12307dd7cddfSDavid du Colombier 	len = e-in+1;
12317dd7cddfSDavid du Colombier 	sofar = 0;
12327dd7cddfSDavid du Colombier 	*out = p = malloc(len+1);
12337dd7cddfSDavid du Colombier 	if(p == nil)
12347dd7cddfSDavid du Colombier 		return 0;
12357dd7cddfSDavid du Colombier 
12367dd7cddfSDavid du Colombier 	av[0] = charset;
12377dd7cddfSDavid du Colombier 	av[1] = "-f";
12387dd7cddfSDavid du Colombier 	av[2] = charset;
12397dd7cddfSDavid du Colombier 	av[3] = 0;
12407dd7cddfSDavid du Colombier 	if(pipe(totcs) < 0)
12417dd7cddfSDavid du Colombier 		return 0;
12427dd7cddfSDavid du Colombier 	if(pipe(fromtcs) < 0){
12437dd7cddfSDavid du Colombier 		close(totcs[0]); close(totcs[1]);
12447dd7cddfSDavid du Colombier 		return 0;
12457dd7cddfSDavid du Colombier 	}
12467dd7cddfSDavid du Colombier 	switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
12477dd7cddfSDavid du Colombier 	case -1:
12487dd7cddfSDavid du Colombier 		close(fromtcs[0]); close(fromtcs[1]);
12497dd7cddfSDavid du Colombier 		close(totcs[0]); close(totcs[1]);
12507dd7cddfSDavid du Colombier 		return 0;
12517dd7cddfSDavid du Colombier 	case 0:
12527dd7cddfSDavid du Colombier 		close(fromtcs[0]); close(totcs[1]);
12537dd7cddfSDavid du Colombier 		dup(fromtcs[1], 1);
12547dd7cddfSDavid du Colombier 		dup(totcs[0], 0);
12557dd7cddfSDavid du Colombier 		close(fromtcs[1]); close(totcs[0]);
1256*9a747e4fSDavid du Colombier 		dup(open("/dev/null", OWRITE), 2);
12577dd7cddfSDavid du Colombier 		exec("/bin/tcs", av);
12587dd7cddfSDavid du Colombier 		_exits(0);
12597dd7cddfSDavid du Colombier 	default:
12607dd7cddfSDavid du Colombier 		close(fromtcs[1]); close(totcs[0]);
12617dd7cddfSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
12627dd7cddfSDavid du Colombier 		case -1:
12637dd7cddfSDavid du Colombier 			close(fromtcs[0]); close(totcs[1]);
12647dd7cddfSDavid du Colombier 			return 0;
12657dd7cddfSDavid du Colombier 		case 0:
12667dd7cddfSDavid du Colombier 			close(fromtcs[0]);
12677dd7cddfSDavid du Colombier 			while(in < e){
12687dd7cddfSDavid du Colombier 				n = write(totcs[1], in, e-in);
12697dd7cddfSDavid du Colombier 				if(n <= 0)
12707dd7cddfSDavid du Colombier 					break;
12717dd7cddfSDavid du Colombier 				in += n;
12727dd7cddfSDavid du Colombier 			}
12737dd7cddfSDavid du Colombier 			close(totcs[1]);
12747dd7cddfSDavid du Colombier 			_exits(0);
12757dd7cddfSDavid du Colombier 		default:
12767dd7cddfSDavid du Colombier 			close(totcs[1]);
12777dd7cddfSDavid du Colombier 			for(;;){
12787dd7cddfSDavid du Colombier 				n = read(fromtcs[0], &p[sofar], len-sofar);
12797dd7cddfSDavid du Colombier 				if(n <= 0)
12807dd7cddfSDavid du Colombier 					break;
12817dd7cddfSDavid du Colombier 				sofar += n;
12827dd7cddfSDavid du Colombier 				p[sofar] = 0;
12837dd7cddfSDavid du Colombier 				if(sofar == len){
12847dd7cddfSDavid du Colombier 					len += 1024;
12857dd7cddfSDavid du Colombier 					*out = p = realloc(p, len+1);
12867dd7cddfSDavid du Colombier 					if(p == nil)
12877dd7cddfSDavid du Colombier 						return 0;
12887dd7cddfSDavid du Colombier 				}
12897dd7cddfSDavid du Colombier 			}
12907dd7cddfSDavid du Colombier 			close(fromtcs[0]);
12917dd7cddfSDavid du Colombier 			break;
12927dd7cddfSDavid du Colombier 		}
12937dd7cddfSDavid du Colombier 		break;
12947dd7cddfSDavid du Colombier 	}
12957dd7cddfSDavid du Colombier 	return sofar;
12967dd7cddfSDavid du Colombier }
12977dd7cddfSDavid du Colombier 
12987dd7cddfSDavid du Colombier enum {
12997dd7cddfSDavid du Colombier 	Winstart= 0x7f,
13007dd7cddfSDavid du Colombier 	Winend= 0x9f,
13017dd7cddfSDavid du Colombier };
13027dd7cddfSDavid du Colombier 
13037dd7cddfSDavid du Colombier Rune winchars[] = {
13047dd7cddfSDavid du Colombier 	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 	L'˜', L'™', L'š', L'›', L'œ', L'•', L'•', L'Ÿ',
13097dd7cddfSDavid du Colombier };
13107dd7cddfSDavid du Colombier 
13117dd7cddfSDavid du Colombier int
13127dd7cddfSDavid du Colombier windows1257toutf(char *out, char *in, char *e)
13137dd7cddfSDavid du Colombier {
13147dd7cddfSDavid du Colombier 	Rune r;
13157dd7cddfSDavid du Colombier 	char *p;
13167dd7cddfSDavid du Colombier 
13177dd7cddfSDavid du Colombier 	p = out;
13187dd7cddfSDavid du Colombier 	for(; in < e; in++){
13197dd7cddfSDavid du Colombier 		r = (*in) & 0xff;
13207dd7cddfSDavid du Colombier 		if(r >= 0x7f && r <= 0x9f)
13217dd7cddfSDavid du Colombier 			r = winchars[r-0x7f];
13227dd7cddfSDavid du Colombier 		p += runetochar(p, &r);
13237dd7cddfSDavid du Colombier 	}
13247dd7cddfSDavid du Colombier 	*p = 0;
13257dd7cddfSDavid du Colombier 	return p - out;
13267dd7cddfSDavid du Colombier }
13277dd7cddfSDavid du Colombier 
13287dd7cddfSDavid du Colombier void *
13297dd7cddfSDavid du Colombier emalloc(ulong n)
13307dd7cddfSDavid du Colombier {
13317dd7cddfSDavid du Colombier 	void *p;
13327dd7cddfSDavid du Colombier 
13337dd7cddfSDavid du Colombier 	p = mallocz(n, 1);
13347dd7cddfSDavid du Colombier 	if(!p){
133580ee5cbfSDavid du Colombier 		fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
13367dd7cddfSDavid du Colombier 		exits("out of memory");
13377dd7cddfSDavid du Colombier 	}
133880ee5cbfSDavid du Colombier 	setmalloctag(p, getcallerpc(&n));
13397dd7cddfSDavid du Colombier 	return p;
13407dd7cddfSDavid du Colombier }
13417dd7cddfSDavid du Colombier 
13427dd7cddfSDavid du Colombier void *
13437dd7cddfSDavid du Colombier erealloc(void *p, ulong n)
13447dd7cddfSDavid du Colombier {
13457dd7cddfSDavid du Colombier 	p = realloc(p, n);
13467dd7cddfSDavid du Colombier 	if(!p){
134780ee5cbfSDavid du Colombier 		fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
13487dd7cddfSDavid du Colombier 		exits("out of memory");
13497dd7cddfSDavid du Colombier 	}
135080ee5cbfSDavid du Colombier 	setrealloctag(p, getcallerpc(&p));
13517dd7cddfSDavid du Colombier 	return p;
13527dd7cddfSDavid du Colombier }
13537dd7cddfSDavid du Colombier 
13547dd7cddfSDavid du Colombier void
13557dd7cddfSDavid du Colombier mailplumb(Mailbox *mb, Message *m, int delete)
13567dd7cddfSDavid du Colombier {
13577dd7cddfSDavid du Colombier 	Plumbmsg p;
13587dd7cddfSDavid du Colombier 	Plumbattr a[7];
13597dd7cddfSDavid du Colombier 	char buf[256];
13607dd7cddfSDavid du Colombier 	int ai;
13617dd7cddfSDavid du Colombier 	char lenstr[10], *from, *subject, *date;
13627dd7cddfSDavid du Colombier 	static int fd = -1;
13637dd7cddfSDavid du Colombier 
13647dd7cddfSDavid du Colombier 	if(m->subject822 == nil)
13657dd7cddfSDavid du Colombier 		subject = "";
13667dd7cddfSDavid du Colombier 	else
13677dd7cddfSDavid du Colombier 		subject = s_to_c(m->subject822);
13687dd7cddfSDavid du Colombier 
13697dd7cddfSDavid du Colombier 	if(m->from822 != nil)
13707dd7cddfSDavid du Colombier 		from = s_to_c(m->from822);
13717dd7cddfSDavid du Colombier 	else if(m->unixfrom != nil)
13727dd7cddfSDavid du Colombier 		from = s_to_c(m->unixfrom);
13737dd7cddfSDavid du Colombier 	else
13747dd7cddfSDavid du Colombier 		from = "";
13757dd7cddfSDavid du Colombier 
13767dd7cddfSDavid du Colombier 	if(m->unixdate != nil)
13777dd7cddfSDavid du Colombier 		date = s_to_c(m->unixdate);
13787dd7cddfSDavid du Colombier 	else
13797dd7cddfSDavid du Colombier 		date = "";
13807dd7cddfSDavid du Colombier 
13817dd7cddfSDavid du Colombier 	sprint(lenstr, "%ld", m->end-m->start);
13827dd7cddfSDavid du Colombier 
13837dd7cddfSDavid du Colombier 	if(biffing && !delete)
13847dd7cddfSDavid du Colombier 		print("[ %s / %s / %s ]\n", from, subject, lenstr);
13857dd7cddfSDavid du Colombier 
13867dd7cddfSDavid du Colombier 	if(!plumbing)
13877dd7cddfSDavid du Colombier 		return;
13887dd7cddfSDavid du Colombier 
13897dd7cddfSDavid du Colombier 	if(fd < 0)
13907dd7cddfSDavid du Colombier 		fd = plumbopen("send", OWRITE);
13917dd7cddfSDavid du Colombier 	if(fd < 0)
13927dd7cddfSDavid du Colombier 		return;
13937dd7cddfSDavid du Colombier 
13947dd7cddfSDavid du Colombier 	p.src = "mailfs";
13957dd7cddfSDavid du Colombier 	p.dst = "seemail";
13967dd7cddfSDavid du Colombier 	p.wdir = "/mail/fs";
13977dd7cddfSDavid du Colombier 	p.type = "text";
13987dd7cddfSDavid du Colombier 
13997dd7cddfSDavid du Colombier 	ai = 0;
14007dd7cddfSDavid du Colombier 	a[ai].name = "filetype";
14017dd7cddfSDavid du Colombier 	a[ai].value = "mail";
14027dd7cddfSDavid du Colombier 
14037dd7cddfSDavid du Colombier 	a[++ai].name = "sender";
14047dd7cddfSDavid du Colombier 	a[ai].value = from;
14057dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
14067dd7cddfSDavid du Colombier 
14077dd7cddfSDavid du Colombier 	a[++ai].name = "length";
14087dd7cddfSDavid du Colombier 	a[ai].value = lenstr;
14097dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
14107dd7cddfSDavid du Colombier 
14117dd7cddfSDavid du Colombier 	a[++ai].name = "mailtype";
14127dd7cddfSDavid du Colombier 	a[ai].value = delete?"delete":"new";
14137dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
14147dd7cddfSDavid du Colombier 
14157dd7cddfSDavid du Colombier 	a[++ai].name = "date";
14167dd7cddfSDavid du Colombier 	a[ai].value = date;
14177dd7cddfSDavid du Colombier 	a[ai-1].next = &a[ai];
14187dd7cddfSDavid du Colombier 
1419*9a747e4fSDavid du Colombier 	if(m->sdigest){
14207dd7cddfSDavid du Colombier 		a[++ai].name = "digest";
14217dd7cddfSDavid du Colombier 		a[ai].value = s_to_c(m->sdigest);
14227dd7cddfSDavid du Colombier 		a[ai-1].next = &a[ai];
1423*9a747e4fSDavid du Colombier 	}
14247dd7cddfSDavid du Colombier 
14257dd7cddfSDavid du Colombier 	a[ai].next = nil;
14267dd7cddfSDavid du Colombier 
14277dd7cddfSDavid du Colombier 	p.attr = a;
14287dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/%s/%s",
14297dd7cddfSDavid du Colombier 		mntpt, mb->name, m->name);
14307dd7cddfSDavid du Colombier 	p.ndata = strlen(buf);
14317dd7cddfSDavid du Colombier 	p.data = buf;
14327dd7cddfSDavid du Colombier 
14337dd7cddfSDavid du Colombier 	plumbsend(fd, &p);
14347dd7cddfSDavid du Colombier }
14357dd7cddfSDavid du Colombier 
14367dd7cddfSDavid du Colombier //
14377dd7cddfSDavid du Colombier // count the number of lines in the body (for imap4)
14387dd7cddfSDavid du Colombier //
14397dd7cddfSDavid du Colombier void
14407dd7cddfSDavid du Colombier countlines(Message *m)
14417dd7cddfSDavid du Colombier {
14427dd7cddfSDavid du Colombier 	int i;
14437dd7cddfSDavid du Colombier 	char *p;
14447dd7cddfSDavid du Colombier 
14457dd7cddfSDavid du Colombier 	i = 0;
14467dd7cddfSDavid du Colombier 	for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
14477dd7cddfSDavid du Colombier 		i++;
14487dd7cddfSDavid du Colombier 	sprint(m->lines, "%d", i);
14497dd7cddfSDavid du Colombier }
145059cc4ca5SDavid du Colombier 
145159cc4ca5SDavid du Colombier char *LOG = "fs";
145259cc4ca5SDavid du Colombier 
145359cc4ca5SDavid du Colombier void
145459cc4ca5SDavid du Colombier logmsg(char *s, Message *m)
145559cc4ca5SDavid du Colombier {
145659cc4ca5SDavid du Colombier 	int pid;
145759cc4ca5SDavid du Colombier 
145859cc4ca5SDavid du Colombier 	if(!logging)
145959cc4ca5SDavid du Colombier 		return;
146059cc4ca5SDavid du Colombier 	pid = getpid();
146159cc4ca5SDavid du Colombier 	if(m == nil)
146259cc4ca5SDavid du Colombier 		syslog(0, LOG, "%s.%d: %s", user, pid, s);
146359cc4ca5SDavid du Colombier 	else
146459cc4ca5SDavid du Colombier 		syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
146559cc4ca5SDavid du Colombier 			user, pid, s,
146659cc4ca5SDavid du Colombier 			m->from822 ? s_to_c(m->from822) : "?",
146759cc4ca5SDavid du Colombier 			s_to_c(m->sdigest));
146859cc4ca5SDavid du Colombier }
146980ee5cbfSDavid du Colombier 
147080ee5cbfSDavid du Colombier //
147180ee5cbfSDavid du Colombier // convert an RFC822 date into a Unix style date
147280ee5cbfSDavid du Colombier // for when the Unix From line isn't there (e.g. POP3).
147380ee5cbfSDavid du Colombier // enough client programs depend on having a Unix date
147480ee5cbfSDavid du Colombier // that it's easiest to write this conversion code once, right here.
147580ee5cbfSDavid du Colombier //
147680ee5cbfSDavid du Colombier // people don't follow RFC822 particularly closely,
147780ee5cbfSDavid du Colombier // so we use strtotm, which is a bunch of heuristics.
147880ee5cbfSDavid du Colombier //
147980ee5cbfSDavid du Colombier 
148080ee5cbfSDavid du Colombier extern int strtotm(char*, Tm*);
148180ee5cbfSDavid du Colombier String*
148280ee5cbfSDavid du Colombier date822tounix(char *s)
148380ee5cbfSDavid du Colombier {
148480ee5cbfSDavid du Colombier 	char *p, *q;
148580ee5cbfSDavid du Colombier 	Tm tm;
148680ee5cbfSDavid du Colombier 
148780ee5cbfSDavid du Colombier 	if(strtotm(s, &tm) < 0)
148880ee5cbfSDavid du Colombier 		return nil;
148980ee5cbfSDavid du Colombier 
149080ee5cbfSDavid du Colombier 	p = asctime(&tm);
149180ee5cbfSDavid du Colombier 	if(q = strchr(p, '\n'))
149280ee5cbfSDavid du Colombier 		*q = '\0';
149380ee5cbfSDavid du Colombier 	return s_copy(p);
149480ee5cbfSDavid du Colombier }
149580ee5cbfSDavid du Colombier 
1496