xref: /plan9/sys/src/cmd/ip/imap4d/msg.c (revision fed0fa9e146ce97377041b76bdecad7bcff8a240)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier #include <libsec.h>
580ee5cbfSDavid du Colombier #include <auth.h>
680ee5cbfSDavid du Colombier #include <fcall.h>
77dd7cddfSDavid du Colombier #include "imap4d.h"
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier static void	body64(int in, int out);
1080ee5cbfSDavid du Colombier static void	bodystrip(int in, int out);
117dd7cddfSDavid du Colombier static void	cleanupHeader(Header *h);
127dd7cddfSDavid du Colombier static char	*domBang(char *s);
137dd7cddfSDavid du Colombier static void	freeMAddr(MAddr *a);
147dd7cddfSDavid du Colombier static void	freeMimeHdr(MimeHdr *mh);
157dd7cddfSDavid du Colombier static char	*headAddrSpec(char *e, char *w);
167dd7cddfSDavid du Colombier static MAddr	*headAddresses(void);
177dd7cddfSDavid du Colombier static MAddr	*headAddress(void);
187dd7cddfSDavid du Colombier static char	*headAtom(char *disallowed);
197dd7cddfSDavid du Colombier static int	headChar(int eat);
207dd7cddfSDavid du Colombier static char	*headDomain(char *e);
217dd7cddfSDavid du Colombier static MAddr	*headMAddr(MAddr *old);
227dd7cddfSDavid du Colombier static char	*headPhrase(char *e, char *w);
237dd7cddfSDavid du Colombier static char	*headQuoted(int start, int stop);
247dd7cddfSDavid du Colombier static char	*headSkipWhite(int);
257dd7cddfSDavid du Colombier static void	headSkip(void);
267dd7cddfSDavid du Colombier static char	*headSubDomain(void);
277dd7cddfSDavid du Colombier static char	*headText(void);
287dd7cddfSDavid du Colombier static void	headToEnd(void);
297dd7cddfSDavid du Colombier static char	*headWord(void);
307dd7cddfSDavid du Colombier static void	mimeDescription(Header *h);
317dd7cddfSDavid du Colombier static void	mimeDisposition(Header *h);
327dd7cddfSDavid du Colombier static void	mimeEncoding(Header *h);
337dd7cddfSDavid du Colombier static void	mimeId(Header *h);
347dd7cddfSDavid du Colombier static void	mimeLanguage(Header *h);
357dd7cddfSDavid du Colombier static void	mimeMd5(Header *h);
367dd7cddfSDavid du Colombier static MimeHdr	*mimeParams(void);
377dd7cddfSDavid du Colombier static void	mimeType(Header *h);
387dd7cddfSDavid du Colombier static MimeHdr	*mkMimeHdr(char *s, char *t, MimeHdr *next);
397dd7cddfSDavid du Colombier static void	msgAddDate(Msg *m);
407dd7cddfSDavid du Colombier static void	msgAddHead(Msg *m, char *head, char *body);
417dd7cddfSDavid du Colombier static int	msgBodySize(Msg *m);
427dd7cddfSDavid du Colombier static int	msgHeader(Msg *m, Header *h, char *file);
437dd7cddfSDavid du Colombier static long	msgReadFile(Msg *m, char *file, char **ss);
447dd7cddfSDavid du Colombier static int	msgUnix(Msg *m, int top);
457dd7cddfSDavid du Colombier static void	stripQuotes(char *q);
467dd7cddfSDavid du Colombier static MAddr	*unixFrom(char *s);
477dd7cddfSDavid du Colombier 
4880ee5cbfSDavid du Colombier 
4980ee5cbfSDavid du Colombier static char bogusBody[] =
5080ee5cbfSDavid du Colombier 	"This message contains null characters, so it cannot be displayed correctly.\r\n"
5180ee5cbfSDavid du Colombier 	"Most likely you were sent a bogus message or a binary file.\r\n"
5280ee5cbfSDavid du Colombier 	"\r\n"
5380ee5cbfSDavid du Colombier 	"Each of the following attachments has a different version of the message.\r\n"
5480ee5cbfSDavid du Colombier 	"The first is inlined with all non-printable characters stripped.\r\n"
5580ee5cbfSDavid du Colombier 	"The second contains the message as it was stored in your mailbox.\r\n"
5680ee5cbfSDavid du Colombier 	"The third has the initial header stripped.\r\n";
5780ee5cbfSDavid du Colombier 
5880ee5cbfSDavid du Colombier static char bogusMimeText[] =
5980ee5cbfSDavid du Colombier 	"Content-Disposition: inline\r\n"
6080ee5cbfSDavid du Colombier 	"Content-Type: text/plain; charset=\"US-ASCII\"\r\n"
6180ee5cbfSDavid du Colombier 	"Content-Transfer-Encoding: 7bit\r\n";
6280ee5cbfSDavid du Colombier 
6380ee5cbfSDavid du Colombier static char bogusMimeBinary[] =
6480ee5cbfSDavid du Colombier 	"Content-Disposition: attachment\r\n"
6580ee5cbfSDavid du Colombier 	"Content-Type: application/octet-stream\r\n"
6680ee5cbfSDavid du Colombier 	"Content-Transfer-Encoding: base64\r\n";
6780ee5cbfSDavid du Colombier 
687dd7cddfSDavid du Colombier /*
697dd7cddfSDavid du Colombier  * stop list for header fields
707dd7cddfSDavid du Colombier  */
717dd7cddfSDavid du Colombier static char	*headFieldStop = ":";
727dd7cddfSDavid du Colombier static char	*mimeTokenStop = "()<>@,;:\\\"/[]?=";
737dd7cddfSDavid du Colombier static char	*headAtomStop = "()<>@,;:\\\".[]";
747dd7cddfSDavid du Colombier static uchar	*headStr;
757dd7cddfSDavid du Colombier static uchar	*lastWhite;
767dd7cddfSDavid du Colombier 
777dd7cddfSDavid du Colombier long
selectFields(char * dst,long n,char * hdr,SList * fields,int matches)787dd7cddfSDavid du Colombier selectFields(char *dst, long n, char *hdr, SList *fields, int matches)
797dd7cddfSDavid du Colombier {
807dd7cddfSDavid du Colombier 	SList *f;
817dd7cddfSDavid du Colombier 	uchar *start;
827dd7cddfSDavid du Colombier 	char *s;
837dd7cddfSDavid du Colombier 	long m, nf;
847dd7cddfSDavid du Colombier 
857dd7cddfSDavid du Colombier 	headStr = (uchar*)hdr;
867dd7cddfSDavid du Colombier 	m = 0;
877dd7cddfSDavid du Colombier 	for(;;){
887dd7cddfSDavid du Colombier 		start = headStr;
897dd7cddfSDavid du Colombier 		s = headAtom(headFieldStop);
907dd7cddfSDavid du Colombier 		if(s == nil)
917dd7cddfSDavid du Colombier 			break;
927dd7cddfSDavid du Colombier 		headSkip();
937dd7cddfSDavid du Colombier 		for(f = fields; f != nil; f = f->next){
947dd7cddfSDavid du Colombier 			if(cistrcmp(s, f->s) == !matches){
957dd7cddfSDavid du Colombier 				nf = headStr - start;
967dd7cddfSDavid du Colombier 				if(m + nf > n)
977dd7cddfSDavid du Colombier 					return 0;
987dd7cddfSDavid du Colombier 				memmove(&dst[m], start, nf);
997dd7cddfSDavid du Colombier 				m += nf;
1007dd7cddfSDavid du Colombier 			}
1017dd7cddfSDavid du Colombier 		}
1027dd7cddfSDavid du Colombier 		free(s);
1037dd7cddfSDavid du Colombier 	}
1047dd7cddfSDavid du Colombier 	if(m + 3 > n)
1057dd7cddfSDavid du Colombier 		return 0;
1067dd7cddfSDavid du Colombier 	dst[m++] = '\r';
1077dd7cddfSDavid du Colombier 	dst[m++] = '\n';
1087dd7cddfSDavid du Colombier 	dst[m] = '\0';
1097dd7cddfSDavid du Colombier 	return m;
1107dd7cddfSDavid du Colombier }
1117dd7cddfSDavid du Colombier 
1127dd7cddfSDavid du Colombier void
freeMsg(Msg * m)1137dd7cddfSDavid du Colombier freeMsg(Msg *m)
1147dd7cddfSDavid du Colombier {
1157dd7cddfSDavid du Colombier 	Msg *k, *last;
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier 	free(m->iBuf);
1187dd7cddfSDavid du Colombier 	freeMAddr(m->to);
1197dd7cddfSDavid du Colombier 	if(m->replyTo != m->from)
1207dd7cddfSDavid du Colombier 		freeMAddr(m->replyTo);
1217dd7cddfSDavid du Colombier 	if(m->sender != m->from)
1227dd7cddfSDavid du Colombier 		freeMAddr(m->sender);
1237dd7cddfSDavid du Colombier 	if(m->from != m->unixFrom)
1247dd7cddfSDavid du Colombier 		freeMAddr(m->from);
1257dd7cddfSDavid du Colombier 	freeMAddr(m->unixFrom);
1267dd7cddfSDavid du Colombier 	freeMAddr(m->cc);
1277dd7cddfSDavid du Colombier 	freeMAddr(m->bcc);
1287dd7cddfSDavid du Colombier 	free(m->unixDate);
1297dd7cddfSDavid du Colombier 	cleanupHeader(&m->head);
1307dd7cddfSDavid du Colombier 	cleanupHeader(&m->mime);
1317dd7cddfSDavid du Colombier 	for(k = m->kids; k != nil; ){
1327dd7cddfSDavid du Colombier 		last = k;
1337dd7cddfSDavid du Colombier 		k = k->next;
1347dd7cddfSDavid du Colombier 		freeMsg(last);
1357dd7cddfSDavid du Colombier 	}
1366852fd5aSDavid du Colombier 	free(m->fs);
1377dd7cddfSDavid du Colombier 	free(m);
1387dd7cddfSDavid du Colombier }
1397dd7cddfSDavid du Colombier 
1407dd7cddfSDavid du Colombier ulong
msgSize(Msg * m)1417dd7cddfSDavid du Colombier msgSize(Msg *m)
1427dd7cddfSDavid du Colombier {
1437dd7cddfSDavid du Colombier 	return m->head.size + m->size;
1447dd7cddfSDavid du Colombier }
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier int
infoIsNil(char * s)1477dd7cddfSDavid du Colombier infoIsNil(char *s)
1487dd7cddfSDavid du Colombier {
1497dd7cddfSDavid du Colombier 	return s == nil || s[0] == '\0';
1507dd7cddfSDavid du Colombier }
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier char*
maddrStr(MAddr * a)1537dd7cddfSDavid du Colombier maddrStr(MAddr *a)
1547dd7cddfSDavid du Colombier {
1557dd7cddfSDavid du Colombier 	char *host, *addr;
1567dd7cddfSDavid du Colombier 	int n;
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 	host = a->host;
1597dd7cddfSDavid du Colombier 	if(host == nil)
1607dd7cddfSDavid du Colombier 		host = "";
1617dd7cddfSDavid du Colombier 	n = strlen(a->box) + strlen(host) + 2;
1627dd7cddfSDavid du Colombier 	if(a->personal != nil)
1637dd7cddfSDavid du Colombier 		n += strlen(a->personal) + 3;
1647dd7cddfSDavid du Colombier 	addr = emalloc(n);
1657dd7cddfSDavid du Colombier 	if(a->personal != nil)
1667dd7cddfSDavid du Colombier 		snprint(addr, n, "%s <%s@%s>", a->personal, a->box, host);
1677dd7cddfSDavid du Colombier 	else
1687dd7cddfSDavid du Colombier 		snprint(addr, n, "%s@%s", a->box, host);
1697dd7cddfSDavid du Colombier 	return addr;
1707dd7cddfSDavid du Colombier }
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier /*
1737dd7cddfSDavid du Colombier  * return actual name of f in m's fs directory
1747dd7cddfSDavid du Colombier  * this is special cased when opening m/rawbody, m/mimeheader, or m/rawheader,
1757dd7cddfSDavid du Colombier  * if the message was corrupted.  in that case,
1767dd7cddfSDavid du Colombier  * a temporary file is made to hold the base64 encoding of m/raw.
1777dd7cddfSDavid du Colombier  */
1787dd7cddfSDavid du Colombier int
msgFile(Msg * m,char * f)1797dd7cddfSDavid du Colombier msgFile(Msg *m, char *f)
1807dd7cddfSDavid du Colombier {
18180ee5cbfSDavid du Colombier 	Msg *parent, *p;
18280ee5cbfSDavid du Colombier 	Dir d;
1837dd7cddfSDavid du Colombier 	Tm tm;
1849a747e4fSDavid du Colombier 	char buf[64], nbuf[2];
1859a747e4fSDavid du Colombier 	uchar dbuf[64];
18680ee5cbfSDavid du Colombier 	int i, n, fd, fd1, fd2;
1877dd7cddfSDavid du Colombier 
1887dd7cddfSDavid du Colombier 	if(!m->bogus
18980ee5cbfSDavid du Colombier 	|| strcmp(f, "") != 0 && strcmp(f, "rawbody") != 0
19080ee5cbfSDavid du Colombier 	&& strcmp(f, "rawheader") != 0 && strcmp(f, "mimeheader") != 0
1913ff48bf5SDavid du Colombier 	&& strcmp(f, "info") != 0 && strcmp(f, "unixheader") != 0){
1929a747e4fSDavid du Colombier 		if(strlen(f) > MsgNameLen)
1937dd7cddfSDavid du Colombier 			bye("internal error: msgFile name too long");
1947dd7cddfSDavid du Colombier 		strcpy(m->efs, f);
1957dd7cddfSDavid du Colombier 		return cdOpen(m->fsDir, m->fs, OREAD);
1967dd7cddfSDavid du Colombier 	}
19780ee5cbfSDavid du Colombier 
19880ee5cbfSDavid du Colombier 	/*
19980ee5cbfSDavid du Colombier 	 * walk up the stupid runt message parts for non-multipart messages
20080ee5cbfSDavid du Colombier 	 */
20180ee5cbfSDavid du Colombier 	parent = m->parent;
20280ee5cbfSDavid du Colombier 	if(parent != nil && parent->parent != nil){
20380ee5cbfSDavid du Colombier 		m = parent;
20480ee5cbfSDavid du Colombier 		parent = m->parent;
20580ee5cbfSDavid du Colombier 	}
20680ee5cbfSDavid du Colombier 	p = m;
20780ee5cbfSDavid du Colombier 	if(parent != nil)
20880ee5cbfSDavid du Colombier 		p = parent;
20980ee5cbfSDavid du Colombier 
2103ff48bf5SDavid du Colombier 	if(strcmp(f, "info") == 0 || strcmp(f, "unixheader") == 0){
21180ee5cbfSDavid du Colombier 		strcpy(p->efs, f);
21280ee5cbfSDavid du Colombier 		return cdOpen(p->fsDir, p->fs, OREAD);
21380ee5cbfSDavid du Colombier 	}
21480ee5cbfSDavid du Colombier 
2157dd7cddfSDavid du Colombier 	fd = imapTmp();
2167dd7cddfSDavid du Colombier 	if(fd < 0)
2177dd7cddfSDavid du Colombier 		return -1;
21880ee5cbfSDavid du Colombier 
21980ee5cbfSDavid du Colombier 	/*
22080ee5cbfSDavid du Colombier 	 * craft the message parts for bogus messages
22180ee5cbfSDavid du Colombier 	 */
22280ee5cbfSDavid du Colombier 	if(strcmp(f, "") == 0){
22380ee5cbfSDavid du Colombier 		/*
22480ee5cbfSDavid du Colombier 		 * make a fake directory for each kid
22580ee5cbfSDavid du Colombier 		 * all we care about is the name
22680ee5cbfSDavid du Colombier 		 */
22780ee5cbfSDavid du Colombier 		if(parent == nil){
2289a747e4fSDavid du Colombier 			nulldir(&d);
2299a747e4fSDavid du Colombier 			d.mode = DMDIR|0600;
2309a747e4fSDavid du Colombier 			d.qid.type = QTDIR;
2319a747e4fSDavid du Colombier 			d.name = nbuf;
2329a747e4fSDavid du Colombier 			nbuf[1] = '\0';
23380ee5cbfSDavid du Colombier 			for(i = '1'; i <= '4'; i++){
2349a747e4fSDavid du Colombier 				nbuf[0] = i;
2359a747e4fSDavid du Colombier 				n = convD2M(&d, dbuf, sizeof(dbuf));
2369a747e4fSDavid du Colombier 				if(n <= BIT16SZ)
23780ee5cbfSDavid du Colombier 					fprint(2, "bad convD2M %d\n", n);
23880ee5cbfSDavid du Colombier 				write(fd, dbuf, n);
23980ee5cbfSDavid du Colombier 			}
24080ee5cbfSDavid du Colombier 		}
24180ee5cbfSDavid du Colombier 	}else if(strcmp(f, "mimeheader") == 0){
24280ee5cbfSDavid du Colombier 		if(parent != nil){
24380ee5cbfSDavid du Colombier 			switch(m->id){
24480ee5cbfSDavid du Colombier 			case 1:
24580ee5cbfSDavid du Colombier 			case 2:
24680ee5cbfSDavid du Colombier 				fprint(fd, "%s", bogusMimeText);
24780ee5cbfSDavid du Colombier 				break;
24880ee5cbfSDavid du Colombier 			case 3:
24980ee5cbfSDavid du Colombier 			case 4:
25080ee5cbfSDavid du Colombier 				fprint(fd, "%s", bogusMimeBinary);
25180ee5cbfSDavid du Colombier 				break;
25280ee5cbfSDavid du Colombier 			}
25380ee5cbfSDavid du Colombier 		}
25480ee5cbfSDavid du Colombier 	}else if(strcmp(f, "rawheader") == 0){
25580ee5cbfSDavid du Colombier 		if(parent == nil){
2567dd7cddfSDavid du Colombier 			date2tm(&tm, m->unixDate);
2577dd7cddfSDavid du Colombier 			rfc822date(buf, sizeof(buf), &tm);
25880ee5cbfSDavid du Colombier 			fprint(fd,
25980ee5cbfSDavid du Colombier 				"Date: %s\r\n"
26080ee5cbfSDavid du Colombier 				"From: imap4 daemon <%s@%s>\r\n"
26180ee5cbfSDavid du Colombier 				"To: <%s@%s>\r\n"
26280ee5cbfSDavid du Colombier 				"Subject: This message was illegal or corrupted\r\n"
26380ee5cbfSDavid du Colombier 				"MIME-Version: 1.0\r\n"
26480ee5cbfSDavid du Colombier 				"Content-Type: multipart/mixed;\r\n\tboundary=\"upas-%s\"\r\n",
26580ee5cbfSDavid du Colombier 					buf, username, site, username, site, m->info[IDigest]);
26680ee5cbfSDavid du Colombier 		}
26780ee5cbfSDavid du Colombier 	}else if(strcmp(f, "rawbody") == 0){
26880ee5cbfSDavid du Colombier 		fd1 = msgFile(p, "raw");
26980ee5cbfSDavid du Colombier 		strcpy(p->efs, "rawbody");
27080ee5cbfSDavid du Colombier 		fd2 = cdOpen(p->fsDir, p->fs, OREAD);
27180ee5cbfSDavid du Colombier 		if(fd1 < 0 || fd2 < 0){
2727dd7cddfSDavid du Colombier 			close(fd);
27380ee5cbfSDavid du Colombier 			close(fd1);
27480ee5cbfSDavid du Colombier 			close(fd2);
2757dd7cddfSDavid du Colombier 			return -1;
2767dd7cddfSDavid du Colombier 		}
27780ee5cbfSDavid du Colombier 		if(parent == nil){
27880ee5cbfSDavid du Colombier 			fprint(fd,
27980ee5cbfSDavid du Colombier 				"This is a multi-part message in MIME format.\r\n"
28080ee5cbfSDavid du Colombier 				"--upas-%s\r\n"
28180ee5cbfSDavid du Colombier 				"%s"
28280ee5cbfSDavid du Colombier 				"\r\n"
28380ee5cbfSDavid du Colombier 				"%s"
28480ee5cbfSDavid du Colombier 				"\r\n",
28580ee5cbfSDavid du Colombier 					m->info[IDigest], bogusMimeText, bogusBody);
28680ee5cbfSDavid du Colombier 
28780ee5cbfSDavid du Colombier 			fprint(fd,
28880ee5cbfSDavid du Colombier 				"--upas-%s\r\n"
28980ee5cbfSDavid du Colombier 				"%s"
29080ee5cbfSDavid du Colombier 				"\r\n",
29180ee5cbfSDavid du Colombier 					m->info[IDigest], bogusMimeText);
29280ee5cbfSDavid du Colombier 			bodystrip(fd1, fd);
29380ee5cbfSDavid du Colombier 
29480ee5cbfSDavid du Colombier 			fprint(fd,
29580ee5cbfSDavid du Colombier 				"--upas-%s\r\n"
29680ee5cbfSDavid du Colombier 				"%s"
29780ee5cbfSDavid du Colombier 				"\r\n",
29880ee5cbfSDavid du Colombier 					m->info[IDigest], bogusMimeBinary);
29980ee5cbfSDavid du Colombier 			seek(fd1, 0, 0);
3007dd7cddfSDavid du Colombier 			body64(fd1, fd);
30180ee5cbfSDavid du Colombier 
30280ee5cbfSDavid du Colombier 			fprint(fd,
30380ee5cbfSDavid du Colombier 				"--upas-%s\r\n"
30480ee5cbfSDavid du Colombier 				"%s"
30580ee5cbfSDavid du Colombier 				"\r\n",
30680ee5cbfSDavid du Colombier 					m->info[IDigest], bogusMimeBinary);
30780ee5cbfSDavid du Colombier 			body64(fd2, fd);
30880ee5cbfSDavid du Colombier 
30980ee5cbfSDavid du Colombier 			fprint(fd, "--upas-%s--\r\n", m->info[IDigest]);
31080ee5cbfSDavid du Colombier 		}else{
31180ee5cbfSDavid du Colombier 			switch(m->id){
31280ee5cbfSDavid du Colombier 			case 1:
31380ee5cbfSDavid du Colombier 				fprint(fd, "%s", bogusBody);
31480ee5cbfSDavid du Colombier 				break;
31580ee5cbfSDavid du Colombier 			case 2:
31680ee5cbfSDavid du Colombier 				bodystrip(fd1, fd);
31780ee5cbfSDavid du Colombier 				break;
31880ee5cbfSDavid du Colombier 			case 3:
31980ee5cbfSDavid du Colombier 				body64(fd1, fd);
32080ee5cbfSDavid du Colombier 				break;
32180ee5cbfSDavid du Colombier 			case 4:
32280ee5cbfSDavid du Colombier 				body64(fd2, fd);
32380ee5cbfSDavid du Colombier 				break;
32480ee5cbfSDavid du Colombier 			}
32580ee5cbfSDavid du Colombier 		}
3267dd7cddfSDavid du Colombier 		close(fd1);
32780ee5cbfSDavid du Colombier 		close(fd2);
3287dd7cddfSDavid du Colombier 	}
3297dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
3307dd7cddfSDavid du Colombier 	return fd;
3317dd7cddfSDavid du Colombier }
3327dd7cddfSDavid du Colombier 
3337dd7cddfSDavid du Colombier int
msgIsMulti(Header * h)3347dd7cddfSDavid du Colombier msgIsMulti(Header *h)
3357dd7cddfSDavid du Colombier {
3367dd7cddfSDavid du Colombier 	return h->type != nil && cistrcmp("multipart", h->type->s) == 0;
3377dd7cddfSDavid du Colombier }
3387dd7cddfSDavid du Colombier 
3397dd7cddfSDavid du Colombier int
msgIsRfc822(Header * h)3407dd7cddfSDavid du Colombier msgIsRfc822(Header *h)
3417dd7cddfSDavid du Colombier {
3427dd7cddfSDavid du Colombier 	return h->type != nil && cistrcmp("message", h->type->s) == 0 && cistrcmp("rfc822", h->type->t) == 0;
3437dd7cddfSDavid du Colombier }
3447dd7cddfSDavid du Colombier 
3457dd7cddfSDavid du Colombier /*
3467dd7cddfSDavid du Colombier  * check if a message has been deleted by someone else
3477dd7cddfSDavid du Colombier  */
3487dd7cddfSDavid du Colombier void
msgDead(Msg * m)3497dd7cddfSDavid du Colombier msgDead(Msg *m)
3507dd7cddfSDavid du Colombier {
3517dd7cddfSDavid du Colombier 	if(m->expunged)
3527dd7cddfSDavid du Colombier 		return;
3537dd7cddfSDavid du Colombier 	*m->efs = '\0';
3549a747e4fSDavid du Colombier 	if(!cdExists(m->fsDir, m->fs))
3557dd7cddfSDavid du Colombier 		m->expunged = 1;
3567dd7cddfSDavid du Colombier }
3577dd7cddfSDavid du Colombier 
3587dd7cddfSDavid du Colombier /*
3597dd7cddfSDavid du Colombier  * make sure the message has valid associated info
3607dd7cddfSDavid du Colombier  * used for ISubject, IDigest, IInReplyTo, IMessageId.
3617dd7cddfSDavid du Colombier  */
3627dd7cddfSDavid du Colombier int
msgInfo(Msg * m)3637dd7cddfSDavid du Colombier msgInfo(Msg *m)
3647dd7cddfSDavid du Colombier {
3657dd7cddfSDavid du Colombier 	char *s;
3667dd7cddfSDavid du Colombier 	int i;
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier 	if(m->info[0] != nil)
3697dd7cddfSDavid du Colombier 		return 1;
3707dd7cddfSDavid du Colombier 
3717dd7cddfSDavid du Colombier 	i = msgReadFile(m, "info", &m->iBuf);
3727dd7cddfSDavid du Colombier 	if(i < 0)
3737dd7cddfSDavid du Colombier 		return 0;
3747dd7cddfSDavid du Colombier 
3757dd7cddfSDavid du Colombier 	s = m->iBuf;
3767dd7cddfSDavid du Colombier 	for(i = 0; i < IMax; i++){
3777dd7cddfSDavid du Colombier 		m->info[i] = s;
3787dd7cddfSDavid du Colombier 		s = strchr(s, '\n');
3797dd7cddfSDavid du Colombier 		if(s == nil)
3807dd7cddfSDavid du Colombier 			break;
3817dd7cddfSDavid du Colombier 		*s++ = '\0';
3827dd7cddfSDavid du Colombier 	}
3837dd7cddfSDavid du Colombier 	for(; i < IMax; i++)
3847dd7cddfSDavid du Colombier 		m->info[i] = nil;
3857dd7cddfSDavid du Colombier 
3867dd7cddfSDavid du Colombier 	for(i = 0; i < IMax; i++)
3877dd7cddfSDavid du Colombier 		if(infoIsNil(m->info[i]))
3887dd7cddfSDavid du Colombier 			m->info[i] = nil;
3897dd7cddfSDavid du Colombier 
3907dd7cddfSDavid du Colombier 	return 1;
3917dd7cddfSDavid du Colombier }
3927dd7cddfSDavid du Colombier 
3937dd7cddfSDavid du Colombier /*
3947dd7cddfSDavid du Colombier  * make sure the message has valid mime structure
3957dd7cddfSDavid du Colombier  * and sub-messages
3967dd7cddfSDavid du Colombier  */
3977dd7cddfSDavid du Colombier int
msgStruct(Msg * m,int top)3987dd7cddfSDavid du Colombier msgStruct(Msg *m, int top)
3997dd7cddfSDavid du Colombier {
4007dd7cddfSDavid du Colombier 	Msg *k, head, *last;
4019a747e4fSDavid du Colombier 	Dir *d;
4027dd7cddfSDavid du Colombier 	char *s;
4037dd7cddfSDavid du Colombier 	ulong max, id;
4047dd7cddfSDavid du Colombier 	int i, nd, fd, ns;
4057dd7cddfSDavid du Colombier 
4067dd7cddfSDavid du Colombier 	if(m->kids != nil)
4077dd7cddfSDavid du Colombier 		return 1;
4087dd7cddfSDavid du Colombier 
4097dd7cddfSDavid du Colombier 	if(m->expunged
4107dd7cddfSDavid du Colombier 	|| !msgInfo(m)
4117dd7cddfSDavid du Colombier 	|| !msgUnix(m, top)
4127dd7cddfSDavid du Colombier 	|| !msgBodySize(m)
4137dd7cddfSDavid du Colombier 	|| !msgHeader(m, &m->mime, "mimeheader")
4149a747e4fSDavid du Colombier 	|| (top || msgIsRfc822(&m->mime) || msgIsMulti(&m->mime)) && !msgHeader(m, &m->head, "rawheader")){
41580ee5cbfSDavid du Colombier 		if(top && m->bogus && !(m->bogus & BogusTried)){
41680ee5cbfSDavid du Colombier 			m->bogus |= BogusTried;
4177dd7cddfSDavid du Colombier 			return msgStruct(m, top);
4187dd7cddfSDavid du Colombier 		}
4197dd7cddfSDavid du Colombier 		msgDead(m);
4207dd7cddfSDavid du Colombier 		return 0;
4217dd7cddfSDavid du Colombier 	}
4227dd7cddfSDavid du Colombier 
4237dd7cddfSDavid du Colombier 	/*
4247dd7cddfSDavid du Colombier 	 * if a message has no kids, it has a kid which is just the body of the real message
4257dd7cddfSDavid du Colombier 	 */
4269a747e4fSDavid du Colombier 	if(!msgIsMulti(&m->head) && !msgIsMulti(&m->mime) && !msgIsRfc822(&m->head) && !msgIsRfc822(&m->mime)){
4277dd7cddfSDavid du Colombier 		k = MKZ(Msg);
4287dd7cddfSDavid du Colombier 		k->id = 1;
4297dd7cddfSDavid du Colombier 		k->fsDir = m->fsDir;
43080ee5cbfSDavid du Colombier 		k->bogus = m->bogus;
43180ee5cbfSDavid du Colombier 		k->parent = m->parent;
4327dd7cddfSDavid du Colombier 		ns = m->efs - m->fs;
4339a747e4fSDavid du Colombier 		k->fs = emalloc(ns + (MsgNameLen + 1));
4347dd7cddfSDavid du Colombier 		memmove(k->fs, m->fs, ns);
4357dd7cddfSDavid du Colombier 		k->efs = k->fs + ns;
4367dd7cddfSDavid du Colombier 		*k->efs = '\0';
4377dd7cddfSDavid du Colombier 		k->size = m->size;
4387dd7cddfSDavid du Colombier 		m->kids = k;
4397dd7cddfSDavid du Colombier 		return 1;
4407dd7cddfSDavid du Colombier 	}
4417dd7cddfSDavid du Colombier 
4427dd7cddfSDavid du Colombier 	/*
4437dd7cddfSDavid du Colombier 	 * read in all child messages messages
4447dd7cddfSDavid du Colombier 	 */
4457dd7cddfSDavid du Colombier 	fd = msgFile(m, "");
4467dd7cddfSDavid du Colombier 	if(fd < 0){
4477dd7cddfSDavid du Colombier 		msgDead(m);
4487dd7cddfSDavid du Colombier 		return 0;
4497dd7cddfSDavid du Colombier 	}
4507dd7cddfSDavid du Colombier 
4517dd7cddfSDavid du Colombier 	max = 0;
4527dd7cddfSDavid du Colombier 	head.next = nil;
4537dd7cddfSDavid du Colombier 	last = &head;
4549a747e4fSDavid du Colombier 	while((nd = dirread(fd, &d)) > 0){
4557dd7cddfSDavid du Colombier 		for(i = 0; i < nd; i++){
4567dd7cddfSDavid du Colombier 			s = d[i].name;
4577dd7cddfSDavid du Colombier 			id = strtol(s, &s, 10);
4587dd7cddfSDavid du Colombier 			if(id <= max || *s != '\0'
4599a747e4fSDavid du Colombier 			|| (d[i].mode & DMDIR) != DMDIR)
4607dd7cddfSDavid du Colombier 				continue;
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 			max = id;
4637dd7cddfSDavid du Colombier 
4647dd7cddfSDavid du Colombier 			k = MKZ(Msg);
4657dd7cddfSDavid du Colombier 			k->id = id;
4667dd7cddfSDavid du Colombier 			k->fsDir = m->fsDir;
46780ee5cbfSDavid du Colombier 			k->bogus = m->bogus;
46880ee5cbfSDavid du Colombier 			k->parent = m;
4699a747e4fSDavid du Colombier 			ns = strlen(m->fs);
4709a747e4fSDavid du Colombier 			k->fs = emalloc(ns + 2 * (MsgNameLen + 1));
4719a747e4fSDavid du Colombier 			k->efs = seprint(k->fs, k->fs + ns + (MsgNameLen + 1), "%s%lud/", m->fs, id);
4727dd7cddfSDavid du Colombier 			k->prev = last;
4737dd7cddfSDavid du Colombier 			k->size = ~0UL;
4747dd7cddfSDavid du Colombier 			k->lines = ~0UL;
4757dd7cddfSDavid du Colombier 			last->next = k;
4767dd7cddfSDavid du Colombier 			last = k;
4777dd7cddfSDavid du Colombier 		}
4787dd7cddfSDavid du Colombier 	}
4797dd7cddfSDavid du Colombier 	close(fd);
4807dd7cddfSDavid du Colombier 	m->kids = head.next;
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier 	/*
4839a747e4fSDavid du Colombier 	 * if kids fail, just whack them
4847dd7cddfSDavid du Colombier 	 */
4859a747e4fSDavid du Colombier 	top = top && (msgIsRfc822(&m->head) || msgIsMulti(&m->head));
4867dd7cddfSDavid du Colombier 	for(k = m->kids; k != nil; k = k->next){
4877dd7cddfSDavid du Colombier 		if(!msgStruct(k, top)){
4887dd7cddfSDavid du Colombier 			for(k = m->kids; k != nil; ){
4897dd7cddfSDavid du Colombier 				last = k;
4907dd7cddfSDavid du Colombier 				k = k->next;
4917dd7cddfSDavid du Colombier 				freeMsg(last);
4927dd7cddfSDavid du Colombier 			}
4937dd7cddfSDavid du Colombier 			m->kids = nil;
4947dd7cddfSDavid du Colombier 			break;
4957dd7cddfSDavid du Colombier 		}
4967dd7cddfSDavid du Colombier 	}
4977dd7cddfSDavid du Colombier 	return 1;
4987dd7cddfSDavid du Colombier }
4997dd7cddfSDavid du Colombier 
5007dd7cddfSDavid du Colombier static long
msgReadFile(Msg * m,char * file,char ** ss)5017dd7cddfSDavid du Colombier msgReadFile(Msg *m, char *file, char **ss)
5027dd7cddfSDavid du Colombier {
5039a747e4fSDavid du Colombier 	Dir *d;
5047dd7cddfSDavid du Colombier 	char *s, buf[BufSize];
5059a747e4fSDavid du Colombier 	vlong length;
5067dd7cddfSDavid du Colombier 	long n, nn;
5077dd7cddfSDavid du Colombier 	int fd;
5087dd7cddfSDavid du Colombier 
5097dd7cddfSDavid du Colombier 	fd = msgFile(m, file);
5107dd7cddfSDavid du Colombier 	if(fd < 0){
5117dd7cddfSDavid du Colombier 		msgDead(m);
5129a747e4fSDavid du Colombier 		return -1;
5137dd7cddfSDavid du Colombier 	}
5147dd7cddfSDavid du Colombier 
5157dd7cddfSDavid du Colombier 	n = read(fd, buf, BufSize);
5167dd7cddfSDavid du Colombier 	if(n < BufSize){
5177dd7cddfSDavid du Colombier 		close(fd);
5187dd7cddfSDavid du Colombier 		if(n < 0){
5197dd7cddfSDavid du Colombier 			*ss = nil;
5207dd7cddfSDavid du Colombier 			return -1;
5217dd7cddfSDavid du Colombier 		}
5227dd7cddfSDavid du Colombier 		s = emalloc(n + 1);
5237dd7cddfSDavid du Colombier 		memmove(s, buf, n);
5247dd7cddfSDavid du Colombier 		s[n] = '\0';
5257dd7cddfSDavid du Colombier 		*ss = s;
5267dd7cddfSDavid du Colombier 		return n;
5277dd7cddfSDavid du Colombier 	}
5287dd7cddfSDavid du Colombier 
5299a747e4fSDavid du Colombier 	d = dirfstat(fd);
5309a747e4fSDavid du Colombier 	if(d == nil){
5317dd7cddfSDavid du Colombier 		close(fd);
5329a747e4fSDavid du Colombier 		return -1;
5337dd7cddfSDavid du Colombier 	}
5349a747e4fSDavid du Colombier 	length = d->length;
5359a747e4fSDavid du Colombier 	free(d);
5369a747e4fSDavid du Colombier 	nn = length;
5377dd7cddfSDavid du Colombier 	s = emalloc(nn + 1);
5387dd7cddfSDavid du Colombier 	memmove(s, buf, n);
5397dd7cddfSDavid du Colombier 	if(nn > n)
5409a747e4fSDavid du Colombier 		nn = readn(fd, s+n, nn-n) + n;
5417dd7cddfSDavid du Colombier 	close(fd);
5429a747e4fSDavid du Colombier 	if(nn != length){
5437dd7cddfSDavid du Colombier 		free(s);
5449a747e4fSDavid du Colombier 		return -1;
5457dd7cddfSDavid du Colombier 	}
5467dd7cddfSDavid du Colombier 	s[nn] = '\0';
5477dd7cddfSDavid du Colombier 	*ss = s;
5487dd7cddfSDavid du Colombier 	return nn;
5497dd7cddfSDavid du Colombier }
5507dd7cddfSDavid du Colombier 
5517dd7cddfSDavid du Colombier static void
freeMAddr(MAddr * a)5527dd7cddfSDavid du Colombier freeMAddr(MAddr *a)
5537dd7cddfSDavid du Colombier {
5547dd7cddfSDavid du Colombier 	MAddr *p;
5557dd7cddfSDavid du Colombier 
5567dd7cddfSDavid du Colombier 	while(a != nil){
5577dd7cddfSDavid du Colombier 		p = a;
5587dd7cddfSDavid du Colombier 		a = a->next;
5597dd7cddfSDavid du Colombier 		free(p->personal);
5607dd7cddfSDavid du Colombier 		free(p->box);
5617dd7cddfSDavid du Colombier 		free(p->host);
5627dd7cddfSDavid du Colombier 		free(p);
5637dd7cddfSDavid du Colombier 	}
5647dd7cddfSDavid du Colombier }
5657dd7cddfSDavid du Colombier 
5667dd7cddfSDavid du Colombier /*
5677dd7cddfSDavid du Colombier  * the message is corrupted or illegal.
5687dd7cddfSDavid du Colombier  * reset message fields.  msgStruct will reparse the message,
5697dd7cddfSDavid du Colombier  * relying on msgFile to make up corrected body parts.
5707dd7cddfSDavid du Colombier  */
5717dd7cddfSDavid du Colombier static int
msgBogus(Msg * m,int flags)57280ee5cbfSDavid du Colombier msgBogus(Msg *m, int flags)
5737dd7cddfSDavid du Colombier {
57480ee5cbfSDavid du Colombier 	if(!(m->bogus & flags))
57580ee5cbfSDavid du Colombier 		m->bogus |= flags;
5767dd7cddfSDavid du Colombier 	m->lines = ~0;
5777dd7cddfSDavid du Colombier 	free(m->head.buf);
5787dd7cddfSDavid du Colombier 	free(m->mime.buf);
5797dd7cddfSDavid du Colombier 	memset(&m->head, 0, sizeof(Header));
5807dd7cddfSDavid du Colombier 	memset(&m->mime, 0, sizeof(Header));
5817dd7cddfSDavid du Colombier 	return 0;
5827dd7cddfSDavid du Colombier }
5837dd7cddfSDavid du Colombier 
58480ee5cbfSDavid du Colombier /*
5859a747e4fSDavid du Colombier  *  stolen from upas/marshal; base64 encodes from one fd to another.
58680ee5cbfSDavid du Colombier  *
58780ee5cbfSDavid du Colombier  *  the size of buf is very important to enc64.  Anything other than
58880ee5cbfSDavid du Colombier  *  a multiple of 3 will cause enc64 to output a termination sequence.
58980ee5cbfSDavid du Colombier  *  To ensure that a full buf corresponds to a multiple of complete lines,
59080ee5cbfSDavid du Colombier  *  we make buf a multiple of 3*18 since that's how many enc64 sticks on
59180ee5cbfSDavid du Colombier  *  a single line.  This avoids short lines in the output which is pleasing
59280ee5cbfSDavid du Colombier  *  but not necessary.
59380ee5cbfSDavid du Colombier  */
5947dd7cddfSDavid du Colombier static int
enc64x18(char * out,int lim,uchar * in,int n)5957dd7cddfSDavid du Colombier enc64x18(char *out, int lim, uchar *in, int n)
5967dd7cddfSDavid du Colombier {
5977dd7cddfSDavid du Colombier 	int m, mm, nn;
5987dd7cddfSDavid du Colombier 
5997dd7cddfSDavid du Colombier 	nn = 0;
6007dd7cddfSDavid du Colombier 	for(; n > 0; n -= m){
6017dd7cddfSDavid du Colombier 		m = 18 * 3;
6027dd7cddfSDavid du Colombier 		if(m > n)
6037dd7cddfSDavid du Colombier 			m = n;
6047dd7cddfSDavid du Colombier 		mm = enc64(out, lim - nn, in, m);
6057dd7cddfSDavid du Colombier 		in += m;
6067dd7cddfSDavid du Colombier 		out += mm;
6077dd7cddfSDavid du Colombier 		*out++ = '\r';
6087dd7cddfSDavid du Colombier 		*out++ = '\n';
6097dd7cddfSDavid du Colombier 		nn += mm + 2;
6107dd7cddfSDavid du Colombier 	}
6117dd7cddfSDavid du Colombier 	return nn;
6127dd7cddfSDavid du Colombier }
6137dd7cddfSDavid du Colombier 
6147dd7cddfSDavid du Colombier static void
body64(int in,int out)6157dd7cddfSDavid du Colombier body64(int in, int out)
6167dd7cddfSDavid du Colombier {
6177dd7cddfSDavid du Colombier 	uchar buf[3*18*54];
6187dd7cddfSDavid du Colombier 	char obuf[3*18*54*2];
6197dd7cddfSDavid du Colombier 	int m, n;
6207dd7cddfSDavid du Colombier 
6217dd7cddfSDavid du Colombier 	for(;;){
6227dd7cddfSDavid du Colombier 		n = read(in, buf, sizeof(buf));
6237dd7cddfSDavid du Colombier 		if(n < 0)
6247dd7cddfSDavid du Colombier 			return;
6257dd7cddfSDavid du Colombier 		if(n == 0)
6267dd7cddfSDavid du Colombier 			break;
6277dd7cddfSDavid du Colombier 		m = enc64x18(obuf, sizeof(obuf), buf, n);
6287dd7cddfSDavid du Colombier 		if(write(out, obuf, m) < 0)
6297dd7cddfSDavid du Colombier 			return;
6307dd7cddfSDavid du Colombier 	}
6317dd7cddfSDavid du Colombier }
6327dd7cddfSDavid du Colombier 
6337dd7cddfSDavid du Colombier /*
63480ee5cbfSDavid du Colombier  * strip all non-printable characters from a file
63580ee5cbfSDavid du Colombier  */
63680ee5cbfSDavid du Colombier static void
bodystrip(int in,int out)63780ee5cbfSDavid du Colombier bodystrip(int in, int out)
63880ee5cbfSDavid du Colombier {
63980ee5cbfSDavid du Colombier 	uchar buf[3*18*54];
64080ee5cbfSDavid du Colombier 	int m, n, i, c;
64180ee5cbfSDavid du Colombier 
64280ee5cbfSDavid du Colombier 	for(;;){
64380ee5cbfSDavid du Colombier 		n = read(in, buf, sizeof(buf));
64480ee5cbfSDavid du Colombier 		if(n < 0)
64580ee5cbfSDavid du Colombier 			return;
64680ee5cbfSDavid du Colombier 		if(n == 0)
64780ee5cbfSDavid du Colombier 			break;
64880ee5cbfSDavid du Colombier 		m = 0;
64980ee5cbfSDavid du Colombier 		for(i = 0; i < n; i++){
65080ee5cbfSDavid du Colombier 			c = buf[i];
65180ee5cbfSDavid du Colombier 			if(c > 0x1f && c < 0x7f		/* normal characters */
65280ee5cbfSDavid du Colombier 			|| c >= 0x9 && c <= 0xd)	/* \t, \n, vertical tab, form feed, \r */
65380ee5cbfSDavid du Colombier 				buf[m++] = c;
65480ee5cbfSDavid du Colombier 		}
65580ee5cbfSDavid du Colombier 
65680ee5cbfSDavid du Colombier 		if(m && write(out, buf, m) < 0)
65780ee5cbfSDavid du Colombier 			return;
65880ee5cbfSDavid du Colombier 	}
65980ee5cbfSDavid du Colombier }
66080ee5cbfSDavid du Colombier 
66180ee5cbfSDavid du Colombier /*
6629a747e4fSDavid du Colombier  * read in the message body to count \n without a preceding \r
6637dd7cddfSDavid du Colombier  */
6647dd7cddfSDavid du Colombier static int
msgBodySize(Msg * m)6657dd7cddfSDavid du Colombier msgBodySize(Msg *m)
6667dd7cddfSDavid du Colombier {
6679a747e4fSDavid du Colombier 	Dir *d;
6687dd7cddfSDavid du Colombier 	char buf[BufSize + 2], *s, *se;
6699a747e4fSDavid du Colombier 	vlong length;
6707dd7cddfSDavid du Colombier 	ulong size, lines, bad;
6717dd7cddfSDavid du Colombier 	int n, fd, c;
6727dd7cddfSDavid du Colombier 
6737dd7cddfSDavid du Colombier 	if(m->lines != ~0UL)
6747dd7cddfSDavid du Colombier 		return 1;
6757dd7cddfSDavid du Colombier 	fd = msgFile(m, "rawbody");
6767dd7cddfSDavid du Colombier 	if(fd < 0)
6777dd7cddfSDavid du Colombier 		return 0;
6789a747e4fSDavid du Colombier 	d = dirfstat(fd);
6799a747e4fSDavid du Colombier 	if(d == nil){
6807dd7cddfSDavid du Colombier 		close(fd);
6817dd7cddfSDavid du Colombier 		return 0;
6827dd7cddfSDavid du Colombier 	}
6839a747e4fSDavid du Colombier 	length = d->length;
6849a747e4fSDavid du Colombier 	free(d);
6857dd7cddfSDavid du Colombier 
6867dd7cddfSDavid du Colombier 	size = 0;
6877dd7cddfSDavid du Colombier 	lines = 0;
6887dd7cddfSDavid du Colombier 	bad = 0;
6897dd7cddfSDavid du Colombier 	buf[0] = ' ';
6907dd7cddfSDavid du Colombier 	for(;;){
6917dd7cddfSDavid du Colombier 		n = read(fd, &buf[1], BufSize);
6927dd7cddfSDavid du Colombier 		if(n <= 0)
6937dd7cddfSDavid du Colombier 			break;
6947dd7cddfSDavid du Colombier 		size += n;
6957dd7cddfSDavid du Colombier 		se = &buf[n + 1];
6967dd7cddfSDavid du Colombier 		for(s = &buf[1]; s < se; s++){
6977dd7cddfSDavid du Colombier 			c = *s;
6987dd7cddfSDavid du Colombier 			if(c == '\0'){
6997dd7cddfSDavid du Colombier 				close(fd);
70080ee5cbfSDavid du Colombier 				return msgBogus(m, BogusBody);
7017dd7cddfSDavid du Colombier 			}
7027dd7cddfSDavid du Colombier 			if(c != '\n')
7037dd7cddfSDavid du Colombier 				continue;
7047dd7cddfSDavid du Colombier 			if(s[-1] != '\r')
7057dd7cddfSDavid du Colombier 				bad++;
7067dd7cddfSDavid du Colombier 			lines++;
7077dd7cddfSDavid du Colombier 		}
7087dd7cddfSDavid du Colombier 		buf[0] = buf[n];
7097dd7cddfSDavid du Colombier 	}
7109a747e4fSDavid du Colombier 	if(size != length)
7117dd7cddfSDavid du Colombier 		bye("bad length reading rawbody");
7127dd7cddfSDavid du Colombier 	size += bad;
7137dd7cddfSDavid du Colombier 	m->size = size;
7147dd7cddfSDavid du Colombier 	m->lines = lines;
7157dd7cddfSDavid du Colombier 	close(fd);
7167dd7cddfSDavid du Colombier 	return 1;
7177dd7cddfSDavid du Colombier }
7187dd7cddfSDavid du Colombier 
7197dd7cddfSDavid du Colombier /*
7207dd7cddfSDavid du Colombier  * retrieve information from the unixheader file
7217dd7cddfSDavid du Colombier  */
7227dd7cddfSDavid du Colombier static int
msgUnix(Msg * m,int top)7237dd7cddfSDavid du Colombier msgUnix(Msg *m, int top)
7247dd7cddfSDavid du Colombier {
7257dd7cddfSDavid du Colombier 	Tm tm;
7267dd7cddfSDavid du Colombier 	char *s, *ss;
7277dd7cddfSDavid du Colombier 
7287dd7cddfSDavid du Colombier 	if(m->unixDate != nil)
7297dd7cddfSDavid du Colombier 		return 1;
7307dd7cddfSDavid du Colombier 
7317dd7cddfSDavid du Colombier 	if(!top){
732dc5a79c1SDavid du Colombier bogus:
7337dd7cddfSDavid du Colombier 		m->unixDate = estrdup("");
7347dd7cddfSDavid du Colombier 		m->unixFrom = unixFrom(nil);
7357dd7cddfSDavid du Colombier 		return 1;
7367dd7cddfSDavid du Colombier 	}
7377dd7cddfSDavid du Colombier 
7387dd7cddfSDavid du Colombier 	if(msgReadFile(m, "unixheader", &ss) < 0)
7397dd7cddfSDavid du Colombier 		return 0;
7407dd7cddfSDavid du Colombier 	s = ss;
7417dd7cddfSDavid du Colombier 	s = strchr(s, ' ');
7427dd7cddfSDavid du Colombier 	if(s == nil){
7437dd7cddfSDavid du Colombier 		free(ss);
744dc5a79c1SDavid du Colombier 		goto bogus;
7457dd7cddfSDavid du Colombier 	}
7467dd7cddfSDavid du Colombier 	s++;
7477dd7cddfSDavid du Colombier 	m->unixFrom = unixFrom(s);
7487dd7cddfSDavid du Colombier 	s = (char*)headStr;
7497dd7cddfSDavid du Colombier 	if(date2tm(&tm, s) == nil)
7507dd7cddfSDavid du Colombier 		s = m->info[IUnixDate];
7515e91980fSDavid du Colombier 	if(s == nil){
7525e91980fSDavid du Colombier 		free(ss);
7535e91980fSDavid du Colombier 		goto bogus;
7545e91980fSDavid du Colombier 	}
7557dd7cddfSDavid du Colombier 	m->unixDate = estrdup(s);
7567dd7cddfSDavid du Colombier 	free(ss);
7577dd7cddfSDavid du Colombier 	return 1;
7587dd7cddfSDavid du Colombier }
7597dd7cddfSDavid du Colombier 
7607dd7cddfSDavid du Colombier /*
7617dd7cddfSDavid du Colombier  * parse the address in the unix header
7627dd7cddfSDavid du Colombier  * last line of defence, so must return something
7637dd7cddfSDavid du Colombier  */
7647dd7cddfSDavid du Colombier static MAddr *
unixFrom(char * s)7657dd7cddfSDavid du Colombier unixFrom(char *s)
7667dd7cddfSDavid du Colombier {
7677dd7cddfSDavid du Colombier 	MAddr *a;
7687dd7cddfSDavid du Colombier 	char *e, *t;
7697dd7cddfSDavid du Colombier 
7707dd7cddfSDavid du Colombier 	if(s == nil)
771dc5a79c1SDavid du Colombier 		return nil;
7727dd7cddfSDavid du Colombier 	headStr = (uchar*)s;
7737dd7cddfSDavid du Colombier 	t = emalloc(strlen(s) + 2);
7747dd7cddfSDavid du Colombier 	e = headAddrSpec(t, nil);
7757dd7cddfSDavid du Colombier 	if(e == nil)
776dc5a79c1SDavid du Colombier 		a = nil;
7777dd7cddfSDavid du Colombier 	else{
7787dd7cddfSDavid du Colombier 		if(*e != '\0')
7797dd7cddfSDavid du Colombier 			*e++ = '\0';
7807dd7cddfSDavid du Colombier 		else
7817dd7cddfSDavid du Colombier 			e = site;
7827dd7cddfSDavid du Colombier 		a = MKZ(MAddr);
7837dd7cddfSDavid du Colombier 
7847dd7cddfSDavid du Colombier 		a->box = estrdup(t);
7857dd7cddfSDavid du Colombier 		a->host = estrdup(e);
7867dd7cddfSDavid du Colombier 	}
7877dd7cddfSDavid du Colombier 	free(t);
7887dd7cddfSDavid du Colombier 	return a;
7897dd7cddfSDavid du Colombier }
7907dd7cddfSDavid du Colombier 
7917dd7cddfSDavid du Colombier /*
7927dd7cddfSDavid du Colombier  * read in the entire header,
7937dd7cddfSDavid du Colombier  * and parse out any existing mime headers
7947dd7cddfSDavid du Colombier  */
7957dd7cddfSDavid du Colombier static int
msgHeader(Msg * m,Header * h,char * file)7967dd7cddfSDavid du Colombier msgHeader(Msg *m, Header *h, char *file)
7977dd7cddfSDavid du Colombier {
7987dd7cddfSDavid du Colombier 	char *s, *ss, *t, *te;
7997dd7cddfSDavid du Colombier 	ulong lines, n, nn;
8007dd7cddfSDavid du Colombier 	long ns;
8017dd7cddfSDavid du Colombier 	int dated, c;
8027dd7cddfSDavid du Colombier 
8037dd7cddfSDavid du Colombier 	if(h->buf != nil)
8047dd7cddfSDavid du Colombier 		return 1;
8057dd7cddfSDavid du Colombier 
8067dd7cddfSDavid du Colombier 	ns = msgReadFile(m, file, &ss);
8077dd7cddfSDavid du Colombier 	if(ns < 0)
8087dd7cddfSDavid du Colombier 		return 0;
8097dd7cddfSDavid du Colombier 	s = ss;
8107dd7cddfSDavid du Colombier 	n = ns;
8117dd7cddfSDavid du Colombier 
8127dd7cddfSDavid du Colombier 	/*
8137dd7cddfSDavid du Colombier 	 * count lines ending with \n and \r\n
8147dd7cddfSDavid du Colombier 	 * add an extra line at the end, since upas/fs headers
8157dd7cddfSDavid du Colombier 	 * don't have a terminating \r\n
8167dd7cddfSDavid du Colombier 	 */
8177dd7cddfSDavid du Colombier 	lines = 1;
8187dd7cddfSDavid du Colombier 	te = s + ns;
8197dd7cddfSDavid du Colombier 	for(t = s; t < te; t++){
8207dd7cddfSDavid du Colombier 		c = *t;
8217dd7cddfSDavid du Colombier 		if(c == '\0')
82280ee5cbfSDavid du Colombier 			return msgBogus(m, BogusHeader);
8237dd7cddfSDavid du Colombier 		if(c != '\n')
8247dd7cddfSDavid du Colombier 			continue;
8257dd7cddfSDavid du Colombier 		if(t == s || t[-1] != '\r')
8267dd7cddfSDavid du Colombier 			n++;
8277dd7cddfSDavid du Colombier 		lines++;
8287dd7cddfSDavid du Colombier 	}
8297dd7cddfSDavid du Colombier 	if(t > s && t[-1] != '\n'){
8307dd7cddfSDavid du Colombier 		if(t[-1] != '\r')
8317dd7cddfSDavid du Colombier 			n++;
8327dd7cddfSDavid du Colombier 		n++;
8337dd7cddfSDavid du Colombier 	}
8347dd7cddfSDavid du Colombier 	n += 2;
8357dd7cddfSDavid du Colombier 	h->buf = emalloc(n + 1);
8367dd7cddfSDavid du Colombier 	h->size = n;
8377dd7cddfSDavid du Colombier 	h->lines = lines;
8387dd7cddfSDavid du Colombier 
8397dd7cddfSDavid du Colombier 	/*
8407dd7cddfSDavid du Colombier 	 * make sure all headers end in \r\n
8417dd7cddfSDavid du Colombier 	 */
8427dd7cddfSDavid du Colombier 	nn = 0;
8437dd7cddfSDavid du Colombier 	for(t = s; t < te; t++){
8447dd7cddfSDavid du Colombier 		c = *t;
8457dd7cddfSDavid du Colombier 		if(c == '\n'){
8467dd7cddfSDavid du Colombier 			if(!nn || h->buf[nn - 1] != '\r')
8477dd7cddfSDavid du Colombier 				h->buf[nn++] = '\r';
8487dd7cddfSDavid du Colombier 			lines++;
8497dd7cddfSDavid du Colombier 		}
8507dd7cddfSDavid du Colombier 		h->buf[nn++] = c;
8517dd7cddfSDavid du Colombier 	}
8527dd7cddfSDavid du Colombier 	if(nn && h->buf[nn-1] != '\n'){
8537dd7cddfSDavid du Colombier 		if(h->buf[nn-1] != '\r')
8547dd7cddfSDavid du Colombier 			h->buf[nn++] = '\r';
8557dd7cddfSDavid du Colombier 		h->buf[nn++] = '\n';
8567dd7cddfSDavid du Colombier 	}
8577dd7cddfSDavid du Colombier 	h->buf[nn++] = '\r';
8587dd7cddfSDavid du Colombier 	h->buf[nn++] = '\n';
8597dd7cddfSDavid du Colombier 	h->buf[nn] = '\0';
8607dd7cddfSDavid du Colombier 	if(nn != n)
861*fed0fa9eSDavid du Colombier 		bye("misconverted header %ld %ld", nn, n);
8627dd7cddfSDavid du Colombier 	free(s);
8637dd7cddfSDavid du Colombier 
8647dd7cddfSDavid du Colombier 	/*
8657dd7cddfSDavid du Colombier 	 * and parse some mime headers
8667dd7cddfSDavid du Colombier 	 */
8677dd7cddfSDavid du Colombier 	headStr = (uchar*)h->buf;
8687dd7cddfSDavid du Colombier 	dated = 0;
8697dd7cddfSDavid du Colombier 	while(s = headAtom(headFieldStop)){
8707dd7cddfSDavid du Colombier 		if(cistrcmp(s, "content-type") == 0)
8717dd7cddfSDavid du Colombier 			mimeType(h);
8727dd7cddfSDavid du Colombier 		else if(cistrcmp(s, "content-transfer-encoding") == 0)
8737dd7cddfSDavid du Colombier 			mimeEncoding(h);
8747dd7cddfSDavid du Colombier 		else if(cistrcmp(s, "content-id") == 0)
8757dd7cddfSDavid du Colombier 			mimeId(h);
8767dd7cddfSDavid du Colombier 		else if(cistrcmp(s, "content-description") == 0)
8777dd7cddfSDavid du Colombier 			mimeDescription(h);
8787dd7cddfSDavid du Colombier 		else if(cistrcmp(s, "content-disposition") == 0)
8797dd7cddfSDavid du Colombier 			mimeDisposition(h);
8807dd7cddfSDavid du Colombier 		else if(cistrcmp(s, "content-md5") == 0)
8817dd7cddfSDavid du Colombier 			mimeMd5(h);
8827dd7cddfSDavid du Colombier 		else if(cistrcmp(s, "content-language") == 0)
8837dd7cddfSDavid du Colombier 			mimeLanguage(h);
8847dd7cddfSDavid du Colombier 		else if(h == &m->head && cistrcmp(s, "from") == 0)
8857dd7cddfSDavid du Colombier 			m->from = headMAddr(m->from);
8867dd7cddfSDavid du Colombier 		else if(h == &m->head && cistrcmp(s, "to") == 0)
8877dd7cddfSDavid du Colombier 			m->to = headMAddr(m->to);
8887dd7cddfSDavid du Colombier 		else if(h == &m->head && cistrcmp(s, "reply-to") == 0)
8897dd7cddfSDavid du Colombier 			m->replyTo = headMAddr(m->replyTo);
8907dd7cddfSDavid du Colombier 		else if(h == &m->head && cistrcmp(s, "sender") == 0)
8917dd7cddfSDavid du Colombier 			m->sender = headMAddr(m->sender);
8927dd7cddfSDavid du Colombier 		else if(h == &m->head && cistrcmp(s, "cc") == 0)
8937dd7cddfSDavid du Colombier 			m->cc = headMAddr(m->cc);
8947dd7cddfSDavid du Colombier 		else if(h == &m->head && cistrcmp(s, "bcc") == 0)
8957dd7cddfSDavid du Colombier 			m->bcc = headMAddr(m->bcc);
8967dd7cddfSDavid du Colombier 		else if(h == &m->head && cistrcmp(s, "date") == 0)
8977dd7cddfSDavid du Colombier 			dated = 1;
8987dd7cddfSDavid du Colombier 		headSkip();
8997dd7cddfSDavid du Colombier 		free(s);
9007dd7cddfSDavid du Colombier 	}
9017dd7cddfSDavid du Colombier 
9027dd7cddfSDavid du Colombier 	if(h == &m->head){
9037dd7cddfSDavid du Colombier 		if(m->from == nil){
9047dd7cddfSDavid du Colombier 			m->from = m->unixFrom;
905dc5a79c1SDavid du Colombier 			if(m->from != nil){
9067dd7cddfSDavid du Colombier 				s = maddrStr(m->from);
9077dd7cddfSDavid du Colombier 				msgAddHead(m, "From", s);
9087dd7cddfSDavid du Colombier 				free(s);
9097dd7cddfSDavid du Colombier 			}
910dc5a79c1SDavid du Colombier 		}
9117dd7cddfSDavid du Colombier 		if(m->sender == nil)
9127dd7cddfSDavid du Colombier 			m->sender = m->from;
9137dd7cddfSDavid du Colombier 		if(m->replyTo == nil)
9147dd7cddfSDavid du Colombier 			m->replyTo = m->from;
9157dd7cddfSDavid du Colombier 
9167dd7cddfSDavid du Colombier 		if(infoIsNil(m->info[IDate]))
9177dd7cddfSDavid du Colombier 			m->info[IDate] = m->unixDate;
918dc5a79c1SDavid du Colombier 		if(!dated && m->from != nil)
9197dd7cddfSDavid du Colombier 			msgAddDate(m);
9207dd7cddfSDavid du Colombier 	}
9217dd7cddfSDavid du Colombier 	return 1;
9227dd7cddfSDavid du Colombier }
9237dd7cddfSDavid du Colombier 
9247dd7cddfSDavid du Colombier /*
9257dd7cddfSDavid du Colombier  * prepend head: body to the cached header
9267dd7cddfSDavid du Colombier  */
9277dd7cddfSDavid du Colombier static void
msgAddHead(Msg * m,char * head,char * body)9287dd7cddfSDavid du Colombier msgAddHead(Msg *m, char *head, char *body)
9297dd7cddfSDavid du Colombier {
9307dd7cddfSDavid du Colombier 	char *s;
9317dd7cddfSDavid du Colombier 	long size, n;
9327dd7cddfSDavid du Colombier 
9337dd7cddfSDavid du Colombier 	n = strlen(head) + strlen(body) + 4;
9347dd7cddfSDavid du Colombier 	size = m->head.size + n;
9357dd7cddfSDavid du Colombier 	s = emalloc(size + 1);
9367dd7cddfSDavid du Colombier 	snprint(s, size + 1, "%s: %s\r\n%s", head, body, m->head.buf);
9377dd7cddfSDavid du Colombier 	free(m->head.buf);
9387dd7cddfSDavid du Colombier 	m->head.buf = s;
9397dd7cddfSDavid du Colombier 	m->head.size = size;
9407dd7cddfSDavid du Colombier 	m->head.lines++;
9417dd7cddfSDavid du Colombier }
9427dd7cddfSDavid du Colombier 
9437dd7cddfSDavid du Colombier static void
msgAddDate(Msg * m)9447dd7cddfSDavid du Colombier msgAddDate(Msg *m)
9457dd7cddfSDavid du Colombier {
9467dd7cddfSDavid du Colombier 	Tm tm;
9477dd7cddfSDavid du Colombier 	char buf[64];
9487dd7cddfSDavid du Colombier 
949dc5a79c1SDavid du Colombier 	/* don't bother if we don't have a date */
950dc5a79c1SDavid du Colombier 	if(infoIsNil(m->info[IDate]))
951dc5a79c1SDavid du Colombier 		return;
952dc5a79c1SDavid du Colombier 
9537dd7cddfSDavid du Colombier 	date2tm(&tm, m->info[IDate]);
9547dd7cddfSDavid du Colombier 	rfc822date(buf, sizeof(buf), &tm);
9557dd7cddfSDavid du Colombier 	msgAddHead(m, "Date", buf);
9567dd7cddfSDavid du Colombier }
9577dd7cddfSDavid du Colombier 
9587dd7cddfSDavid du Colombier static MimeHdr*
mkMimeHdr(char * s,char * t,MimeHdr * next)9597dd7cddfSDavid du Colombier mkMimeHdr(char *s, char *t, MimeHdr *next)
9607dd7cddfSDavid du Colombier {
9617dd7cddfSDavid du Colombier 	MimeHdr *mh;
9627dd7cddfSDavid du Colombier 
9637dd7cddfSDavid du Colombier 	mh = MK(MimeHdr);
9647dd7cddfSDavid du Colombier 	mh->s = s;
9657dd7cddfSDavid du Colombier 	mh->t = t;
9667dd7cddfSDavid du Colombier 	mh->next = next;
9677dd7cddfSDavid du Colombier 	return mh;
9687dd7cddfSDavid du Colombier }
9697dd7cddfSDavid du Colombier 
9707dd7cddfSDavid du Colombier static void
freeMimeHdr(MimeHdr * mh)9717dd7cddfSDavid du Colombier freeMimeHdr(MimeHdr *mh)
9727dd7cddfSDavid du Colombier {
9737dd7cddfSDavid du Colombier 	MimeHdr *last;
9747dd7cddfSDavid du Colombier 
9757dd7cddfSDavid du Colombier 	while(mh != nil){
9767dd7cddfSDavid du Colombier 		last = mh;
9777dd7cddfSDavid du Colombier 		mh = mh->next;
9787dd7cddfSDavid du Colombier 		free(last->s);
9797dd7cddfSDavid du Colombier 		free(last->t);
9807dd7cddfSDavid du Colombier 		free(last);
9817dd7cddfSDavid du Colombier 	}
9827dd7cddfSDavid du Colombier }
9837dd7cddfSDavid du Colombier 
9847dd7cddfSDavid du Colombier static void
cleanupHeader(Header * h)9857dd7cddfSDavid du Colombier cleanupHeader(Header *h)
9867dd7cddfSDavid du Colombier {
9877dd7cddfSDavid du Colombier 	freeMimeHdr(h->type);
9887dd7cddfSDavid du Colombier 	freeMimeHdr(h->id);
9897dd7cddfSDavid du Colombier 	freeMimeHdr(h->description);
9907dd7cddfSDavid du Colombier 	freeMimeHdr(h->encoding);
9917dd7cddfSDavid du Colombier 	freeMimeHdr(h->md5);
9927dd7cddfSDavid du Colombier 	freeMimeHdr(h->disposition);
9937dd7cddfSDavid du Colombier 	freeMimeHdr(h->language);
9947dd7cddfSDavid du Colombier }
9957dd7cddfSDavid du Colombier 
9967dd7cddfSDavid du Colombier /*
9977dd7cddfSDavid du Colombier  * parser for rfc822 & mime header fields
9987dd7cddfSDavid du Colombier  */
9997dd7cddfSDavid du Colombier 
10007dd7cddfSDavid du Colombier /*
10017dd7cddfSDavid du Colombier  * type		: 'content-type' ':' token '/' token params
10027dd7cddfSDavid du Colombier  */
10037dd7cddfSDavid du Colombier static void
mimeType(Header * h)10047dd7cddfSDavid du Colombier mimeType(Header *h)
10057dd7cddfSDavid du Colombier {
10067dd7cddfSDavid du Colombier 	char *s, *t;
10077dd7cddfSDavid du Colombier 
10087dd7cddfSDavid du Colombier 	if(headChar(1) != ':')
10097dd7cddfSDavid du Colombier 		return;
10107dd7cddfSDavid du Colombier 	s = headAtom(mimeTokenStop);
10117dd7cddfSDavid du Colombier 	if(s == nil || headChar(1) != '/'){
10127dd7cddfSDavid du Colombier 		free(s);
10137dd7cddfSDavid du Colombier 		return;
10147dd7cddfSDavid du Colombier 	}
10157dd7cddfSDavid du Colombier 	t = headAtom(mimeTokenStop);
10167dd7cddfSDavid du Colombier 	if(t == nil){
10177dd7cddfSDavid du Colombier 		free(s);
10187dd7cddfSDavid du Colombier 		return;
10197dd7cddfSDavid du Colombier 	}
10207dd7cddfSDavid du Colombier 	h->type = mkMimeHdr(s, t, mimeParams());
10217dd7cddfSDavid du Colombier }
10227dd7cddfSDavid du Colombier 
10237dd7cddfSDavid du Colombier /*
10247dd7cddfSDavid du Colombier  * params	:
10257dd7cddfSDavid du Colombier  *		| params ';' token '=' token
10267dd7cddfSDavid du Colombier  * 		| params ';' token '=' quoted-str
10277dd7cddfSDavid du Colombier  */
10287dd7cddfSDavid du Colombier static MimeHdr*
mimeParams(void)10297dd7cddfSDavid du Colombier mimeParams(void)
10307dd7cddfSDavid du Colombier {
10317dd7cddfSDavid du Colombier 	MimeHdr head, *last;
10327dd7cddfSDavid du Colombier 	char *s, *t;
10337dd7cddfSDavid du Colombier 
10347dd7cddfSDavid du Colombier 	head.next = nil;
10357dd7cddfSDavid du Colombier 	last = &head;
10367dd7cddfSDavid du Colombier 	for(;;){
10377dd7cddfSDavid du Colombier 		if(headChar(1) != ';')
10387dd7cddfSDavid du Colombier 			break;
10397dd7cddfSDavid du Colombier 		s = headAtom(mimeTokenStop);
10407dd7cddfSDavid du Colombier 		if(s == nil || headChar(1) != '='){
10417dd7cddfSDavid du Colombier 			free(s);
10427dd7cddfSDavid du Colombier 			break;
10437dd7cddfSDavid du Colombier 		}
10447dd7cddfSDavid du Colombier 		if(headChar(0) == '"'){
10457dd7cddfSDavid du Colombier 			t = headQuoted('"', '"');
10467dd7cddfSDavid du Colombier 			stripQuotes(t);
10477dd7cddfSDavid du Colombier 		}else
10487dd7cddfSDavid du Colombier 			t = headAtom(mimeTokenStop);
10497dd7cddfSDavid du Colombier 		if(t == nil){
10507dd7cddfSDavid du Colombier 			free(s);
10517dd7cddfSDavid du Colombier 			break;
10527dd7cddfSDavid du Colombier 		}
10537dd7cddfSDavid du Colombier 		last->next = mkMimeHdr(s, t, nil);
10547dd7cddfSDavid du Colombier 		last = last->next;
10557dd7cddfSDavid du Colombier 	}
10567dd7cddfSDavid du Colombier 	return head.next;
10577dd7cddfSDavid du Colombier }
10587dd7cddfSDavid du Colombier 
10597dd7cddfSDavid du Colombier /*
10607dd7cddfSDavid du Colombier  * encoding	: 'content-transfer-encoding' ':' token
10617dd7cddfSDavid du Colombier  */
10627dd7cddfSDavid du Colombier static void
mimeEncoding(Header * h)10637dd7cddfSDavid du Colombier mimeEncoding(Header *h)
10647dd7cddfSDavid du Colombier {
10657dd7cddfSDavid du Colombier 	char *s;
10667dd7cddfSDavid du Colombier 
10677dd7cddfSDavid du Colombier 	if(headChar(1) != ':')
10687dd7cddfSDavid du Colombier 		return;
10697dd7cddfSDavid du Colombier 	s = headAtom(mimeTokenStop);
10707dd7cddfSDavid du Colombier 	if(s == nil)
10717dd7cddfSDavid du Colombier 		return;
10727dd7cddfSDavid du Colombier 	h->encoding = mkMimeHdr(s, nil, nil);
10737dd7cddfSDavid du Colombier }
10747dd7cddfSDavid du Colombier 
10757dd7cddfSDavid du Colombier /*
10767dd7cddfSDavid du Colombier  * mailaddr	: ':' addresses
10777dd7cddfSDavid du Colombier  */
10787dd7cddfSDavid du Colombier static MAddr*
headMAddr(MAddr * old)10797dd7cddfSDavid du Colombier headMAddr(MAddr *old)
10807dd7cddfSDavid du Colombier {
10817dd7cddfSDavid du Colombier 	MAddr *a;
10827dd7cddfSDavid du Colombier 
10837dd7cddfSDavid du Colombier 	if(headChar(1) != ':')
10847dd7cddfSDavid du Colombier 		return old;
10857dd7cddfSDavid du Colombier 
10867dd7cddfSDavid du Colombier 	if(headChar(0) == '\n')
10877dd7cddfSDavid du Colombier 		return old;
10887dd7cddfSDavid du Colombier 
10897dd7cddfSDavid du Colombier 	a = headAddresses();
10907dd7cddfSDavid du Colombier 	if(a == nil)
10917dd7cddfSDavid du Colombier 		return old;
10927dd7cddfSDavid du Colombier 
10937dd7cddfSDavid du Colombier 	freeMAddr(old);
10947dd7cddfSDavid du Colombier 	return a;
10957dd7cddfSDavid du Colombier }
10967dd7cddfSDavid du Colombier 
10977dd7cddfSDavid du Colombier /*
10987dd7cddfSDavid du Colombier  * addresses	: address | addresses ',' address
10997dd7cddfSDavid du Colombier  */
11007dd7cddfSDavid du Colombier static MAddr*
headAddresses(void)11017dd7cddfSDavid du Colombier headAddresses(void)
11027dd7cddfSDavid du Colombier {
11037dd7cddfSDavid du Colombier 	MAddr *addr, *tail, *a;
11047dd7cddfSDavid du Colombier 
11057dd7cddfSDavid du Colombier 	addr = headAddress();
11067dd7cddfSDavid du Colombier 	if(addr == nil)
11077dd7cddfSDavid du Colombier 		return nil;
11087dd7cddfSDavid du Colombier 	tail = addr;
11097dd7cddfSDavid du Colombier 	while(headChar(0) == ','){
11107dd7cddfSDavid du Colombier 		headChar(1);
11117dd7cddfSDavid du Colombier 		a = headAddress();
11127dd7cddfSDavid du Colombier 		if(a == nil){
11137dd7cddfSDavid du Colombier 			freeMAddr(addr);
11147dd7cddfSDavid du Colombier 			return nil;
11157dd7cddfSDavid du Colombier 		}
11167dd7cddfSDavid du Colombier 		tail->next = a;
11177dd7cddfSDavid du Colombier 		tail = a;
11187dd7cddfSDavid du Colombier 	}
11197dd7cddfSDavid du Colombier 	return addr;
11207dd7cddfSDavid du Colombier }
11217dd7cddfSDavid du Colombier 
11227dd7cddfSDavid du Colombier /*
11237dd7cddfSDavid du Colombier  * address	: mailbox | group
11247dd7cddfSDavid du Colombier  * group	: phrase ':' mboxes ';' | phrase ':' ';'
11257dd7cddfSDavid du Colombier  * mailbox	: addr-spec
11267dd7cddfSDavid du Colombier  *		| optphrase '<' addr-spec '>'
11277dd7cddfSDavid du Colombier  *		| optphrase '<' route ':' addr-spec '>'
11287dd7cddfSDavid du Colombier  * optphrase	: | phrase
11297dd7cddfSDavid du Colombier  * route	: '@' domain
11307dd7cddfSDavid du Colombier  *		| route ',' '@' domain
11317dd7cddfSDavid du Colombier  * personal names are the phrase before '<',
11327dd7cddfSDavid du Colombier  * or a comment before or after a simple addr-spec
11337dd7cddfSDavid du Colombier  */
11347dd7cddfSDavid du Colombier static MAddr*
headAddress(void)11357dd7cddfSDavid du Colombier headAddress(void)
11367dd7cddfSDavid du Colombier {
11377dd7cddfSDavid du Colombier 	MAddr *addr;
11387dd7cddfSDavid du Colombier 	uchar *hs;
11397dd7cddfSDavid du Colombier 	char *s, *e, *w, *personal;
11407dd7cddfSDavid du Colombier 	int c;
11417dd7cddfSDavid du Colombier 
11427dd7cddfSDavid du Colombier 	s = emalloc(strlen((char*)headStr) + 2);
11437dd7cddfSDavid du Colombier 	e = s;
11447dd7cddfSDavid du Colombier 	personal = headSkipWhite(1);
11457dd7cddfSDavid du Colombier 	c = headChar(0);
11467dd7cddfSDavid du Colombier 	if(c == '<')
11477dd7cddfSDavid du Colombier 		w = nil;
11487dd7cddfSDavid du Colombier 	else{
11497dd7cddfSDavid du Colombier 		w = headWord();
11507dd7cddfSDavid du Colombier 		c = headChar(0);
11517dd7cddfSDavid du Colombier 	}
11527dd7cddfSDavid du Colombier 	if(c == '.' || c == '@' || c == ',' || c == '\n' || c == '\0'){
11537dd7cddfSDavid du Colombier 		lastWhite = headStr;
11547dd7cddfSDavid du Colombier 		e = headAddrSpec(s, w);
11557dd7cddfSDavid du Colombier 		if(personal == nil){
11567dd7cddfSDavid du Colombier 			hs = headStr;
11577dd7cddfSDavid du Colombier 			headStr = lastWhite;
11587dd7cddfSDavid du Colombier 			personal = headSkipWhite(1);
11597dd7cddfSDavid du Colombier 			headStr = hs;
11607dd7cddfSDavid du Colombier 		}
11617dd7cddfSDavid du Colombier 	}else{
11627dd7cddfSDavid du Colombier 		if(c != '<' || w != nil){
11637dd7cddfSDavid du Colombier 			free(personal);
11647dd7cddfSDavid du Colombier 			if(!headPhrase(e, w)){
11657dd7cddfSDavid du Colombier 				free(s);
11667dd7cddfSDavid du Colombier 				return nil;
11677dd7cddfSDavid du Colombier 			}
11687dd7cddfSDavid du Colombier 
11697dd7cddfSDavid du Colombier 			/*
11707dd7cddfSDavid du Colombier 			 * ignore addresses with groups,
11717dd7cddfSDavid du Colombier 			 * so the only thing left if <
11727dd7cddfSDavid du Colombier 			 */
11737dd7cddfSDavid du Colombier 			c = headChar(1);
11747dd7cddfSDavid du Colombier 			if(c != '<'){
11757dd7cddfSDavid du Colombier 				free(s);
11767dd7cddfSDavid du Colombier 				return nil;
11777dd7cddfSDavid du Colombier 			}
11787dd7cddfSDavid du Colombier 			personal = estrdup(s);
11797dd7cddfSDavid du Colombier 		}else
11807dd7cddfSDavid du Colombier 			headChar(1);
11817dd7cddfSDavid du Colombier 
11827dd7cddfSDavid du Colombier 		/*
11837dd7cddfSDavid du Colombier 		 * after this point, we need to free personal before returning.
11847dd7cddfSDavid du Colombier 		 * set e to nil to everything afterwards fails.
11857dd7cddfSDavid du Colombier 		 *
11867dd7cddfSDavid du Colombier 		 * ignore routes, they are useless, and heavily discouraged in rfc1123.
11877dd7cddfSDavid du Colombier 		 * imap4 reports them up to, but not including, the terminating :
11887dd7cddfSDavid du Colombier 		 */
11897dd7cddfSDavid du Colombier 		e = s;
11907dd7cddfSDavid du Colombier 		c = headChar(0);
11917dd7cddfSDavid du Colombier 		if(c == '@'){
11927dd7cddfSDavid du Colombier 			for(;;){
11937dd7cddfSDavid du Colombier 				c = headChar(1);
11947dd7cddfSDavid du Colombier 				if(c != '@'){
11957dd7cddfSDavid du Colombier 					e = nil;
11967dd7cddfSDavid du Colombier 					break;
11977dd7cddfSDavid du Colombier 				}
11987dd7cddfSDavid du Colombier 				headDomain(e);
11997dd7cddfSDavid du Colombier 				c = headChar(1);
12007dd7cddfSDavid du Colombier 				if(c != ','){
12017dd7cddfSDavid du Colombier 					e = s;
12027dd7cddfSDavid du Colombier 					break;
12037dd7cddfSDavid du Colombier 				}
12047dd7cddfSDavid du Colombier 			}
12057dd7cddfSDavid du Colombier 			if(c != ':')
12067dd7cddfSDavid du Colombier 				e = nil;
12077dd7cddfSDavid du Colombier 		}
12087dd7cddfSDavid du Colombier 
12097dd7cddfSDavid du Colombier 		if(e != nil)
12107dd7cddfSDavid du Colombier 			e = headAddrSpec(s, nil);
12117dd7cddfSDavid du Colombier 		if(headChar(1) != '>')
12127dd7cddfSDavid du Colombier 			e = nil;
12137dd7cddfSDavid du Colombier 	}
12147dd7cddfSDavid du Colombier 
12157dd7cddfSDavid du Colombier 	/*
12167dd7cddfSDavid du Colombier 	 * e points to @host, or nil if an error occured
12177dd7cddfSDavid du Colombier 	 */
12187dd7cddfSDavid du Colombier 	if(e == nil){
12197dd7cddfSDavid du Colombier 		free(personal);
12207dd7cddfSDavid du Colombier 		addr = nil;
12217dd7cddfSDavid du Colombier 	}else{
12227dd7cddfSDavid du Colombier 		if(*e != '\0')
12237dd7cddfSDavid du Colombier 			*e++ = '\0';
12247dd7cddfSDavid du Colombier 		else
12257dd7cddfSDavid du Colombier 			e = site;
12267dd7cddfSDavid du Colombier 		addr = MKZ(MAddr);
12277dd7cddfSDavid du Colombier 
12287dd7cddfSDavid du Colombier 		addr->personal = personal;
12297dd7cddfSDavid du Colombier 		addr->box = estrdup(s);
12307dd7cddfSDavid du Colombier 		addr->host = estrdup(e);
12317dd7cddfSDavid du Colombier 	}
12327dd7cddfSDavid du Colombier 	free(s);
12337dd7cddfSDavid du Colombier 	return addr;
12347dd7cddfSDavid du Colombier }
12357dd7cddfSDavid du Colombier 
12367dd7cddfSDavid du Colombier /*
12377dd7cddfSDavid du Colombier  * phrase	: word
12387dd7cddfSDavid du Colombier  *		| phrase word
12397dd7cddfSDavid du Colombier  * w is the optional initial word of the phrase
12407dd7cddfSDavid du Colombier  * returns the end of the phrase, or nil if a failure occured
12417dd7cddfSDavid du Colombier  */
12427dd7cddfSDavid du Colombier static char*
headPhrase(char * e,char * w)12437dd7cddfSDavid du Colombier headPhrase(char *e, char *w)
12447dd7cddfSDavid du Colombier {
12457dd7cddfSDavid du Colombier 	int c;
12467dd7cddfSDavid du Colombier 
12477dd7cddfSDavid du Colombier 	for(;;){
12487dd7cddfSDavid du Colombier 		if(w == nil){
12497dd7cddfSDavid du Colombier 			w = headWord();
12507dd7cddfSDavid du Colombier 			if(w == nil)
12517dd7cddfSDavid du Colombier 				return nil;
12527dd7cddfSDavid du Colombier 		}
12537dd7cddfSDavid du Colombier 		if(w[0] == '"')
12547dd7cddfSDavid du Colombier 			stripQuotes(w);
12557dd7cddfSDavid du Colombier 		strcpy(e, w);
12567dd7cddfSDavid du Colombier 		free(w);
12577dd7cddfSDavid du Colombier 		w = nil;
12587dd7cddfSDavid du Colombier 		e = strchr(e, '\0');
12597dd7cddfSDavid du Colombier 		c = headChar(0);
12607dd7cddfSDavid du Colombier 		if(c <= ' ' || strchr(headAtomStop, c) != nil && c != '"')
12617dd7cddfSDavid du Colombier 			break;
12627dd7cddfSDavid du Colombier 		*e++ = ' ';
12637dd7cddfSDavid du Colombier 		*e = '\0';
12647dd7cddfSDavid du Colombier 	}
12657dd7cddfSDavid du Colombier 	return e;
12667dd7cddfSDavid du Colombier }
12677dd7cddfSDavid du Colombier 
12687dd7cddfSDavid du Colombier /*
12697dd7cddfSDavid du Colombier  * addr-spec	: local-part '@' domain
12707dd7cddfSDavid du Colombier  *		| local-part			extension to allow ! and local names
12717dd7cddfSDavid du Colombier  * local-part	: word
12727dd7cddfSDavid du Colombier  *		| local-part '.' word
12737dd7cddfSDavid du Colombier  *
12747dd7cddfSDavid du Colombier  * if no '@' is present, rewrite d!e!f!u as @d,@e:u@f,
12757dd7cddfSDavid du Colombier  * where d, e, f are valid domain components.
12767dd7cddfSDavid du Colombier  * the @d,@e: is ignored, since routes are ignored.
12777dd7cddfSDavid du Colombier  * perhaps they should be rewritten as e!f!u@d, but that is inconsistent with upas.
12787dd7cddfSDavid du Colombier  *
12797dd7cddfSDavid du Colombier  * returns a pointer to '@', the end if none, or nil if there was an error
12807dd7cddfSDavid du Colombier  */
12817dd7cddfSDavid du Colombier static char*
headAddrSpec(char * e,char * w)12827dd7cddfSDavid du Colombier headAddrSpec(char *e, char *w)
12837dd7cddfSDavid du Colombier {
12847dd7cddfSDavid du Colombier 	char *s, *at, *b, *bang, *dom;
12857dd7cddfSDavid du Colombier 	int c;
12867dd7cddfSDavid du Colombier 
12877dd7cddfSDavid du Colombier 	s = e;
12887dd7cddfSDavid du Colombier 	for(;;){
12897dd7cddfSDavid du Colombier 		if(w == nil){
12907dd7cddfSDavid du Colombier 			w = headWord();
12917dd7cddfSDavid du Colombier 			if(w == nil)
12927dd7cddfSDavid du Colombier 				return nil;
12937dd7cddfSDavid du Colombier 		}
12947dd7cddfSDavid du Colombier 		strcpy(e, w);
12957dd7cddfSDavid du Colombier 		free(w);
12967dd7cddfSDavid du Colombier 		w = nil;
12977dd7cddfSDavid du Colombier 		e = strchr(e, '\0');
12987dd7cddfSDavid du Colombier 		lastWhite = headStr;
12997dd7cddfSDavid du Colombier 		c = headChar(0);
13007dd7cddfSDavid du Colombier 		if(c != '.')
13017dd7cddfSDavid du Colombier 			break;
13027dd7cddfSDavid du Colombier 		headChar(1);
13037dd7cddfSDavid du Colombier 		*e++ = '.';
13047dd7cddfSDavid du Colombier 		*e = '\0';
13057dd7cddfSDavid du Colombier 	}
13067dd7cddfSDavid du Colombier 
13077dd7cddfSDavid du Colombier 	if(c != '@'){
13087dd7cddfSDavid du Colombier 		/*
13097dd7cddfSDavid du Colombier 		 * extenstion: allow name without domain
13107dd7cddfSDavid du Colombier 		 * check for domain!xxx
13117dd7cddfSDavid du Colombier 		 */
13127dd7cddfSDavid du Colombier 		bang = domBang(s);
13137dd7cddfSDavid du Colombier 		if(bang == nil)
13147dd7cddfSDavid du Colombier 			return e;
13157dd7cddfSDavid du Colombier 
13167dd7cddfSDavid du Colombier 		/*
13177dd7cddfSDavid du Colombier 		 * if dom1!dom2!xxx, ignore dom1!
13187dd7cddfSDavid du Colombier 		 */
13197dd7cddfSDavid du Colombier 		dom = s;
13207dd7cddfSDavid du Colombier 		for(; b = domBang(bang + 1); bang = b)
13217dd7cddfSDavid du Colombier 			dom = bang + 1;
13227dd7cddfSDavid du Colombier 
13237dd7cddfSDavid du Colombier 		/*
13247dd7cddfSDavid du Colombier 		 * convert dom!mbox into mbox@dom
13257dd7cddfSDavid du Colombier 		 */
13267dd7cddfSDavid du Colombier 		*bang = '@';
13277dd7cddfSDavid du Colombier 		strrev(dom, bang);
13287dd7cddfSDavid du Colombier 		strrev(bang+1, e);
13297dd7cddfSDavid du Colombier 		strrev(dom, e);
13307dd7cddfSDavid du Colombier 		bang = &dom[e - bang - 1];
13317dd7cddfSDavid du Colombier 		if(dom > s){
13327dd7cddfSDavid du Colombier 			bang -= dom - s;
13337dd7cddfSDavid du Colombier 			for(e = s; *e = *dom; e++)
13347dd7cddfSDavid du Colombier 				dom++;
13357dd7cddfSDavid du Colombier 		}
13367dd7cddfSDavid du Colombier 
13377dd7cddfSDavid du Colombier 		/*
13387dd7cddfSDavid du Colombier 		 * eliminate a trailing '.'
13397dd7cddfSDavid du Colombier 		 */
13407dd7cddfSDavid du Colombier 		if(e[-1] == '.')
13417dd7cddfSDavid du Colombier 			e[-1] = '\0';
13427dd7cddfSDavid du Colombier 		return bang;
13437dd7cddfSDavid du Colombier 	}
13447dd7cddfSDavid du Colombier 	headChar(1);
13457dd7cddfSDavid du Colombier 
13467dd7cddfSDavid du Colombier 	at = e;
13477dd7cddfSDavid du Colombier 	*e++ = '@';
13487dd7cddfSDavid du Colombier 	*e = '\0';
13497dd7cddfSDavid du Colombier 	if(!headDomain(e))
13507dd7cddfSDavid du Colombier 		return nil;
13517dd7cddfSDavid du Colombier 	return at;
13527dd7cddfSDavid du Colombier }
13537dd7cddfSDavid du Colombier 
13547dd7cddfSDavid du Colombier /*
13557dd7cddfSDavid du Colombier  * find the ! in domain!rest, where domain must have at least
13569a747e4fSDavid du Colombier  * one internal '.'
13577dd7cddfSDavid du Colombier  */
13587dd7cddfSDavid du Colombier static char*
domBang(char * s)13597dd7cddfSDavid du Colombier domBang(char *s)
13607dd7cddfSDavid du Colombier {
13617dd7cddfSDavid du Colombier 	int dot, c;
13627dd7cddfSDavid du Colombier 
13637dd7cddfSDavid du Colombier 	dot = 0;
13647dd7cddfSDavid du Colombier 	for(; c = *s; s++){
13657dd7cddfSDavid du Colombier 		if(c == '!'){
13667dd7cddfSDavid du Colombier 			if(!dot || dot == 1 && s[-1] == '.' || s[1] == '\0')
13677dd7cddfSDavid du Colombier 				return nil;
13687dd7cddfSDavid du Colombier 			return s;
13697dd7cddfSDavid du Colombier 		}
13707dd7cddfSDavid du Colombier 		if(c == '"')
13717dd7cddfSDavid du Colombier 			break;
13727dd7cddfSDavid du Colombier 		if(c == '.')
13737dd7cddfSDavid du Colombier 			dot++;
13747dd7cddfSDavid du Colombier 	}
13757dd7cddfSDavid du Colombier 	return nil;
13767dd7cddfSDavid du Colombier }
13777dd7cddfSDavid du Colombier 
13787dd7cddfSDavid du Colombier /*
13797dd7cddfSDavid du Colombier  * domain	: sub-domain
13807dd7cddfSDavid du Colombier  *		| domain '.' sub-domain
13817dd7cddfSDavid du Colombier  * returns the end of the domain, or nil if a failure occured
13827dd7cddfSDavid du Colombier  */
13837dd7cddfSDavid du Colombier static char*
headDomain(char * e)13847dd7cddfSDavid du Colombier headDomain(char *e)
13857dd7cddfSDavid du Colombier {
13867dd7cddfSDavid du Colombier 	char *w;
13877dd7cddfSDavid du Colombier 
13887dd7cddfSDavid du Colombier 	for(;;){
13897dd7cddfSDavid du Colombier 		w = headSubDomain();
13907dd7cddfSDavid du Colombier 		if(w == nil)
13917dd7cddfSDavid du Colombier 			return nil;
13927dd7cddfSDavid du Colombier 		strcpy(e, w);
13937dd7cddfSDavid du Colombier 		free(w);
13947dd7cddfSDavid du Colombier 		e = strchr(e, '\0');
13957dd7cddfSDavid du Colombier 		lastWhite = headStr;
13967dd7cddfSDavid du Colombier 		if(headChar(0) != '.')
13977dd7cddfSDavid du Colombier 			break;
13987dd7cddfSDavid du Colombier 		headChar(1);
13997dd7cddfSDavid du Colombier 		*e++ = '.';
14007dd7cddfSDavid du Colombier 		*e = '\0';
14017dd7cddfSDavid du Colombier 	}
14027dd7cddfSDavid du Colombier 	return e;
14037dd7cddfSDavid du Colombier }
14047dd7cddfSDavid du Colombier 
14057dd7cddfSDavid du Colombier /*
14067dd7cddfSDavid du Colombier  * id		: 'content-id' ':' msg-id
14077dd7cddfSDavid du Colombier  * msg-id	: '<' addr-spec '>'
14087dd7cddfSDavid du Colombier  */
14097dd7cddfSDavid du Colombier static void
mimeId(Header * h)14107dd7cddfSDavid du Colombier mimeId(Header *h)
14117dd7cddfSDavid du Colombier {
14127dd7cddfSDavid du Colombier 	char *s, *e, *w;
14137dd7cddfSDavid du Colombier 
14147dd7cddfSDavid du Colombier 	if(headChar(1) != ':')
14157dd7cddfSDavid du Colombier 		return;
14167dd7cddfSDavid du Colombier 	if(headChar(1) != '<')
14177dd7cddfSDavid du Colombier 		return;
14187dd7cddfSDavid du Colombier 
14197dd7cddfSDavid du Colombier 	s = emalloc(strlen((char*)headStr) + 3);
14207dd7cddfSDavid du Colombier 	e = s;
14217dd7cddfSDavid du Colombier 	*e++ = '<';
14227dd7cddfSDavid du Colombier 	e = headAddrSpec(e, nil);
14237dd7cddfSDavid du Colombier 	if(e == nil || headChar(1) != '>'){
14247dd7cddfSDavid du Colombier 		free(s);
14257dd7cddfSDavid du Colombier 		return;
14267dd7cddfSDavid du Colombier 	}
14277dd7cddfSDavid du Colombier 	e = strchr(e, '\0');
14287dd7cddfSDavid du Colombier 	*e++ = '>';
14297dd7cddfSDavid du Colombier 	e[0] = '\0';
14307dd7cddfSDavid du Colombier 	w = strdup(s);
14317dd7cddfSDavid du Colombier 	free(s);
14327dd7cddfSDavid du Colombier 	h->id = mkMimeHdr(w, nil, nil);
14337dd7cddfSDavid du Colombier }
14347dd7cddfSDavid du Colombier 
14357dd7cddfSDavid du Colombier /*
14367dd7cddfSDavid du Colombier  * description	: 'content-description' ':' *text
14377dd7cddfSDavid du Colombier  */
14387dd7cddfSDavid du Colombier static void
mimeDescription(Header * h)14397dd7cddfSDavid du Colombier mimeDescription(Header *h)
14407dd7cddfSDavid du Colombier {
14417dd7cddfSDavid du Colombier 	if(headChar(1) != ':')
14427dd7cddfSDavid du Colombier 		return;
14437dd7cddfSDavid du Colombier 	headSkipWhite(0);
14447dd7cddfSDavid du Colombier 	h->description = mkMimeHdr(headText(), nil, nil);
14457dd7cddfSDavid du Colombier }
14467dd7cddfSDavid du Colombier 
14477dd7cddfSDavid du Colombier /*
14487dd7cddfSDavid du Colombier  * disposition	: 'content-disposition' ':' token params
14497dd7cddfSDavid du Colombier  */
14507dd7cddfSDavid du Colombier static void
mimeDisposition(Header * h)14517dd7cddfSDavid du Colombier mimeDisposition(Header *h)
14527dd7cddfSDavid du Colombier {
14537dd7cddfSDavid du Colombier 	char *s;
14547dd7cddfSDavid du Colombier 
14557dd7cddfSDavid du Colombier 	if(headChar(1) != ':')
14567dd7cddfSDavid du Colombier 		return;
14577dd7cddfSDavid du Colombier 	s = headAtom(mimeTokenStop);
14587dd7cddfSDavid du Colombier 	if(s == nil)
14597dd7cddfSDavid du Colombier 		return;
14607dd7cddfSDavid du Colombier 	h->disposition = mkMimeHdr(s, nil, mimeParams());
14617dd7cddfSDavid du Colombier }
14627dd7cddfSDavid du Colombier 
14637dd7cddfSDavid du Colombier /*
14647dd7cddfSDavid du Colombier  * md5		: 'content-md5' ':' token
14657dd7cddfSDavid du Colombier  */
14667dd7cddfSDavid du Colombier static void
mimeMd5(Header * h)14677dd7cddfSDavid du Colombier mimeMd5(Header *h)
14687dd7cddfSDavid du Colombier {
14697dd7cddfSDavid du Colombier 	char *s;
14707dd7cddfSDavid du Colombier 
14717dd7cddfSDavid du Colombier 	if(headChar(1) != ':')
14727dd7cddfSDavid du Colombier 		return;
14737dd7cddfSDavid du Colombier 	s = headAtom(mimeTokenStop);
14747dd7cddfSDavid du Colombier 	if(s == nil)
14757dd7cddfSDavid du Colombier 		return;
14767dd7cddfSDavid du Colombier 	h->md5 = mkMimeHdr(s, nil, nil);
14777dd7cddfSDavid du Colombier }
14787dd7cddfSDavid du Colombier 
14797dd7cddfSDavid du Colombier /*
14807dd7cddfSDavid du Colombier  * language	: 'content-language' ':' langs
14817dd7cddfSDavid du Colombier  * langs	: token
14827dd7cddfSDavid du Colombier  *		| langs commas token
14837dd7cddfSDavid du Colombier  * commas	: ','
14847dd7cddfSDavid du Colombier  *		| commas ','
14857dd7cddfSDavid du Colombier  */
14867dd7cddfSDavid du Colombier static void
mimeLanguage(Header * h)14877dd7cddfSDavid du Colombier mimeLanguage(Header *h)
14887dd7cddfSDavid du Colombier {
14897dd7cddfSDavid du Colombier 	MimeHdr head, *last;
14907dd7cddfSDavid du Colombier 	char *s;
14917dd7cddfSDavid du Colombier 
14927dd7cddfSDavid du Colombier 	head.next = nil;
14937dd7cddfSDavid du Colombier 	last = &head;
14947dd7cddfSDavid du Colombier 	for(;;){
14957dd7cddfSDavid du Colombier 		s = headAtom(mimeTokenStop);
14967dd7cddfSDavid du Colombier 		if(s == nil)
14977dd7cddfSDavid du Colombier 			break;
14987dd7cddfSDavid du Colombier 		last->next = mkMimeHdr(s, nil, nil);
14997dd7cddfSDavid du Colombier 		last = last->next;
15007dd7cddfSDavid du Colombier 		while(headChar(0) != ',')
15017dd7cddfSDavid du Colombier 			headChar(1);
15027dd7cddfSDavid du Colombier 	}
15037dd7cddfSDavid du Colombier 	h->language = head.next;
15047dd7cddfSDavid du Colombier }
15057dd7cddfSDavid du Colombier 
15067dd7cddfSDavid du Colombier /*
15077dd7cddfSDavid du Colombier  * token	: 1*<char 33-255, except "()<>@,;:\\\"/[]?=" aka mimeTokenStop>
15087dd7cddfSDavid du Colombier  * atom		: 1*<chars 33-255, except "()<>@,;:\\\".[]" aka headAtomStop>
15097dd7cddfSDavid du Colombier  * note this allows 8 bit characters, which occur in utf.
15107dd7cddfSDavid du Colombier  */
15117dd7cddfSDavid du Colombier static char*
headAtom(char * disallowed)15127dd7cddfSDavid du Colombier headAtom(char *disallowed)
15137dd7cddfSDavid du Colombier {
15147dd7cddfSDavid du Colombier 	char *s;
15157dd7cddfSDavid du Colombier 	int c, ns, as;
15167dd7cddfSDavid du Colombier 
15177dd7cddfSDavid du Colombier 	headSkipWhite(0);
15187dd7cddfSDavid du Colombier 
15197dd7cddfSDavid du Colombier 	s = emalloc(StrAlloc);
15207dd7cddfSDavid du Colombier 	as = StrAlloc;
15217dd7cddfSDavid du Colombier 	ns = 0;
15227dd7cddfSDavid du Colombier 	for(;;){
15237dd7cddfSDavid du Colombier 		c = *headStr++;
15247dd7cddfSDavid du Colombier 		if(c <= ' ' || strchr(disallowed, c) != nil){
15257dd7cddfSDavid du Colombier 			headStr--;
15267dd7cddfSDavid du Colombier 			break;
15277dd7cddfSDavid du Colombier 		}
15287dd7cddfSDavid du Colombier 		s[ns++] = c;
15297dd7cddfSDavid du Colombier 		if(ns >= as){
15307dd7cddfSDavid du Colombier 			as += StrAlloc;
15317dd7cddfSDavid du Colombier 			s = erealloc(s, as);
15327dd7cddfSDavid du Colombier 		}
15337dd7cddfSDavid du Colombier 	}
15347dd7cddfSDavid du Colombier 	if(ns == 0){
15357dd7cddfSDavid du Colombier 		free(s);
15367dd7cddfSDavid du Colombier 		return 0;
15377dd7cddfSDavid du Colombier 	}
15387dd7cddfSDavid du Colombier 	s[ns] = '\0';
15397dd7cddfSDavid du Colombier 	return s;
15407dd7cddfSDavid du Colombier }
15417dd7cddfSDavid du Colombier 
15427dd7cddfSDavid du Colombier /*
15437dd7cddfSDavid du Colombier  * sub-domain	: atom | domain-lit
15447dd7cddfSDavid du Colombier  */
15457dd7cddfSDavid du Colombier static char *
headSubDomain(void)15467dd7cddfSDavid du Colombier headSubDomain(void)
15477dd7cddfSDavid du Colombier {
15487dd7cddfSDavid du Colombier 	if(headChar(0) == '[')
15497dd7cddfSDavid du Colombier 		return headQuoted('[', ']');
15507dd7cddfSDavid du Colombier 	return headAtom(headAtomStop);
15517dd7cddfSDavid du Colombier }
15527dd7cddfSDavid du Colombier 
15537dd7cddfSDavid du Colombier /*
15547dd7cddfSDavid du Colombier  * word	: atom | quoted-str
15557dd7cddfSDavid du Colombier  */
15567dd7cddfSDavid du Colombier static char *
headWord(void)15577dd7cddfSDavid du Colombier headWord(void)
15587dd7cddfSDavid du Colombier {
15597dd7cddfSDavid du Colombier 	if(headChar(0) == '"')
15607dd7cddfSDavid du Colombier 		return headQuoted('"', '"');
15617dd7cddfSDavid du Colombier 	return headAtom(headAtomStop);
15627dd7cddfSDavid du Colombier }
15637dd7cddfSDavid du Colombier 
15647dd7cddfSDavid du Colombier /*
15657dd7cddfSDavid du Colombier  * q is a quoted string.  remove enclosing " and and \ escapes
15667dd7cddfSDavid du Colombier  */
15677dd7cddfSDavid du Colombier static void
stripQuotes(char * q)15687dd7cddfSDavid du Colombier stripQuotes(char *q)
15697dd7cddfSDavid du Colombier {
15707dd7cddfSDavid du Colombier 	char *s;
15717dd7cddfSDavid du Colombier 	int c;
15727dd7cddfSDavid du Colombier 
1573223a736eSDavid du Colombier 	if(q == nil)
1574223a736eSDavid du Colombier 		return;
15757dd7cddfSDavid du Colombier 	s = q++;
15767dd7cddfSDavid du Colombier 	while(c = *q++){
15777dd7cddfSDavid du Colombier 		if(c == '\\'){
15787dd7cddfSDavid du Colombier 			c = *q++;
15797dd7cddfSDavid du Colombier 			if(!c)
15807dd7cddfSDavid du Colombier 				return;
15817dd7cddfSDavid du Colombier 		}
15827dd7cddfSDavid du Colombier 		*s++ = c;
15837dd7cddfSDavid du Colombier 	}
15847dd7cddfSDavid du Colombier 	s[-1] = '\0';
15857dd7cddfSDavid du Colombier }
15867dd7cddfSDavid du Colombier 
15877dd7cddfSDavid du Colombier /*
1588223a736eSDavid du Colombier  * quoted-str	: '"' *(any char but '"\\\r', or '\' any char, or linear-white-space) '"'
1589223a736eSDavid du Colombier  * domain-lit	: '[' *(any char but '[]\\\r', or '\' any char, or linear-white-space) ']'
15907dd7cddfSDavid du Colombier  */
15917dd7cddfSDavid du Colombier static char *
headQuoted(int start,int stop)15927dd7cddfSDavid du Colombier headQuoted(int start, int stop)
15937dd7cddfSDavid du Colombier {
15947dd7cddfSDavid du Colombier 	char *s;
15957dd7cddfSDavid du Colombier 	int c, ns, as;
15967dd7cddfSDavid du Colombier 
15977dd7cddfSDavid du Colombier 	if(headChar(1) != start)
15987dd7cddfSDavid du Colombier 		return nil;
15997dd7cddfSDavid du Colombier 	s = emalloc(StrAlloc);
16007dd7cddfSDavid du Colombier 	as = StrAlloc;
16017dd7cddfSDavid du Colombier 	ns = 0;
16027dd7cddfSDavid du Colombier 	s[ns++] = start;
16037dd7cddfSDavid du Colombier 	for(;;){
16047dd7cddfSDavid du Colombier 		c = *headStr;
16057dd7cddfSDavid du Colombier 		if(c == stop){
16067dd7cddfSDavid du Colombier 			headStr++;
16077dd7cddfSDavid du Colombier 			break;
16087dd7cddfSDavid du Colombier 		}
16097dd7cddfSDavid du Colombier 		if(c == '\0'){
16107dd7cddfSDavid du Colombier 			free(s);
16117dd7cddfSDavid du Colombier 			return nil;
16127dd7cddfSDavid du Colombier 		}
1613223a736eSDavid du Colombier 		if(c == '\r'){
1614223a736eSDavid du Colombier 			headStr++;
16157dd7cddfSDavid du Colombier 			continue;
1616223a736eSDavid du Colombier 		}
16177dd7cddfSDavid du Colombier 		if(c == '\n'){
16187dd7cddfSDavid du Colombier 			headStr++;
16197dd7cddfSDavid du Colombier 			while(*headStr == ' ' || *headStr == '\t' || *headStr == '\r' || *headStr == '\n')
16207dd7cddfSDavid du Colombier 				headStr++;
16217dd7cddfSDavid du Colombier 			c = ' ';
16227dd7cddfSDavid du Colombier 		}else if(c == '\\'){
16237dd7cddfSDavid du Colombier 			headStr++;
16247dd7cddfSDavid du Colombier 			s[ns++] = c;
16257dd7cddfSDavid du Colombier 			c = *headStr;
16267dd7cddfSDavid du Colombier 			if(c == '\0'){
16277dd7cddfSDavid du Colombier 				free(s);
16287dd7cddfSDavid du Colombier 				return nil;
16297dd7cddfSDavid du Colombier 			}
16307dd7cddfSDavid du Colombier 			headStr++;
16317dd7cddfSDavid du Colombier 		}else
16327dd7cddfSDavid du Colombier 			headStr++;
16337dd7cddfSDavid du Colombier 		s[ns++] = c;
16347dd7cddfSDavid du Colombier 		if(ns + 1 >= as){	/* leave room for \c or "0 */
16357dd7cddfSDavid du Colombier 			as += StrAlloc;
16367dd7cddfSDavid du Colombier 			s = erealloc(s, as);
16377dd7cddfSDavid du Colombier 		}
16387dd7cddfSDavid du Colombier 	}
16397dd7cddfSDavid du Colombier 	s[ns++] = stop;
16407dd7cddfSDavid du Colombier 	s[ns] = '\0';
16417dd7cddfSDavid du Colombier 	return s;
16427dd7cddfSDavid du Colombier }
16437dd7cddfSDavid du Colombier 
16447dd7cddfSDavid du Colombier /*
16457dd7cddfSDavid du Colombier  * headText	: contents of rest of header line
16467dd7cddfSDavid du Colombier  */
16477dd7cddfSDavid du Colombier static char *
headText(void)16487dd7cddfSDavid du Colombier headText(void)
16497dd7cddfSDavid du Colombier {
16507dd7cddfSDavid du Colombier 	uchar *v;
16517dd7cddfSDavid du Colombier 	char *s;
16527dd7cddfSDavid du Colombier 
16537dd7cddfSDavid du Colombier 	v = headStr;
16547dd7cddfSDavid du Colombier 	headToEnd();
16557dd7cddfSDavid du Colombier 	s = emalloc(headStr - v + 1);
16567dd7cddfSDavid du Colombier 	memmove(s, v, headStr - v);
16577dd7cddfSDavid du Colombier 	s[headStr - v] = '\0';
16587dd7cddfSDavid du Colombier 	return s;
16597dd7cddfSDavid du Colombier }
16607dd7cddfSDavid du Colombier 
16617dd7cddfSDavid du Colombier /*
16627dd7cddfSDavid du Colombier  * white space is ' ' '\t' or nested comments.
16637dd7cddfSDavid du Colombier  * skip white space.
16647dd7cddfSDavid du Colombier  * if com and a comment is seen,
16657dd7cddfSDavid du Colombier  * return it's contents and stop processing white space.
16667dd7cddfSDavid du Colombier  */
16677dd7cddfSDavid du Colombier static char*
headSkipWhite(int com)16687dd7cddfSDavid du Colombier headSkipWhite(int com)
16697dd7cddfSDavid du Colombier {
16707dd7cddfSDavid du Colombier 	char *s;
16717dd7cddfSDavid du Colombier 	int c, incom, as, ns;
16727dd7cddfSDavid du Colombier 
16737dd7cddfSDavid du Colombier 	s = nil;
16747dd7cddfSDavid du Colombier 	as = StrAlloc;
16757dd7cddfSDavid du Colombier 	ns = 0;
16767dd7cddfSDavid du Colombier 	if(com)
16777dd7cddfSDavid du Colombier 		s = emalloc(StrAlloc);
16787dd7cddfSDavid du Colombier 	incom = 0;
16797dd7cddfSDavid du Colombier 	for(; c = *headStr; headStr++){
16807dd7cddfSDavid du Colombier 		switch(c){
16817dd7cddfSDavid du Colombier 		case ' ':
16827dd7cddfSDavid du Colombier 		case '\t':
16837dd7cddfSDavid du Colombier 		case '\r':
16847dd7cddfSDavid du Colombier 			c = ' ';
16857dd7cddfSDavid du Colombier 			break;
16867dd7cddfSDavid du Colombier 		case '\n':
16877dd7cddfSDavid du Colombier 			c = headStr[1];
16887dd7cddfSDavid du Colombier 			if(c != ' ' && c != '\t')
16897dd7cddfSDavid du Colombier 				goto breakout;
16907dd7cddfSDavid du Colombier 			c = ' ';
16917dd7cddfSDavid du Colombier 			break;
16927dd7cddfSDavid du Colombier 		case '\\':
16937dd7cddfSDavid du Colombier 			if(com && incom)
16947dd7cddfSDavid du Colombier 				s[ns++] = c;
16957dd7cddfSDavid du Colombier 			c = headStr[1];
16967dd7cddfSDavid du Colombier 			if(c == '\0')
16977dd7cddfSDavid du Colombier 				goto breakout;
16987dd7cddfSDavid du Colombier 			headStr++;
16997dd7cddfSDavid du Colombier 			break;
17007dd7cddfSDavid du Colombier 		case '(':
17017dd7cddfSDavid du Colombier 			incom++;
17027dd7cddfSDavid du Colombier 			if(incom == 1)
17037dd7cddfSDavid du Colombier 				continue;
17047dd7cddfSDavid du Colombier 			break;
17057dd7cddfSDavid du Colombier 		case ')':
17067dd7cddfSDavid du Colombier 			incom--;
17077dd7cddfSDavid du Colombier 			if(com && !incom){
17087dd7cddfSDavid du Colombier 				s[ns] = '\0';
17097dd7cddfSDavid du Colombier 				return s;
17107dd7cddfSDavid du Colombier 			}
17117dd7cddfSDavid du Colombier 			break;
17127dd7cddfSDavid du Colombier 		default:
17137dd7cddfSDavid du Colombier 			if(!incom)
17147dd7cddfSDavid du Colombier 				goto breakout;
17157dd7cddfSDavid du Colombier 			break;
17167dd7cddfSDavid du Colombier 		}
17177dd7cddfSDavid du Colombier 		if(com && incom && (c != ' ' || ns > 0 && s[ns-1] != ' ')){
17187dd7cddfSDavid du Colombier 			s[ns++] = c;
17197dd7cddfSDavid du Colombier 			if(ns + 1 >= as){	/* leave room for \c or 0 */
17207dd7cddfSDavid du Colombier 				as += StrAlloc;
17217dd7cddfSDavid du Colombier 				s = erealloc(s, as);
17227dd7cddfSDavid du Colombier 			}
17237dd7cddfSDavid du Colombier 		}
17247dd7cddfSDavid du Colombier 	}
17257dd7cddfSDavid du Colombier breakout:;
17267dd7cddfSDavid du Colombier 	free(s);
17277dd7cddfSDavid du Colombier 	return nil;
17287dd7cddfSDavid du Colombier }
17297dd7cddfSDavid du Colombier 
17307dd7cddfSDavid du Colombier /*
17317dd7cddfSDavid du Colombier  * return the next non-white character
17327dd7cddfSDavid du Colombier  */
17337dd7cddfSDavid du Colombier static int
headChar(int eat)17347dd7cddfSDavid du Colombier headChar(int eat)
17357dd7cddfSDavid du Colombier {
17367dd7cddfSDavid du Colombier 	int c;
17377dd7cddfSDavid du Colombier 
17387dd7cddfSDavid du Colombier 	headSkipWhite(0);
17397dd7cddfSDavid du Colombier 	c = *headStr;
17407dd7cddfSDavid du Colombier 	if(eat && c != '\0' && c != '\n')
17417dd7cddfSDavid du Colombier 		headStr++;
17427dd7cddfSDavid du Colombier 	return c;
17437dd7cddfSDavid du Colombier }
17447dd7cddfSDavid du Colombier 
17457dd7cddfSDavid du Colombier static void
headToEnd(void)17467dd7cddfSDavid du Colombier headToEnd(void)
17477dd7cddfSDavid du Colombier {
17487dd7cddfSDavid du Colombier 	uchar *s;
17497dd7cddfSDavid du Colombier 	int c;
17507dd7cddfSDavid du Colombier 
17517dd7cddfSDavid du Colombier 	for(;;){
17527dd7cddfSDavid du Colombier 		s = headStr;
17537dd7cddfSDavid du Colombier 		c = *s++;
17547dd7cddfSDavid du Colombier 		while(c == '\r')
17557dd7cddfSDavid du Colombier 			c = *s++;
17567dd7cddfSDavid du Colombier 		if(c == '\n'){
17577dd7cddfSDavid du Colombier 			c = *s++;
17587dd7cddfSDavid du Colombier 			if(c != ' ' && c != '\t')
17597dd7cddfSDavid du Colombier 				return;
17607dd7cddfSDavid du Colombier 		}
17617dd7cddfSDavid du Colombier 		if(c == '\0')
17627dd7cddfSDavid du Colombier 			return;
17637dd7cddfSDavid du Colombier 		headStr = s;
17647dd7cddfSDavid du Colombier 	}
17657dd7cddfSDavid du Colombier }
17667dd7cddfSDavid du Colombier 
17677dd7cddfSDavid du Colombier static void
headSkip(void)17687dd7cddfSDavid du Colombier headSkip(void)
17697dd7cddfSDavid du Colombier {
17707dd7cddfSDavid du Colombier 	int c;
17717dd7cddfSDavid du Colombier 
17727dd7cddfSDavid du Colombier 	while(c = *headStr){
17737dd7cddfSDavid du Colombier 		headStr++;
17747dd7cddfSDavid du Colombier 		if(c == '\n'){
17757dd7cddfSDavid du Colombier 			c = *headStr;
17767dd7cddfSDavid du Colombier 			if(c == ' ' || c == '\t')
17777dd7cddfSDavid du Colombier 				continue;
17787dd7cddfSDavid du Colombier 			return;
17797dd7cddfSDavid du Colombier 		}
17807dd7cddfSDavid du Colombier 	}
17817dd7cddfSDavid du Colombier }
1782