xref: /csrg-svn/usr.sbin/sendmail/src/mime.c (revision 69480)
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*69480Seric static char sccsid[] = "@(#)mime.c	8.22 (Berkeley) 05/15/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 
33*69480Seric #if MIME8TO7
3467545Seric 
3567545Seric /* character set for hex and base64 encoding */
3667545Seric char	Base16Code[] =	"0123456789ABCDEF";
3767545Seric char	Base64Code[] =	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3867545Seric 
3967545Seric /* types of MIME boundaries */
4067545Seric #define MBT_SYNTAX	0	/* syntax error */
4167545Seric #define MBT_NOTSEP	1	/* not a boundary */
4267545Seric #define MBT_INTERMED	2	/* intermediate boundary (no trailing --) */
4367545Seric #define MBT_FINAL	3	/* final boundary (trailing -- included) */
4467547Seric 
4568717Seric static char	*MimeBoundaryNames[] =
4668717Seric {
4768717Seric 	"SYNTAX",	"NOTSEP",	"INTERMED",	"FINAL"
4868717Seric };
4967545Seric /*
5067545Seric **  MIME8TO7 -- output 8 bit body in 7 bit format
5167545Seric **
5267545Seric **	The header has already been output -- this has to do the
5367545Seric **	8 to 7 bit conversion.  It would be easy if we didn't have
5467545Seric **	to deal with nested formats (multipart/xxx and message/rfc822).
5567545Seric **
5667545Seric **	We won't be called if we don't have to do a conversion, and
5767545Seric **	appropriate MIME-Version: and Content-Type: fields have been
5867545Seric **	output.  Any Content-Transfer-Encoding: field has not been
5967545Seric **	output, and we can add it here.
6067545Seric **
6167545Seric **	Parameters:
6267545Seric **		mci -- mailer connection information.
6367545Seric **		header -- the header for this body part.
6467545Seric **		e -- envelope.
6569281Seric **		boundaries -- the currently pending message boundaries.
6669281Seric **			NULL if we are processing the outer portion.
6768517Seric **		flags -- to tweak processing.
6867545Seric **
6967545Seric **	Returns:
7067545Seric **		An indicator of what terminated the message part:
7167545Seric **		  MBT_FINAL -- the final boundary
7267545Seric **		  MBT_INTERMED -- an intermediate boundary
7367545Seric **		  MBT_NOTSEP -- an end of file
7467545Seric */
7567545Seric 
7668517Seric struct args
7768517Seric {
7868517Seric 	char	*field;		/* name of field */
7968517Seric 	char	*value;		/* value of that field */
8068517Seric };
8168517Seric 
8267545Seric int
8369281Seric mime8to7(mci, header, e, boundaries, flags)
8467545Seric 	register MCI *mci;
8568717Seric 	HDR *header;
8668717Seric 	register ENVELOPE *e;
8768517Seric 	char **boundaries;
8868517Seric 	int flags;
8967545Seric {
9067545Seric 	register char *p;
9167545Seric 	int linelen;
9267545Seric 	int bt;
9367545Seric 	off_t offset;
9467545Seric 	size_t sectionsize, sectionhighbits;
9568517Seric 	int i;
9668517Seric 	char *type;
9768517Seric 	char *subtype;
9868847Seric 	char *cte;
9968517Seric 	char **pvp;
10068517Seric 	int argc = 0;
10168876Seric 	char *bp;
10268517Seric 	struct args argv[MAXMIMEARGS];
10367545Seric 	char bbuf[128];
10467545Seric 	char buf[MAXLINE];
10568517Seric 	char pvpbuf[MAXLINE];
10668711Seric 	extern char MimeTokenTab[256];
10767545Seric 
10867545Seric 	if (tTd(43, 1))
10967545Seric 	{
11068717Seric 		printf("mime8to7: flags = %x, boundaries =", flags);
11168717Seric 		if (boundaries[0] == NULL)
11268717Seric 			printf(" <none>");
11368717Seric 		else
11468717Seric 		{
11568717Seric 			for (i = 0; boundaries[i] != NULL; i++)
11668717Seric 				printf(" %s", boundaries[i]);
11768717Seric 		}
11868717Seric 		printf("\n");
11967545Seric 	}
12068876Seric 	p = hvalue("Content-Transfer-Encoding", header);
12168876Seric 	if (p == NULL ||
12268876Seric 	    (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
12368876Seric 			   MimeTokenTab)) == NULL ||
12468876Seric 	    pvp[0] == NULL)
12568876Seric 	{
12668876Seric 		cte = NULL;
12768876Seric 	}
12868876Seric 	else
12968876Seric 	{
13068876Seric 		cataddr(pvp, NULL, buf, sizeof buf, '\0');
13168876Seric 		cte = newstr(buf);
13268876Seric 	}
13368876Seric 
13468847Seric 	type = subtype = NULL;
13567545Seric 	p = hvalue("Content-Type", header);
13668847Seric 	if (p == NULL)
13768847Seric 	{
13868847Seric 		if (bitset(M87F_DIGEST, flags))
13968847Seric 			p = "message/rfc822";
14068847Seric 		else
14168847Seric 			p = "text/plain";
14268847Seric 	}
14368517Seric 	if (p != NULL &&
14468711Seric 	    (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
14568711Seric 			   MimeTokenTab)) != NULL &&
14668517Seric 	    pvp[0] != NULL)
14767545Seric 	{
14868717Seric 		if (tTd(43, 40))
14968717Seric 		{
15068717Seric 			for (i = 0; pvp[i] != NULL; i++)
15168717Seric 				printf("pvp[%d] = \"%s\"\n", i, pvp[i]);
15268717Seric 		}
15368517Seric 		type = *pvp++;
15468517Seric 		if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
15568517Seric 		    *++pvp != NULL)
15668517Seric 		{
15768517Seric 			subtype = *pvp++;
15868517Seric 		}
15968517Seric 
16068517Seric 		/* break out parameters */
16168517Seric 		while (*pvp != NULL && argc < MAXMIMEARGS)
16268517Seric 		{
16368517Seric 			/* skip to semicolon separator */
16468517Seric 			while (*pvp != NULL && strcmp(*pvp, ";") != 0)
16568517Seric 				pvp++;
16668517Seric 			if (*pvp++ == NULL || *pvp == NULL)
16768517Seric 				break;
16868517Seric 
16968517Seric 			/* extract field name */
17068517Seric 			argv[argc].field = *pvp++;
17168517Seric 
17268517Seric 			/* see if there is a value */
17368517Seric 			if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
17468517Seric 			    (*++pvp == NULL || strcmp(*pvp, ";") != 0))
17568517Seric 			{
17668517Seric 				argv[argc].value = *pvp;
17768517Seric 				argc++;
17868517Seric 			}
17968517Seric 		}
18068517Seric 	}
18168717Seric 
18268847Seric 	/* check for disaster cases */
18368847Seric 	if (type == NULL)
18468847Seric 		type = "-none-";
18568847Seric 	if (subtype == NULL)
18668847Seric 		subtype = "-none-";
18768847Seric 
18868876Seric 	/* don't propogate some flags more than one level into the message */
18968876Seric 	flags &= ~M87F_DIGEST;
19068876Seric 
19168847Seric 	/*
19268847Seric 	**  Check for cases that can not be encoded.
19368847Seric 	**
19468847Seric 	**	For example, you can't encode certain kinds of types
19568847Seric 	**	or already-encoded messages.  If we find this case,
19668847Seric 	**	just copy it through.
19768847Seric 	*/
19868847Seric 
19968717Seric 	sprintf(buf, "%s/%s", type, subtype);
20068847Seric 	if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
20168717Seric 		flags |= M87F_NO8BIT;
20268717Seric 
20368717Seric 	/*
20468717Seric 	**  Multipart requires special processing.
20568717Seric 	**
20668717Seric 	**	Do a recursive descent into the message.
20768717Seric 	*/
20868717Seric 
20968876Seric 	if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags))
21068517Seric 	{
21168876Seric 		int blen;
21267545Seric 
21368847Seric 		if (strcasecmp(subtype, "digest") == 0)
21468847Seric 			flags |= M87F_DIGEST;
21568847Seric 
21668517Seric 		for (i = 0; i < argc; i++)
21767545Seric 		{
21868517Seric 			if (strcasecmp(argv[i].field, "boundary") == 0)
21968517Seric 				break;
22068517Seric 		}
22168517Seric 		if (i >= argc)
22268517Seric 		{
22367545Seric 			syserr("mime8to7: Content-Type: %s missing boundary", p);
22467545Seric 			p = "---";
22567545Seric 		}
22667545Seric 		else
22768876Seric 		{
22868517Seric 			p = argv[i].value;
22968876Seric 			stripquotes(p);
23068876Seric 		}
23168876Seric 		blen = strlen(p);
23268876Seric 		if (blen > sizeof bbuf - 1)
23367545Seric 		{
23468876Seric 			syserr("mime8to7: multipart boundary \"%s\" too long",
23568876Seric 				p);
23668876Seric 			blen = sizeof bbuf - 1;
23767545Seric 		}
23868876Seric 		strncpy(bbuf, p, blen);
23968876Seric 		bbuf[blen] = '\0';
24067545Seric 		if (tTd(43, 1))
24167545Seric 			printf("mime8to7: multipart boundary \"%s\"\n", bbuf);
24268517Seric 		for (i = 0; i < MAXMIMENESTING; i++)
24368517Seric 			if (boundaries[i] == NULL)
24468517Seric 				break;
24568517Seric 		if (i >= MAXMIMENESTING)
24668517Seric 			syserr("mime8to7: multipart nesting boundary too deep");
24768517Seric 		else
24868517Seric 		{
24968517Seric 			boundaries[i] = bbuf;
25068517Seric 			boundaries[i + 1] = NULL;
25168517Seric 		}
25269281Seric 		mci->mci_flags |= MCIF_INMIME;
25367545Seric 
25467545Seric 		/* skip the early "comment" prologue */
25568717Seric 		putline("", mci);
25667545Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
25767545Seric 		{
25868517Seric 			bt = mimeboundary(buf, boundaries);
25967545Seric 			if (bt != MBT_NOTSEP)
26067545Seric 				break;
26168847Seric 			putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
26268717Seric 			if (tTd(43, 99))
26368717Seric 				printf("  ...%s", buf);
26467545Seric 		}
26568717Seric 		if (feof(e->e_dfp))
26668717Seric 			bt = MBT_FINAL;
26767545Seric 		while (bt != MBT_FINAL)
26867545Seric 		{
26967545Seric 			auto HDR *hdr = NULL;
27067545Seric 
27167545Seric 			sprintf(buf, "--%s", bbuf);
27267545Seric 			putline(buf, mci);
27368717Seric 			if (tTd(43, 35))
27468717Seric 				printf("  ...%s\n", buf);
27567545Seric 			collect(e->e_dfp, FALSE, FALSE, &hdr, e);
27668717Seric 			if (tTd(43, 101))
27768717Seric 				putline("+++after collect", mci);
27868876Seric 			putheader(mci, hdr, e);
27968717Seric 			if (tTd(43, 101))
28068717Seric 				putline("+++after putheader", mci);
28169281Seric 			bt = mime8to7(mci, hdr, e, boundaries, flags);
28267545Seric 		}
28367545Seric 		sprintf(buf, "--%s--", bbuf);
28467545Seric 		putline(buf, mci);
28568717Seric 		if (tTd(43, 35))
28668717Seric 			printf("  ...%s\n", buf);
28768717Seric 		boundaries[i] = NULL;
28869281Seric 		mci->mci_flags &= ~MCIF_INMIME;
28967545Seric 
29067545Seric 		/* skip the late "comment" epilogue */
29167545Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
29267545Seric 		{
29368517Seric 			bt = mimeboundary(buf, boundaries);
29467545Seric 			if (bt != MBT_NOTSEP)
29567545Seric 				break;
29668847Seric 			putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
29768717Seric 			if (tTd(43, 99))
29868717Seric 				printf("  ...%s", buf);
29967545Seric 		}
30068717Seric 		if (feof(e->e_dfp))
30168717Seric 			bt = MBT_FINAL;
30268717Seric 		if (tTd(43, 3))
30368717Seric 			printf("\t\t\tmime8to7=>%s (multipart)\n",
30468717Seric 				MimeBoundaryNames[bt]);
30567545Seric 		return bt;
30667545Seric 	}
30767545Seric 
30867545Seric 	/*
30968847Seric 	**  Message/* types -- recurse exactly once.
31068847Seric 	*/
31168847Seric 
31268847Seric 	if (strcasecmp(type, "message") == 0)
31368847Seric 	{
31468876Seric 		if (strcasecmp(subtype, "rfc822") != 0)
31568876Seric 		{
31668876Seric 			flags |= M87F_NO8BIT;
31768876Seric 		}
31868876Seric 		else
31968876Seric 		{
32068876Seric 			register char *q;
32168876Seric 			auto HDR *hdr = NULL;
32268847Seric 
32368876Seric 			putline("", mci);
32468847Seric 
32569281Seric 			mci->mci_flags |= MCIF_INMIME;
32668876Seric 			collect(e->e_dfp, FALSE, FALSE, &hdr, e);
32768876Seric 			if (tTd(43, 101))
32868876Seric 				putline("+++after collect", mci);
32968876Seric 			putheader(mci, hdr, e);
33068876Seric 			if (tTd(43, 101))
33168876Seric 				putline("+++after putheader", mci);
33268876Seric 			bt = mime8to7(mci, hdr, e, boundaries, flags);
33369281Seric 			mci->mci_flags &= ~MCIF_INMIME;
33468876Seric 			return bt;
33568876Seric 		}
33668847Seric 	}
33768847Seric 
33868847Seric 	/*
33967545Seric 	**  Non-compound body type
34067545Seric 	**
34167545Seric 	**	Compute the ratio of seven to eight bit characters;
34267545Seric 	**	use that as a heuristic to decide how to do the
34367545Seric 	**	encoding.
34467545Seric 	*/
34567545Seric 
34667545Seric 	sectionsize = sectionhighbits = 0;
34768517Seric 	if (!bitset(M87F_NO8BIT, flags))
34867545Seric 	{
34968515Seric 		/* remember where we were */
35068515Seric 		offset = ftell(e->e_dfp);
35168515Seric 		if (offset == -1)
35268564Seric 			syserr("mime8to7: cannot ftell on df%s", e->e_id);
35368515Seric 
35468515Seric 		/* do a scan of this body type to count character types */
35568515Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
35667545Seric 		{
35768717Seric 			if (mimeboundary(buf, boundaries) != MBT_NOTSEP)
35868515Seric 				break;
35968515Seric 			for (p = buf; *p != '\0'; p++)
36068515Seric 			{
36168515Seric 				/* count bytes with the high bit set */
36268515Seric 				sectionsize++;
36368515Seric 				if (bitset(0200, *p))
36468515Seric 					sectionhighbits++;
36568515Seric 			}
36668515Seric 
36768515Seric 			/*
36868515Seric 			**  Heuristic: if 1/4 of the first 4K bytes are 8-bit,
36968515Seric 			**  assume base64.  This heuristic avoids double-reading
37068515Seric 			**  large graphics or video files.
37168515Seric 			*/
37268515Seric 
37368515Seric 			if (sectionsize >= 4096 &&
37468515Seric 			    sectionhighbits > sectionsize / 4)
37568515Seric 				break;
37667545Seric 		}
37767547Seric 
37868515Seric 		/* return to the original offset for processing */
37968515Seric 		/* XXX use relative seeks to handle >31 bit file sizes? */
38068515Seric 		if (fseek(e->e_dfp, offset, SEEK_SET) < 0)
38168564Seric 			syserr("mime8to7: cannot fseek on df%s", e->e_id);
38268717Seric 		else
38368717Seric 			clearerr(e->e_dfp);
38467545Seric 	}
38567545Seric 
38667547Seric 	/*
38767547Seric 	**  Heuristically determine encoding method.
38867547Seric 	**	If more than 1/8 of the total characters have the
38967547Seric 	**	eighth bit set, use base64; else use quoted-printable.
39068860Seric 	**	However, only encode binary encoded data as base64,
39168860Seric 	**	since otherwise the NL=>CRLF mapping will be a problem.
39267547Seric 	*/
39367547Seric 
39467545Seric 	if (tTd(43, 8))
39567545Seric 	{
39668860Seric 		printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s\n",
39768860Seric 			sectionhighbits, sectionsize,
39868860Seric 			cte == NULL ? "[none]" : cte);
39967545Seric 	}
40068860Seric 	if (cte != NULL && strcasecmp(cte, "binary") == 0)
40168860Seric 		sectionsize = sectionhighbits;
40268717Seric 	linelen = 0;
40368876Seric 	bp = buf;
40467554Seric 	if (sectionhighbits == 0)
40567545Seric 	{
40667554Seric 		/* no encoding necessary */
40768847Seric 		if (cte != NULL)
40867695Seric 		{
40968847Seric 			sprintf(buf, "Content-Transfer-Encoding: %s", cte);
41067695Seric 			putline(buf, mci);
41168717Seric 			if (tTd(43, 36))
41268717Seric 				printf("  ...%s\n", buf);
41367695Seric 		}
41467554Seric 		putline("", mci);
41567554Seric 		mci->mci_flags &= ~MCIF_INHEADER;
41667554Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
41767554Seric 		{
41868517Seric 			bt = mimeboundary(buf, boundaries);
41967554Seric 			if (bt != MBT_NOTSEP)
42067554Seric 				break;
42167554Seric 			putline(buf, mci);
42267554Seric 		}
42368717Seric 		if (feof(e->e_dfp))
42468717Seric 			bt = MBT_FINAL;
42567554Seric 	}
42667554Seric 	else if (sectionsize / 8 < sectionhighbits)
42767554Seric 	{
42867545Seric 		/* use base64 encoding */
42967545Seric 		int c1, c2;
43067545Seric 
43167545Seric 		putline("Content-Transfer-Encoding: base64", mci);
43268717Seric 		if (tTd(43, 36))
43368717Seric 			printf("  ...Content-Transfer-Encoding: base64\n");
43467545Seric 		putline("", mci);
43567545Seric 		mci->mci_flags &= ~MCIF_INHEADER;
43669281Seric 		while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF)
43767545Seric 		{
43867545Seric 			if (linelen > 71)
43967545Seric 			{
44068876Seric 				*bp = '\0';
44168876Seric 				putline(buf, mci);
44267545Seric 				linelen = 0;
44368876Seric 				bp = buf;
44467545Seric 			}
44567545Seric 			linelen += 4;
44668876Seric 			*bp++ = Base64Code[(c1 >> 2)];
44767545Seric 			c1 = (c1 & 0x03) << 4;
44869281Seric 			c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
44967545Seric 			if (c2 == EOF)
45067545Seric 			{
45168876Seric 				*bp++ = Base64Code[c1];
45268876Seric 				*bp++ = '=';
45368876Seric 				*bp++ = '=';
45467545Seric 				break;
45567545Seric 			}
45667545Seric 			c1 |= (c2 >> 4) & 0x0f;
45768876Seric 			*bp++ = Base64Code[c1];
45867545Seric 			c1 = (c2 & 0x0f) << 2;
45969281Seric 			c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
46067545Seric 			if (c2 == EOF)
46167545Seric 			{
46268876Seric 				*bp++ = Base64Code[c1];
46368876Seric 				*bp++ = '=';
46467545Seric 				break;
46567545Seric 			}
46667545Seric 			c1 |= (c2 >> 6) & 0x03;
46768876Seric 			*bp++ = Base64Code[c1];
46868876Seric 			*bp++ = Base64Code[c2 & 0x3f];
46967545Seric 		}
47067545Seric 	}
47167545Seric 	else
47267545Seric 	{
47367545Seric 		/* use quoted-printable encoding */
47467545Seric 		int c1, c2;
47568515Seric 		int fromstate;
47668862Seric 		BITMAP badchars;
47767545Seric 
47868862Seric 		/* set up map of characters that must be mapped */
47968862Seric 		clrbitmap(badchars);
48068862Seric 		for (c1 = 0x00; c1 < 0x20; c1++)
48168862Seric 			setbitn(c1, badchars);
48268862Seric 		clrbitn('\t', badchars);
48368862Seric 		for (c1 = 0x7f; c1 < 0x100; c1++)
48468862Seric 			setbitn(c1, badchars);
48568862Seric 		setbitn('=', badchars);
48668862Seric 		if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags))
48768862Seric 			for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++)
48868862Seric 				setbitn(*p, badchars);
48968862Seric 
49067545Seric 		putline("Content-Transfer-Encoding: quoted-printable", mci);
49168717Seric 		if (tTd(43, 36))
49268717Seric 			printf("  ...Content-Transfer-Encoding: quoted-printable\n");
49367545Seric 		putline("", mci);
49467545Seric 		mci->mci_flags &= ~MCIF_INHEADER;
49568717Seric 		fromstate = 0;
49667554Seric 		c2 = '\n';
49769281Seric 		while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF)
49867545Seric 		{
49967545Seric 			if (c1 == '\n')
50067545Seric 			{
50167545Seric 				if (c2 == ' ' || c2 == '\t')
50267545Seric 				{
50368876Seric 					*bp++ = '=';
50468876Seric 					*bp++ = Base16Code[(c2 >> 4) & 0x0f];
50568876Seric 					*bp++ = Base16Code[c2 & 0x0f];
50668876Seric 					*bp = '\0';
50768876Seric 					putline(buf, mci);
50868876Seric 					bp = buf;
50967545Seric 				}
51068876Seric 				*bp = '\0';
51168876Seric 				putline(buf, mci);
51268515Seric 				linelen = fromstate = 0;
51368876Seric 				bp = buf;
51467545Seric 				c2 = c1;
51567545Seric 				continue;
51667545Seric 			}
51768515Seric 			if (c2 == ' ' && linelen == 4 && fromstate == 4 &&
51868515Seric 			    bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
51967840Seric 			{
52068876Seric 				*bp++ = '=';
52168876Seric 				*bp++ = '2';
52268876Seric 				*bp++ = '0';
52368515Seric 				linelen += 3;
52468515Seric 			}
52568515Seric 			else if (c2 == ' ' || c2 == '\t')
52668515Seric 			{
52768876Seric 				*bp++ = c2;
52867840Seric 				linelen++;
52967840Seric 			}
53067545Seric 			if (linelen > 72)
53167545Seric 			{
53268876Seric 				*bp++ = '=';
53368876Seric 				*bp = '\0';
53468876Seric 				putline(buf, mci);
53568515Seric 				linelen = fromstate = 0;
53668876Seric 				bp = buf;
53767554Seric 				c2 = '\n';
53867545Seric 			}
53968862Seric 			if (bitnset(c1 & 0xff, badchars))
54067545Seric 			{
54168876Seric 				*bp++ = '=';
54268876Seric 				*bp++ = Base16Code[(c1 >> 4) & 0x0f];
54368876Seric 				*bp++ = Base16Code[c1 & 0x0f];
54467545Seric 				linelen += 3;
54567545Seric 			}
54667840Seric 			else if (c1 != ' ' && c1 != '\t')
54767545Seric 			{
54868515Seric 				if (linelen < 4 && c1 == "From"[linelen])
54968515Seric 					fromstate++;
55068876Seric 				*bp++ = c1;
55167545Seric 				linelen++;
55267545Seric 			}
55367545Seric 			c2 = c1;
55467545Seric 		}
55567840Seric 
55667840Seric 		/* output any saved character */
55767840Seric 		if (c2 == ' ' || c2 == '\t')
55867840Seric 		{
55968876Seric 			*bp++ = '=';
56068876Seric 			*bp++ = Base16Code[(c2 >> 4) & 0x0f];
56168876Seric 			*bp++ = Base16Code[c2 & 0x0f];
56268515Seric 			linelen += 3;
56367840Seric 		}
56467545Seric 	}
56567545Seric 	if (linelen > 0)
56668876Seric 	{
56768876Seric 		*bp = '\0';
56868876Seric 		putline(buf, mci);
56968876Seric 	}
57068717Seric 	if (tTd(43, 3))
57168717Seric 		printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
57268717Seric 	return bt;
57367545Seric }
57468515Seric /*
57568515Seric **  MIME_GETCHAR -- get a character for MIME processing
57668515Seric **
57768515Seric **	Treats boundaries as EOF.
57868515Seric **
57968515Seric **	Parameters:
58068515Seric **		fp -- the input file.
58168517Seric **		boundaries -- the current MIME boundaries.
58268717Seric **		btp -- if the return value is EOF, *btp is set to
58368717Seric **			the type of the boundary.
58468515Seric **
58568515Seric **	Returns:
58668515Seric **		The next character in the input stream.
58768515Seric */
58867545Seric 
58967545Seric int
59068717Seric mime_getchar(fp, boundaries, btp)
59167545Seric 	register FILE *fp;
59268517Seric 	char **boundaries;
59368717Seric 	int *btp;
59467545Seric {
59567545Seric 	int c;
59668864Seric 	static u_char *bp = NULL;
59767545Seric 	static int buflen = 0;
59867545Seric 	static bool atbol = TRUE;	/* at beginning of line */
59968717Seric 	static int bt = MBT_SYNTAX;	/* boundary type of next EOF */
60068864Seric 	static u_char buf[128];		/* need not be a full line */
60167545Seric 
60267545Seric 	if (buflen > 0)
60367545Seric 	{
60467545Seric 		buflen--;
60567545Seric 		return *bp++;
60667545Seric 	}
60768515Seric 	bp = buf;
60868515Seric 	buflen = 0;
60968876Seric 	c = getc(fp);
61068515Seric 	if (c == '\n')
61168515Seric 	{
61268515Seric 		/* might be part of a MIME boundary */
61368515Seric 		*bp++ = c;
61468515Seric 		atbol = TRUE;
61568876Seric 		c = getc(fp);
61668876Seric 		if (c == '\n')
61768876Seric 		{
61868876Seric 			ungetc(c, fp);
61968876Seric 			return c;
62068876Seric 		}
62168515Seric 	}
62268515Seric 	if (c != EOF)
62368515Seric 		*bp++ = c;
62468717Seric 	else
62568717Seric 		bt = MBT_FINAL;
62668517Seric 	if (atbol && c == '-')
62767545Seric 	{
62867545Seric 		/* check for a message boundary */
62968876Seric 		c = getc(fp);
63067545Seric 		if (c != '-')
63167545Seric 		{
63267545Seric 			if (c != EOF)
63368515Seric 				*bp++ = c;
63468717Seric 			else
63568717Seric 				bt = MBT_FINAL;
63668515Seric 			buflen = bp - buf - 1;
63768515Seric 			bp = buf;
63868515Seric 			return *bp++;
63967545Seric 		}
64067545Seric 
64167545Seric 		/* got "--", now check for rest of separator */
64267545Seric 		*bp++ = '-';
64368847Seric 		while (bp < &buf[sizeof buf - 2] &&
64468876Seric 		       (c = getc(fp)) != EOF && c != '\n')
64567545Seric 		{
64667545Seric 			*bp++ = c;
64767545Seric 		}
64867545Seric 		*bp = '\0';
64968717Seric 		bt = mimeboundary(&buf[1], boundaries);
65068717Seric 		switch (bt)
65167545Seric 		{
65267545Seric 		  case MBT_FINAL:
65367545Seric 		  case MBT_INTERMED:
65467545Seric 			/* we have a message boundary */
65567545Seric 			buflen = 0;
65668717Seric 			*btp = bt;
65767545Seric 			return EOF;
65867545Seric 		}
65967545Seric 
66067545Seric 		atbol = c == '\n';
66167545Seric 		if (c != EOF)
66267545Seric 			*bp++ = c;
66367545Seric 	}
66467545Seric 
66568515Seric 	buflen = bp - buf - 1;
66668515Seric 	if (buflen < 0)
66768717Seric 	{
66868717Seric 		*btp = bt;
66968515Seric 		return EOF;
67068717Seric 	}
67168515Seric 	bp = buf;
67268515Seric 	return *bp++;
67367545Seric }
67467545Seric /*
67568847Seric **  MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF
67668847Seric **
67768847Seric **	Parameters:
67868847Seric **		fp -- the input file.
67968847Seric **		boundaries -- the current MIME boundaries.
68068847Seric **		btp -- if the return value is EOF, *btp is set to
68168847Seric **			the type of the boundary.
68268847Seric **
68368847Seric **	Returns:
68468847Seric **		The next character in the input stream.
68568847Seric */
68668847Seric 
68768847Seric int
68868847Seric mime_getchar_crlf(fp, boundaries, btp)
68968847Seric 	register FILE *fp;
69068847Seric 	char **boundaries;
69168847Seric 	int *btp;
69268847Seric {
69368847Seric 	static bool sendlf = FALSE;
69468847Seric 	int c;
69568847Seric 
69668847Seric 	if (sendlf)
69768847Seric 	{
69868847Seric 		sendlf = FALSE;
69968847Seric 		return '\n';
70068847Seric 	}
70168847Seric 	c = mime_getchar(fp, boundaries, btp);
70268847Seric 	if (c == '\n')
70368847Seric 	{
70468847Seric 		sendlf = TRUE;
70568847Seric 		return '\r';
70668847Seric 	}
70768847Seric 	return c;
70868847Seric }
70968847Seric /*
71067545Seric **  MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
71167545Seric **
71267545Seric **	Parameters:
71367545Seric **		line -- the input line.
71468517Seric **		boundaries -- the set of currently pending boundaries.
71567545Seric **
71667545Seric **	Returns:
71767545Seric **		MBT_NOTSEP -- if this is not a separator line
71867545Seric **		MBT_INTERMED -- if this is an intermediate separator
71967545Seric **		MBT_FINAL -- if this is a final boundary
72067545Seric **		MBT_SYNTAX -- if this is a boundary for the wrong
72167545Seric **			enclosure -- i.e., a syntax error.
72267545Seric */
72367545Seric 
72467545Seric int
72568517Seric mimeboundary(line, boundaries)
72667545Seric 	register char *line;
72768517Seric 	char **boundaries;
72867545Seric {
72969474Seric 	int type = MBT_NOTSEP;
73067545Seric 	int i;
73168517Seric 	int savec;
73267545Seric 
73368517Seric 	if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
73467545Seric 		return MBT_NOTSEP;
73567545Seric 	i = strlen(line);
73667545Seric 	if (line[i - 1] == '\n')
73767545Seric 		i--;
73869474Seric 
73969474Seric 	/* strip off trailing whitespace */
74068515Seric 	while (line[i - 1] == ' ' || line[i - 1] == '\t')
74168515Seric 		i--;
74269474Seric 	savec = line[i];
74369474Seric 	line[i] = '\0';
74469474Seric 
74569474Seric 	if (tTd(43, 5))
74669474Seric 		printf("mimeboundary: line=\"%s\"... ", line);
74769474Seric 
74869474Seric 	/* check for this as an intermediate boundary */
74969474Seric 	if (isboundary(&line[2], boundaries) >= 0)
75069474Seric 		type = MBT_INTERMED;
75169474Seric 	else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0)
75267545Seric 	{
75369474Seric 		/* check for a final boundary */
75469474Seric 		line[i - 2] = '\0';
75569474Seric 		if (isboundary(&line[2], boundaries) >= 0)
75669474Seric 			type = MBT_FINAL;
75769474Seric 		line[i - 2] = '-';
75867545Seric 	}
75967545Seric 
76068517Seric 	line[i] = savec;
76167545Seric 	if (tTd(43, 5))
76268717Seric 		printf("%s\n", MimeBoundaryNames[type]);
76367545Seric 	return type;
76467545Seric }
76567896Seric /*
76667896Seric **  DEFCHARSET -- return default character set for message
76767896Seric **
76867896Seric **	The first choice for character set is for the mailer
76967896Seric **	corresponding to the envelope sender.  If neither that
77067896Seric **	nor the global configuration file has a default character
77167896Seric **	set defined, return "unknown-8bit" as recommended by
77267896Seric **	RFC 1428 section 3.
77367896Seric **
77467896Seric **	Parameters:
77567896Seric **		e -- the envelope for this message.
77667896Seric **
77767896Seric **	Returns:
77867896Seric **		The default character set for that mailer.
77967896Seric */
78067896Seric 
78167896Seric char *
78267896Seric defcharset(e)
78367896Seric 	register ENVELOPE *e;
78467896Seric {
78567896Seric 	if (e != NULL && e->e_from.q_mailer != NULL &&
78667896Seric 	    e->e_from.q_mailer->m_defcharset != NULL)
78767896Seric 		return e->e_from.q_mailer->m_defcharset;
78867896Seric 	if (DefaultCharSet != NULL)
78967896Seric 		return DefaultCharSet;
79067896Seric 	return "unknown-8bit";
79167896Seric }
79268517Seric /*
79368517Seric **  ISBOUNDARY -- is a given string a currently valid boundary?
79468517Seric **
79568517Seric **	Parameters:
79668517Seric **		line -- the current input line.
79768517Seric **		boundaries -- the list of valid boundaries.
79868517Seric **
79968517Seric **	Returns:
80068517Seric **		The index number in boundaries if the line is found.
80168517Seric **		-1 -- otherwise.
80268517Seric **
80368517Seric */
80468517Seric 
80568517Seric int
80668517Seric isboundary(line, boundaries)
80768517Seric 	char *line;
80868517Seric 	char **boundaries;
80968517Seric {
81068517Seric 	register int i;
81168517Seric 
81268711Seric 	for (i = 0; boundaries[i] != NULL; i++)
81368517Seric 	{
81468517Seric 		if (strcmp(line, boundaries[i]) == 0)
81568517Seric 			return i;
81668517Seric 	}
81768517Seric 	return -1;
81868517Seric }
819*69480Seric 
820*69480Seric #endif /* MIME */
821