xref: /csrg-svn/usr.sbin/sendmail/src/mime.c (revision 68717)
167545Seric /*
267545Seric  * Copyright (c) 1994 Eric P. Allman
367545Seric  * Copyright (c) 1994
467545Seric  *	The Regents of the University of California.  All rights reserved.
567545Seric  *
667545Seric  * %sccs.include.redist.c%
767545Seric  */
867545Seric 
967545Seric # include "sendmail.h"
1067545Seric # include <string.h>
1167545Seric 
1267545Seric #ifndef lint
13*68717Seric static char sccsid[] = "@(#)mime.c	8.14 (Berkeley) 04/03/95";
1467545Seric #endif /* not lint */
1567545Seric 
1667545Seric /*
1767545Seric **  MIME support.
1867545Seric **
1967545Seric **	I am indebted to John Beck of Hewlett-Packard, who contributed
2067545Seric **	his code to me for inclusion.  As it turns out, I did not use
2167545Seric **	his code since he used a "minimum change" approach that used
2267545Seric **	several temp files, and I wanted a "minimum impact" approach
2367545Seric **	that would avoid copying.  However, looking over his code
2467545Seric **	helped me cement my understanding of the problem.
2567545Seric **
2667545Seric **	I also looked at, but did not directly use, Nathaniel
2767545Seric **	Borenstein's "code.c" module.  Again, it functioned as
2867545Seric **	a file-to-file translator, which did not fit within my
2967545Seric **	design bounds, but it was a useful base for understanding
3067545Seric **	the problem.
3167545Seric */
3267545Seric 
3367545Seric 
3467545Seric /* character set for hex and base64 encoding */
3567545Seric char	Base16Code[] =	"0123456789ABCDEF";
3667545Seric char	Base64Code[] =	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3767545Seric 
3867545Seric /* types of MIME boundaries */
3967545Seric #define MBT_SYNTAX	0	/* syntax error */
4067545Seric #define MBT_NOTSEP	1	/* not a boundary */
4167545Seric #define MBT_INTERMED	2	/* intermediate boundary (no trailing --) */
4267545Seric #define MBT_FINAL	3	/* final boundary (trailing -- included) */
4367547Seric 
44*68717Seric static char	*MimeBoundaryNames[] =
45*68717Seric {
46*68717Seric 	"SYNTAX",	"NOTSEP",	"INTERMED",	"FINAL"
47*68717Seric };
4867545Seric /*
4967545Seric **  MIME8TO7 -- output 8 bit body in 7 bit format
5067545Seric **
5167545Seric **	The header has already been output -- this has to do the
5267545Seric **	8 to 7 bit conversion.  It would be easy if we didn't have
5367545Seric **	to deal with nested formats (multipart/xxx and message/rfc822).
5467545Seric **
5567545Seric **	We won't be called if we don't have to do a conversion, and
5667545Seric **	appropriate MIME-Version: and Content-Type: fields have been
5767545Seric **	output.  Any Content-Transfer-Encoding: field has not been
5867545Seric **	output, and we can add it here.
5967545Seric **
6067545Seric **	Parameters:
6167545Seric **		mci -- mailer connection information.
6267545Seric **		header -- the header for this body part.
6367545Seric **		e -- envelope.
6468517Seric **		boundaries -- the currently pending message boundaries.
6568517Seric **			NULL if we are processing the outer portion.
6668517Seric **		flags -- to tweak processing.
6767545Seric **
6867545Seric **	Returns:
6967545Seric **		An indicator of what terminated the message part:
7067545Seric **		  MBT_FINAL -- the final boundary
7167545Seric **		  MBT_INTERMED -- an intermediate boundary
7267545Seric **		  MBT_NOTSEP -- an end of file
7367545Seric */
7467545Seric 
7568517Seric struct args
7668517Seric {
7768517Seric 	char	*field;		/* name of field */
7868517Seric 	char	*value;		/* value of that field */
7968517Seric };
8068517Seric 
8167545Seric int
8268517Seric mime8to7(mci, header, e, boundaries, flags)
8367545Seric 	register MCI *mci;
84*68717Seric 	HDR *header;
85*68717Seric 	register ENVELOPE *e;
8668517Seric 	char **boundaries;
8768517Seric 	int flags;
8867545Seric {
8967545Seric 	register char *p;
9067545Seric 	int linelen;
9167545Seric 	int bt;
9267545Seric 	off_t offset;
9367545Seric 	size_t sectionsize, sectionhighbits;
9468517Seric 	int i;
9568517Seric 	char *type;
9668517Seric 	char *subtype;
9768517Seric 	char **pvp;
9868517Seric 	int argc = 0;
9968517Seric 	struct args argv[MAXMIMEARGS];
10067545Seric 	char bbuf[128];
10167545Seric 	char buf[MAXLINE];
10268517Seric 	char pvpbuf[MAXLINE];
10368711Seric 	extern char MimeTokenTab[256];
10467545Seric 
10567545Seric 	if (tTd(43, 1))
10667545Seric 	{
107*68717Seric 		printf("mime8to7: flags = %x, boundaries =", flags);
108*68717Seric 		if (boundaries[0] == NULL)
109*68717Seric 			printf(" <none>");
110*68717Seric 		else
111*68717Seric 		{
112*68717Seric 			for (i = 0; boundaries[i] != NULL; i++)
113*68717Seric 				printf(" %s", boundaries[i]);
114*68717Seric 		}
115*68717Seric 		printf("\n");
11667545Seric 	}
11768517Seric 	type = subtype = "-none-";
11867545Seric 	p = hvalue("Content-Type", header);
11968517Seric 	if (p != NULL &&
12068711Seric 	    (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
12168711Seric 			   MimeTokenTab)) != NULL &&
12268517Seric 	    pvp[0] != NULL)
12367545Seric 	{
124*68717Seric 		if (tTd(43, 40))
125*68717Seric 		{
126*68717Seric 			for (i = 0; pvp[i] != NULL; i++)
127*68717Seric 				printf("pvp[%d] = \"%s\"\n", i, pvp[i]);
128*68717Seric 		}
12968517Seric 		type = *pvp++;
13068517Seric 		if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
13168517Seric 		    *++pvp != NULL)
13268517Seric 		{
13368517Seric 			subtype = *pvp++;
13468517Seric 		}
13568517Seric 
13668517Seric 		/* break out parameters */
13768517Seric 		while (*pvp != NULL && argc < MAXMIMEARGS)
13868517Seric 		{
13968517Seric 			/* skip to semicolon separator */
14068517Seric 			while (*pvp != NULL && strcmp(*pvp, ";") != 0)
14168517Seric 				pvp++;
14268517Seric 			if (*pvp++ == NULL || *pvp == NULL)
14368517Seric 				break;
14468517Seric 
14568517Seric 			/* extract field name */
14668517Seric 			argv[argc].field = *pvp++;
14768517Seric 
14868517Seric 			/* see if there is a value */
14968517Seric 			if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
15068517Seric 			    (*++pvp == NULL || strcmp(*pvp, ";") != 0))
15168517Seric 			{
15268517Seric 				argv[argc].value = *pvp;
15368517Seric 				argc++;
15468517Seric 			}
15568517Seric 		}
15668517Seric 	}
157*68717Seric 
158*68717Seric 	/* handle types that cannot have 8-bit data internally */
159*68717Seric 	sprintf(buf, "%s/%s", type, subtype);
160*68717Seric 	if (wordinclass(buf, 'n'))
161*68717Seric 		flags |= M87F_NO8BIT;
162*68717Seric 
163*68717Seric 	/*
164*68717Seric 	**  Multipart requires special processing.
165*68717Seric 	**
166*68717Seric 	**	Do a recursive descent into the message.
167*68717Seric 	*/
168*68717Seric 
16968517Seric 	if (strcasecmp(type, "multipart") == 0)
17068517Seric 	{
17167545Seric 		register char *q;
17267545Seric 
17368517Seric 		for (i = 0; i < argc; i++)
17467545Seric 		{
17568517Seric 			if (strcasecmp(argv[i].field, "boundary") == 0)
17668517Seric 				break;
17768517Seric 		}
17868517Seric 		if (i >= argc)
17968517Seric 		{
18067545Seric 			syserr("mime8to7: Content-Type: %s missing boundary", p);
18167545Seric 			p = "---";
18267545Seric 		}
18367545Seric 		else
18468517Seric 			p = argv[i].value;
18567545Seric 		if (*p == '"')
18668711Seric 			q = strchr(++p, '"');
18767545Seric 		else
18867545Seric 			q = p + strlen(p);
18967545Seric 		if (q - p > sizeof bbuf - 1)
19067545Seric 		{
19167545Seric 			syserr("mime8to7: multipart boundary \"%.*s\" too long",
19267545Seric 				q - p, p);
19367545Seric 			q = p + sizeof bbuf - 1;
19467545Seric 		}
19567545Seric 		strncpy(bbuf, p, q - p);
19667545Seric 		bbuf[q - p] = '\0';
19767545Seric 		if (tTd(43, 1))
19867545Seric 			printf("mime8to7: multipart boundary \"%s\"\n", bbuf);
19968517Seric 		for (i = 0; i < MAXMIMENESTING; i++)
20068517Seric 			if (boundaries[i] == NULL)
20168517Seric 				break;
20268517Seric 		if (i >= MAXMIMENESTING)
20368517Seric 			syserr("mime8to7: multipart nesting boundary too deep");
20468517Seric 		else
20568517Seric 		{
20668517Seric 			boundaries[i] = bbuf;
20768517Seric 			boundaries[i + 1] = NULL;
20868517Seric 		}
20967545Seric 
21067545Seric 		/* skip the early "comment" prologue */
211*68717Seric 		putline("", mci);
21267545Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
21367545Seric 		{
21468517Seric 			bt = mimeboundary(buf, boundaries);
21567545Seric 			if (bt != MBT_NOTSEP)
21667545Seric 				break;
21767545Seric 			putline(buf, mci);
218*68717Seric 			if (tTd(43, 99))
219*68717Seric 				printf("  ...%s", buf);
22067545Seric 		}
221*68717Seric 		if (feof(e->e_dfp))
222*68717Seric 			bt = MBT_FINAL;
22367545Seric 		while (bt != MBT_FINAL)
22467545Seric 		{
22567545Seric 			auto HDR *hdr = NULL;
22667545Seric 
22767545Seric 			sprintf(buf, "--%s", bbuf);
22867545Seric 			putline(buf, mci);
229*68717Seric 			if (tTd(43, 35))
230*68717Seric 				printf("  ...%s\n", buf);
23167545Seric 			collect(e->e_dfp, FALSE, FALSE, &hdr, e);
232*68717Seric 			if (tTd(43, 101))
233*68717Seric 				putline("+++after collect", mci);
23467936Seric 			putheader(mci, hdr, e, 0);
235*68717Seric 			if (tTd(43, 101))
236*68717Seric 				putline("+++after putheader", mci);
23768517Seric 			bt = mime8to7(mci, hdr, e, boundaries, flags);
23867545Seric 		}
23967545Seric 		sprintf(buf, "--%s--", bbuf);
24067545Seric 		putline(buf, mci);
241*68717Seric 		if (tTd(43, 35))
242*68717Seric 			printf("  ...%s\n", buf);
243*68717Seric 		boundaries[i] = NULL;
24467545Seric 
24567545Seric 		/* skip the late "comment" epilogue */
24667545Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
24767545Seric 		{
24868517Seric 			bt = mimeboundary(buf, boundaries);
24967545Seric 			if (bt != MBT_NOTSEP)
25067545Seric 				break;
251*68717Seric 			putline(buf, mci);
252*68717Seric 			if (tTd(43, 99))
253*68717Seric 				printf("  ...%s", buf);
25467545Seric 		}
255*68717Seric 		if (feof(e->e_dfp))
256*68717Seric 			bt = MBT_FINAL;
257*68717Seric 		if (tTd(43, 3))
258*68717Seric 			printf("\t\t\tmime8to7=>%s (multipart)\n",
259*68717Seric 				MimeBoundaryNames[bt]);
26067545Seric 		return bt;
26167545Seric 	}
26267545Seric 
26367545Seric 	/*
26467545Seric 	**  Non-compound body type
26567545Seric 	**
26667545Seric 	**	Compute the ratio of seven to eight bit characters;
26767545Seric 	**	use that as a heuristic to decide how to do the
26867545Seric 	**	encoding.
26967545Seric 	*/
27067545Seric 
27167545Seric 	sectionsize = sectionhighbits = 0;
27268517Seric 	if (!bitset(M87F_NO8BIT, flags))
27367545Seric 	{
27468515Seric 		/* remember where we were */
27568515Seric 		offset = ftell(e->e_dfp);
27668515Seric 		if (offset == -1)
27768564Seric 			syserr("mime8to7: cannot ftell on df%s", e->e_id);
27868515Seric 
27968515Seric 		/* do a scan of this body type to count character types */
28068515Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
28167545Seric 		{
282*68717Seric 			if (mimeboundary(buf, boundaries) != MBT_NOTSEP)
28368515Seric 				break;
28468515Seric 			for (p = buf; *p != '\0'; p++)
28568515Seric 			{
28668515Seric 				/* count bytes with the high bit set */
28768515Seric 				sectionsize++;
28868515Seric 				if (bitset(0200, *p))
28968515Seric 					sectionhighbits++;
29068515Seric 			}
29168515Seric 
29268515Seric 			/*
29368515Seric 			**  Heuristic: if 1/4 of the first 4K bytes are 8-bit,
29468515Seric 			**  assume base64.  This heuristic avoids double-reading
29568515Seric 			**  large graphics or video files.
29668515Seric 			*/
29768515Seric 
29868515Seric 			if (sectionsize >= 4096 &&
29968515Seric 			    sectionhighbits > sectionsize / 4)
30068515Seric 				break;
30167545Seric 		}
30267547Seric 
30368515Seric 		/* return to the original offset for processing */
30468515Seric 		/* XXX use relative seeks to handle >31 bit file sizes? */
30568515Seric 		if (fseek(e->e_dfp, offset, SEEK_SET) < 0)
30668564Seric 			syserr("mime8to7: cannot fseek on df%s", e->e_id);
307*68717Seric 		else
308*68717Seric 			clearerr(e->e_dfp);
30967545Seric 	}
31067545Seric 
31167547Seric 	/*
31267547Seric 	**  Heuristically determine encoding method.
31367547Seric 	**	If more than 1/8 of the total characters have the
31467547Seric 	**	eighth bit set, use base64; else use quoted-printable.
31567547Seric 	*/
31667547Seric 
31767545Seric 	if (tTd(43, 8))
31867545Seric 	{
319*68717Seric 		printf("mime8to7: %ld high bit(s) in %ld byte(s)\n",
32067545Seric 			sectionhighbits, sectionsize);
32167545Seric 	}
322*68717Seric 	linelen = 0;
32367554Seric 	if (sectionhighbits == 0)
32467545Seric 	{
32567554Seric 		/* no encoding necessary */
32667695Seric 		p = hvalue("content-transfer-encoding", header);
32767695Seric 		if (p != NULL)
32867695Seric 		{
32967695Seric 			sprintf(buf, "Content-Transfer-Encoding: %s", p);
33067695Seric 			putline(buf, mci);
331*68717Seric 			if (tTd(43, 36))
332*68717Seric 				printf("  ...%s\n", buf);
33367695Seric 		}
33467554Seric 		putline("", mci);
33567554Seric 		mci->mci_flags &= ~MCIF_INHEADER;
33667554Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
33767554Seric 		{
33868517Seric 			bt = mimeboundary(buf, boundaries);
33967554Seric 			if (bt != MBT_NOTSEP)
34067554Seric 				break;
34167554Seric 			if (buf[0] == 'F' &&
34267554Seric 			    bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
34367554Seric 			    strncmp(buf, "From ", 5) == 0)
34467554Seric 				(void) putc('>', mci->mci_out);
34567554Seric 			putline(buf, mci);
34667554Seric 		}
347*68717Seric 		if (feof(e->e_dfp))
348*68717Seric 			bt = MBT_FINAL;
34967554Seric 	}
35067554Seric 	else if (sectionsize / 8 < sectionhighbits)
35167554Seric 	{
35267545Seric 		/* use base64 encoding */
35367545Seric 		int c1, c2;
35467545Seric 
35567545Seric 		putline("Content-Transfer-Encoding: base64", mci);
356*68717Seric 		if (tTd(43, 36))
357*68717Seric 			printf("  ...Content-Transfer-Encoding: base64\n");
35867545Seric 		putline("", mci);
35967545Seric 		mci->mci_flags &= ~MCIF_INHEADER;
360*68717Seric 		while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF)
36167545Seric 		{
36267545Seric 			if (linelen > 71)
36367545Seric 			{
36467545Seric 				fputs(mci->mci_mailer->m_eol, mci->mci_out);
36567545Seric 				linelen = 0;
36667545Seric 			}
36767545Seric 			linelen += 4;
36867545Seric 			fputc(Base64Code[c1 >> 2], mci->mci_out);
36967545Seric 			c1 = (c1 & 0x03) << 4;
370*68717Seric 			c2 = mime_getchar(e->e_dfp, boundaries, &bt);
37167545Seric 			if (c2 == EOF)
37267545Seric 			{
37367545Seric 				fputc(Base64Code[c1], mci->mci_out);
37467545Seric 				fputc('=', mci->mci_out);
37567545Seric 				fputc('=', mci->mci_out);
37667545Seric 				break;
37767545Seric 			}
37867545Seric 			c1 |= (c2 >> 4) & 0x0f;
37967545Seric 			fputc(Base64Code[c1], mci->mci_out);
38067545Seric 			c1 = (c2 & 0x0f) << 2;
381*68717Seric 			c2 = mime_getchar(e->e_dfp, boundaries, &bt);
38267545Seric 			if (c2 == EOF)
38367545Seric 			{
38467545Seric 				fputc(Base64Code[c1], mci->mci_out);
38567545Seric 				fputc('=', mci->mci_out);
38667545Seric 				break;
38767545Seric 			}
38867545Seric 			c1 |= (c2 >> 6) & 0x03;
38967545Seric 			fputc(Base64Code[c1], mci->mci_out);
39067545Seric 			fputc(Base64Code[c2 & 0x3f], mci->mci_out);
39167545Seric 		}
39267545Seric 	}
39367545Seric 	else
39467545Seric 	{
39567545Seric 		/* use quoted-printable encoding */
39667545Seric 		int c1, c2;
39768515Seric 		int fromstate;
39867545Seric 
39967545Seric 		putline("Content-Transfer-Encoding: quoted-printable", mci);
400*68717Seric 		if (tTd(43, 36))
401*68717Seric 			printf("  ...Content-Transfer-Encoding: quoted-printable\n");
40267545Seric 		putline("", mci);
40367545Seric 		mci->mci_flags &= ~MCIF_INHEADER;
404*68717Seric 		fromstate = 0;
40567554Seric 		c2 = '\n';
406*68717Seric 		while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF)
40767545Seric 		{
40867545Seric 			if (c1 == '\n')
40967545Seric 			{
41067545Seric 				if (c2 == ' ' || c2 == '\t')
41167545Seric 				{
41267545Seric 					fputc('=', mci->mci_out);
41367840Seric 					fputc(Base16Code[(c2 >> 4) & 0x0f],
41467840Seric 								mci->mci_out);
41567840Seric 					fputc(Base16Code[c2 & 0x0f],
41667840Seric 								mci->mci_out);
41767840Seric 					fputs(mci->mci_mailer->m_eol,
41867840Seric 								mci->mci_out);
41967545Seric 				}
42067545Seric 				fputs(mci->mci_mailer->m_eol, mci->mci_out);
42168515Seric 				linelen = fromstate = 0;
42267545Seric 				c2 = c1;
42367545Seric 				continue;
42467545Seric 			}
42568515Seric 			if (c2 == ' ' && linelen == 4 && fromstate == 4 &&
42668515Seric 			    bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
42767840Seric 			{
42868515Seric 				fputs("=20", mci->mci_out);
42968515Seric 				linelen += 3;
43068515Seric 			}
43168515Seric 			else if (c2 == ' ' || c2 == '\t')
43268515Seric 			{
43367840Seric 				fputc(c2, mci->mci_out);
43467840Seric 				linelen++;
43567840Seric 			}
43667545Seric 			if (linelen > 72)
43767545Seric 			{
43867545Seric 				fputc('=', mci->mci_out);
43967545Seric 				fputs(mci->mci_mailer->m_eol, mci->mci_out);
44068515Seric 				linelen = fromstate = 0;
44167554Seric 				c2 = '\n';
44267545Seric 			}
44367761Seric 			if (c2 == '\n' && c1 == '.' &&
44467761Seric 				 bitnset(M_XDOT, mci->mci_mailer->m_flags))
44567761Seric 			{
44667761Seric 				fputc('.', mci->mci_out);
44767761Seric 				linelen++;
44867761Seric 			}
44967547Seric 			if ((c1 < 0x20 && c1 != '\t') || c1 >= 0x7f || c1 == '=')
45067545Seric 			{
45167545Seric 				fputc('=', mci->mci_out);
45267545Seric 				fputc(Base16Code[(c1 >> 4) & 0x0f], mci->mci_out);
45367545Seric 				fputc(Base16Code[c1 & 0x0f], mci->mci_out);
45467545Seric 				linelen += 3;
45567545Seric 			}
45667840Seric 			else if (c1 != ' ' && c1 != '\t')
45767545Seric 			{
45868515Seric 				if (linelen < 4 && c1 == "From"[linelen])
45968515Seric 					fromstate++;
46067545Seric 				fputc(c1, mci->mci_out);
46167545Seric 				linelen++;
46267545Seric 			}
46367545Seric 			c2 = c1;
46467545Seric 		}
46567840Seric 
46667840Seric 		/* output any saved character */
46767840Seric 		if (c2 == ' ' || c2 == '\t')
46867840Seric 		{
46968515Seric 			fputc('=', mci->mci_out);
47068515Seric 			fputc(Base16Code[(c2 >> 4) & 0x0f], mci->mci_out);
47168515Seric 			fputc(Base16Code[c2 & 0x0f], mci->mci_out);
47268515Seric 			linelen += 3;
47367840Seric 		}
47467545Seric 	}
47567545Seric 	if (linelen > 0)
47667545Seric 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
477*68717Seric 	if (tTd(43, 3))
478*68717Seric 		printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
479*68717Seric 	return bt;
48067545Seric }
48168515Seric /*
48268515Seric **  MIME_GETCHAR -- get a character for MIME processing
48368515Seric **
48468515Seric **	Treats boundaries as EOF.
48568515Seric **
48668515Seric **	Parameters:
48768515Seric **		fp -- the input file.
48868517Seric **		boundaries -- the current MIME boundaries.
489*68717Seric **		btp -- if the return value is EOF, *btp is set to
490*68717Seric **			the type of the boundary.
49168515Seric **
49268515Seric **	Returns:
49368515Seric **		The next character in the input stream.
49468515Seric */
49567545Seric 
49667545Seric int
497*68717Seric mime_getchar(fp, boundaries, btp)
49867545Seric 	register FILE *fp;
49968517Seric 	char **boundaries;
500*68717Seric 	int *btp;
50167545Seric {
50267545Seric 	int c;
50367545Seric 	static char *bp = NULL;
50467545Seric 	static int buflen = 0;
50567545Seric 	static bool atbol = TRUE;	/* at beginning of line */
506*68717Seric 	static int bt = MBT_SYNTAX;	/* boundary type of next EOF */
50767545Seric 	static char buf[128];		/* need not be a full line */
50867545Seric 
50967545Seric 	if (buflen > 0)
51067545Seric 	{
51167545Seric 		buflen--;
51267545Seric 		return *bp++;
51367545Seric 	}
51468515Seric 	bp = buf;
51568515Seric 	buflen = 0;
51667545Seric 	c = fgetc(fp);
51768515Seric 	if (c == '\n')
51868515Seric 	{
51968515Seric 		/* might be part of a MIME boundary */
52068515Seric 		*bp++ = c;
52168515Seric 		atbol = TRUE;
52268515Seric 		c = fgetc(fp);
52368515Seric 	}
52468515Seric 	if (c != EOF)
52568515Seric 		*bp++ = c;
526*68717Seric 	else
527*68717Seric 		bt = MBT_FINAL;
52868517Seric 	if (atbol && c == '-')
52967545Seric 	{
53067545Seric 		/* check for a message boundary */
53167545Seric 		c = fgetc(fp);
53267545Seric 		if (c != '-')
53367545Seric 		{
53467545Seric 			if (c != EOF)
53568515Seric 				*bp++ = c;
536*68717Seric 			else
537*68717Seric 				bt = MBT_FINAL;
53868515Seric 			buflen = bp - buf - 1;
53968515Seric 			bp = buf;
54068515Seric 			return *bp++;
54167545Seric 		}
54267545Seric 
54367545Seric 		/* got "--", now check for rest of separator */
54467545Seric 		*bp++ = '-';
54567545Seric 		while (bp < &buf[sizeof buf - 1] &&
54667545Seric 		       (c = fgetc(fp)) != EOF && c != '\n')
54767545Seric 		{
54867545Seric 			*bp++ = c;
54967545Seric 		}
55067545Seric 		*bp = '\0';
551*68717Seric 		bt = mimeboundary(&buf[1], boundaries);
552*68717Seric 		switch (bt)
55367545Seric 		{
55467545Seric 		  case MBT_FINAL:
55567545Seric 		  case MBT_INTERMED:
55667545Seric 			/* we have a message boundary */
55767545Seric 			buflen = 0;
558*68717Seric 			*btp = bt;
55967545Seric 			return EOF;
56067545Seric 		}
56167545Seric 
56267545Seric 		atbol = c == '\n';
56367545Seric 		if (c != EOF)
56467545Seric 			*bp++ = c;
56567545Seric 	}
56667545Seric 
56768515Seric 	buflen = bp - buf - 1;
56868515Seric 	if (buflen < 0)
569*68717Seric 	{
570*68717Seric 		*btp = bt;
57168515Seric 		return EOF;
572*68717Seric 	}
57368515Seric 	bp = buf;
57468515Seric 	return *bp++;
57567545Seric }
57667545Seric /*
57767545Seric **  MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
57867545Seric **
57967545Seric **	Parameters:
58067545Seric **		line -- the input line.
58168517Seric **		boundaries -- the set of currently pending boundaries.
58267545Seric **
58367545Seric **	Returns:
58467545Seric **		MBT_NOTSEP -- if this is not a separator line
58567545Seric **		MBT_INTERMED -- if this is an intermediate separator
58667545Seric **		MBT_FINAL -- if this is a final boundary
58767545Seric **		MBT_SYNTAX -- if this is a boundary for the wrong
58867545Seric **			enclosure -- i.e., a syntax error.
58967545Seric */
59067545Seric 
59167545Seric int
59268517Seric mimeboundary(line, boundaries)
59367545Seric 	register char *line;
59468517Seric 	char **boundaries;
59567545Seric {
59667545Seric 	int type;
59767545Seric 	int i;
59868517Seric 	int savec;
59967545Seric 
60068517Seric 	if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
60167545Seric 		return MBT_NOTSEP;
60267545Seric 	i = strlen(line);
60367545Seric 	if (line[i - 1] == '\n')
60467545Seric 		i--;
605*68717Seric 	if (tTd(43, 5))
606*68717Seric 		printf("mimeboundary: line=\"%.*s\"... ", i, line);
60768515Seric 	while (line[i - 1] == ' ' || line[i - 1] == '\t')
60868515Seric 		i--;
60967545Seric 	if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0)
61067545Seric 	{
61167545Seric 		type = MBT_FINAL;
61267545Seric 		i -= 2;
61367545Seric 	}
61467545Seric 	else
61567545Seric 		type = MBT_INTERMED;
61667545Seric 
61768517Seric 	savec = line[i];
61868517Seric 	line[i] = '\0';
61967545Seric 	/* XXX should check for improper nesting here */
62068517Seric 	if (isboundary(&line[2], boundaries) < 0)
62167545Seric 		type = MBT_NOTSEP;
62268517Seric 	line[i] = savec;
62367545Seric 	if (tTd(43, 5))
624*68717Seric 		printf("%s\n", MimeBoundaryNames[type]);
62567545Seric 	return type;
62667545Seric }
62767896Seric /*
62867896Seric **  DEFCHARSET -- return default character set for message
62967896Seric **
63067896Seric **	The first choice for character set is for the mailer
63167896Seric **	corresponding to the envelope sender.  If neither that
63267896Seric **	nor the global configuration file has a default character
63367896Seric **	set defined, return "unknown-8bit" as recommended by
63467896Seric **	RFC 1428 section 3.
63567896Seric **
63667896Seric **	Parameters:
63767896Seric **		e -- the envelope for this message.
63867896Seric **
63967896Seric **	Returns:
64067896Seric **		The default character set for that mailer.
64167896Seric */
64267896Seric 
64367896Seric char *
64467896Seric defcharset(e)
64567896Seric 	register ENVELOPE *e;
64667896Seric {
64767896Seric 	if (e != NULL && e->e_from.q_mailer != NULL &&
64867896Seric 	    e->e_from.q_mailer->m_defcharset != NULL)
64967896Seric 		return e->e_from.q_mailer->m_defcharset;
65067896Seric 	if (DefaultCharSet != NULL)
65167896Seric 		return DefaultCharSet;
65267896Seric 	return "unknown-8bit";
65367896Seric }
65468517Seric /*
65568517Seric **  ISBOUNDARY -- is a given string a currently valid boundary?
65668517Seric **
65768517Seric **	Parameters:
65868517Seric **		line -- the current input line.
65968517Seric **		boundaries -- the list of valid boundaries.
66068517Seric **
66168517Seric **	Returns:
66268517Seric **		The index number in boundaries if the line is found.
66368517Seric **		-1 -- otherwise.
66468517Seric **
66568517Seric */
66668517Seric 
66768517Seric int
66868517Seric isboundary(line, boundaries)
66968517Seric 	char *line;
67068517Seric 	char **boundaries;
67168517Seric {
67268517Seric 	register int i;
67368517Seric 
67468711Seric 	for (i = 0; boundaries[i] != NULL; i++)
67568517Seric 	{
67668517Seric 		if (strcmp(line, boundaries[i]) == 0)
67768517Seric 			return i;
67868517Seric 	}
67968517Seric 	return -1;
68068517Seric }
681