xref: /csrg-svn/usr.sbin/sendmail/src/mime.c (revision 68517)
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*68517Seric static char sccsid[] = "@(#)mime.c	8.11 (Berkeley) 03/11/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 
4467547Seric static int	MimeBoundaryType;	/* internal linkage */
4567545Seric /*
4667545Seric **  MIME8TO7 -- output 8 bit body in 7 bit format
4767545Seric **
4867545Seric **	The header has already been output -- this has to do the
4967545Seric **	8 to 7 bit conversion.  It would be easy if we didn't have
5067545Seric **	to deal with nested formats (multipart/xxx and message/rfc822).
5167545Seric **
5267545Seric **	We won't be called if we don't have to do a conversion, and
5367545Seric **	appropriate MIME-Version: and Content-Type: fields have been
5467545Seric **	output.  Any Content-Transfer-Encoding: field has not been
5567545Seric **	output, and we can add it here.
5667545Seric **
5767545Seric **	Parameters:
5867545Seric **		mci -- mailer connection information.
5967545Seric **		header -- the header for this body part.
6067545Seric **		e -- envelope.
61*68517Seric **		boundaries -- the currently pending message boundaries.
62*68517Seric **			NULL if we are processing the outer portion.
63*68517Seric **		flags -- to tweak processing.
6467545Seric **
6567545Seric **	Returns:
6667545Seric **		An indicator of what terminated the message part:
6767545Seric **		  MBT_FINAL -- the final boundary
6867545Seric **		  MBT_INTERMED -- an intermediate boundary
6967545Seric **		  MBT_NOTSEP -- an end of file
7067545Seric */
7167545Seric 
72*68517Seric struct args
73*68517Seric {
74*68517Seric 	char	*field;		/* name of field */
75*68517Seric 	char	*value;		/* value of that field */
76*68517Seric };
77*68517Seric 
7867545Seric int
79*68517Seric mime8to7(mci, header, e, boundaries, flags)
8067545Seric 	register MCI *mci;
8168515Seric 	HDR *header; register ENVELOPE *e;
82*68517Seric 	char **boundaries;
83*68517Seric 	int flags;
8467545Seric {
8567545Seric 	register char *p;
8667545Seric 	int linelen;
8767545Seric 	int bt;
8867545Seric 	off_t offset;
8967545Seric 	size_t sectionsize, sectionhighbits;
90*68517Seric 	int i;
91*68517Seric 	char *type;
92*68517Seric 	char *subtype;
93*68517Seric 	char **pvp;
94*68517Seric 	int argc = 0;
95*68517Seric 	struct args argv[MAXMIMEARGS];
9667545Seric 	char bbuf[128];
9767545Seric 	char buf[MAXLINE];
98*68517Seric 	char pvpbuf[MAXLINE];
9967545Seric 
10067545Seric 	if (tTd(43, 1))
10167545Seric 	{
10267545Seric 		printf("mime8to7: boundary=%s\n",
103*68517Seric 			boundaries[0] == NULL ? "<none>" : boundaries[0]);
104*68517Seric 		for (i = 1; boundaries[i] != NULL; i++)
105*68517Seric 			printf("\tboundaries[i]\n");
10667545Seric 	}
107*68517Seric 	type = subtype = "-none-";
10867545Seric 	p = hvalue("Content-Type", header);
109*68517Seric 	if (p != NULL &&
110*68517Seric 	    (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL)) != NULL &&
111*68517Seric 	    pvp[0] != NULL)
11267545Seric 	{
113*68517Seric 		type = *pvp++;
114*68517Seric 		if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
115*68517Seric 		    *++pvp != NULL)
116*68517Seric 		{
117*68517Seric 			subtype = *pvp++;
118*68517Seric 		}
119*68517Seric 
120*68517Seric 		/* break out parameters */
121*68517Seric 		while (*pvp != NULL && argc < MAXMIMEARGS)
122*68517Seric 		{
123*68517Seric 			/* skip to semicolon separator */
124*68517Seric 			while (*pvp != NULL && strcmp(*pvp, ";") != 0)
125*68517Seric 				pvp++;
126*68517Seric 			if (*pvp++ == NULL || *pvp == NULL)
127*68517Seric 				break;
128*68517Seric 
129*68517Seric 			/* extract field name */
130*68517Seric 			argv[argc].field = *pvp++;
131*68517Seric 
132*68517Seric 			/* see if there is a value */
133*68517Seric 			if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
134*68517Seric 			    (*++pvp == NULL || strcmp(*pvp, ";") != 0))
135*68517Seric 			{
136*68517Seric 				argv[argc].value = *pvp;
137*68517Seric 				argc++;
138*68517Seric 			}
139*68517Seric 		}
140*68517Seric 	}
141*68517Seric 	if (strcasecmp(type, "multipart") == 0)
142*68517Seric 	{
14367545Seric 		register char *q;
14467545Seric 
145*68517Seric 		for (i = 0; i < argc; i++)
14667545Seric 		{
147*68517Seric 			if (strcasecmp(argv[i].field, "boundary") == 0)
148*68517Seric 				break;
149*68517Seric 		}
150*68517Seric 		if (i >= argc)
151*68517Seric 		{
15267545Seric 			syserr("mime8to7: Content-Type: %s missing boundary", p);
15367545Seric 			p = "---";
15467545Seric 		}
15567545Seric 		else
156*68517Seric 			p = argv[i].value;
15767545Seric 		if (*p == '"')
15867545Seric 			q = strchr(p, '"');
15967545Seric 		else
16067545Seric 			q = p + strlen(p);
16167545Seric 		if (q - p > sizeof bbuf - 1)
16267545Seric 		{
16367545Seric 			syserr("mime8to7: multipart boundary \"%.*s\" too long",
16467545Seric 				q - p, p);
16567545Seric 			q = p + sizeof bbuf - 1;
16667545Seric 		}
16767545Seric 		strncpy(bbuf, p, q - p);
16867545Seric 		bbuf[q - p] = '\0';
16967545Seric 		if (tTd(43, 1))
17067545Seric 		{
17167545Seric 			printf("mime8to7: multipart boundary \"%s\"\n", bbuf);
17267545Seric 		}
173*68517Seric 		for (i = 0; i < MAXMIMENESTING; i++)
174*68517Seric 			if (boundaries[i] == NULL)
175*68517Seric 				break;
176*68517Seric 		if (i >= MAXMIMENESTING)
177*68517Seric 			syserr("mime8to7: multipart nesting boundary too deep");
178*68517Seric 		else
179*68517Seric 		{
180*68517Seric 			boundaries[i] = bbuf;
181*68517Seric 			boundaries[i + 1] = NULL;
182*68517Seric 		}
18367545Seric 
184*68517Seric 		/* flag subtypes that can't have any 8-bit data */
185*68517Seric 		if (strcasecmp(subtype, "signed") == 0)
186*68517Seric 			flags |= M87F_NO8BIT;
187*68517Seric 
18867545Seric 		/* skip the early "comment" prologue */
18967545Seric 		bt = MBT_FINAL;
19067545Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
19167545Seric 		{
192*68517Seric 			bt = mimeboundary(buf, boundaries);
19367545Seric 			if (bt != MBT_NOTSEP)
19467545Seric 				break;
19567545Seric 			putline(buf, mci);
19667545Seric 		}
19767545Seric 		while (bt != MBT_FINAL)
19867545Seric 		{
19967545Seric 			auto HDR *hdr = NULL;
20067545Seric 
20167545Seric 			sprintf(buf, "--%s", bbuf);
20267545Seric 			putline(buf, mci);
20367545Seric 			collect(e->e_dfp, FALSE, FALSE, &hdr, e);
20467936Seric 			putheader(mci, hdr, e, 0);
205*68517Seric 			bt = mime8to7(mci, hdr, e, boundaries, flags);
20667545Seric 		}
20767545Seric 		sprintf(buf, "--%s--", bbuf);
20867545Seric 		putline(buf, mci);
20967545Seric 
21067545Seric 		/* skip the late "comment" epilogue */
21167545Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
21267545Seric 		{
21367545Seric 			putline(buf, mci);
214*68517Seric 			bt = mimeboundary(buf, boundaries);
21567545Seric 			if (bt != MBT_NOTSEP)
21667545Seric 				break;
21767545Seric 		}
218*68517Seric 		boundaries[i] = NULL;
21967545Seric 		return bt;
22067545Seric 	}
22167545Seric 
22267545Seric 	/*
22367545Seric 	**  Non-compound body type
22467545Seric 	**
22567545Seric 	**	Compute the ratio of seven to eight bit characters;
22667545Seric 	**	use that as a heuristic to decide how to do the
22767545Seric 	**	encoding.
22867545Seric 	*/
22967545Seric 
230*68517Seric 	/* handle types that cannot have 8-bit data internally */
231*68517Seric 	sprintf(buf, "%s/%s", type, subtype);
232*68517Seric 	if (wordinclass(buf, 'n'))
233*68517Seric 		flags |= M87F_NO8BIT;
234*68517Seric 
23567545Seric 	sectionsize = sectionhighbits = 0;
236*68517Seric 	if (!bitset(M87F_NO8BIT, flags))
23767545Seric 	{
23868515Seric 		/* remember where we were */
23968515Seric 		offset = ftell(e->e_dfp);
24068515Seric 		if (offset == -1)
24168515Seric 			syserr("mime8to7: cannot ftell on %s", e->e_df);
24268515Seric 
24368515Seric 		/* do a scan of this body type to count character types */
24468515Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
24567545Seric 		{
246*68517Seric 			bt = mimeboundary(buf, boundaries);
24768515Seric 			if (bt != MBT_NOTSEP)
24868515Seric 				break;
24968515Seric 			for (p = buf; *p != '\0'; p++)
25068515Seric 			{
25168515Seric 				/* count bytes with the high bit set */
25268515Seric 				sectionsize++;
25368515Seric 				if (bitset(0200, *p))
25468515Seric 					sectionhighbits++;
25568515Seric 			}
25668515Seric 
25768515Seric 			/*
25868515Seric 			**  Heuristic: if 1/4 of the first 4K bytes are 8-bit,
25968515Seric 			**  assume base64.  This heuristic avoids double-reading
26068515Seric 			**  large graphics or video files.
26168515Seric 			*/
26268515Seric 
26368515Seric 			if (sectionsize >= 4096 &&
26468515Seric 			    sectionhighbits > sectionsize / 4)
26568515Seric 				break;
26667545Seric 		}
26768515Seric 		if (feof(e->e_dfp))
26868515Seric 			bt = MBT_FINAL;
26967547Seric 
27068515Seric 		/* return to the original offset for processing */
27168515Seric 		/* XXX use relative seeks to handle >31 bit file sizes? */
27268515Seric 		if (fseek(e->e_dfp, offset, SEEK_SET) < 0)
27368515Seric 			syserr("mime8to7: cannot fseek on %s", e->e_df);
27467545Seric 	}
27567545Seric 
27667547Seric 	/*
27767547Seric 	**  Heuristically determine encoding method.
27867547Seric 	**	If more than 1/8 of the total characters have the
27967547Seric 	**	eighth bit set, use base64; else use quoted-printable.
28067547Seric 	*/
28167547Seric 
28267545Seric 	if (tTd(43, 8))
28367545Seric 	{
28467545Seric 		printf("mime8to7: %ld high bits in %ld bytes\n",
28567545Seric 			sectionhighbits, sectionsize);
28667545Seric 	}
28767554Seric 	if (sectionhighbits == 0)
28867545Seric 	{
28967554Seric 		/* no encoding necessary */
29067695Seric 		p = hvalue("content-transfer-encoding", header);
29167695Seric 		if (p != NULL)
29267695Seric 		{
29367695Seric 			sprintf(buf, "Content-Transfer-Encoding: %s", p);
29467695Seric 			putline(buf, mci);
29567695Seric 		}
29667554Seric 		putline("", mci);
29767554Seric 		mci->mci_flags &= ~MCIF_INHEADER;
29867554Seric 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
29967554Seric 		{
300*68517Seric 			bt = mimeboundary(buf, boundaries);
30167554Seric 			if (bt != MBT_NOTSEP)
30267554Seric 				break;
30367554Seric 			if (buf[0] == 'F' &&
30467554Seric 			    bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
30567554Seric 			    strncmp(buf, "From ", 5) == 0)
30667554Seric 				(void) putc('>', mci->mci_out);
30767554Seric 			putline(buf, mci);
30867554Seric 		}
30967554Seric 	}
31067554Seric 	else if (sectionsize / 8 < sectionhighbits)
31167554Seric 	{
31267545Seric 		/* use base64 encoding */
31367545Seric 		int c1, c2;
31467545Seric 
31567545Seric 		putline("Content-Transfer-Encoding: base64", mci);
31667545Seric 		putline("", mci);
31767545Seric 		mci->mci_flags &= ~MCIF_INHEADER;
31867545Seric 		linelen = 0;
319*68517Seric 		while ((c1 = mime_getchar(e->e_dfp, boundaries)) != EOF)
32067545Seric 		{
32167545Seric 			if (linelen > 71)
32267545Seric 			{
32367545Seric 				fputs(mci->mci_mailer->m_eol, mci->mci_out);
32467545Seric 				linelen = 0;
32567545Seric 			}
32667545Seric 			linelen += 4;
32767545Seric 			fputc(Base64Code[c1 >> 2], mci->mci_out);
32867545Seric 			c1 = (c1 & 0x03) << 4;
329*68517Seric 			c2 = mime_getchar(e->e_dfp, boundaries);
33067545Seric 			if (c2 == EOF)
33167545Seric 			{
33267545Seric 				fputc(Base64Code[c1], mci->mci_out);
33367545Seric 				fputc('=', mci->mci_out);
33467545Seric 				fputc('=', mci->mci_out);
33567545Seric 				break;
33667545Seric 			}
33767545Seric 			c1 |= (c2 >> 4) & 0x0f;
33867545Seric 			fputc(Base64Code[c1], mci->mci_out);
33967545Seric 			c1 = (c2 & 0x0f) << 2;
340*68517Seric 			c2 = mime_getchar(e->e_dfp, boundaries);
34167545Seric 			if (c2 == EOF)
34267545Seric 			{
34367545Seric 				fputc(Base64Code[c1], mci->mci_out);
34467545Seric 				fputc('=', mci->mci_out);
34567545Seric 				break;
34667545Seric 			}
34767545Seric 			c1 |= (c2 >> 6) & 0x03;
34867545Seric 			fputc(Base64Code[c1], mci->mci_out);
34967545Seric 			fputc(Base64Code[c2 & 0x3f], mci->mci_out);
35067545Seric 		}
35167545Seric 	}
35267545Seric 	else
35367545Seric 	{
35467545Seric 		/* use quoted-printable encoding */
35567545Seric 		int c1, c2;
35668515Seric 		int fromstate;
35767545Seric 
35867545Seric 		putline("Content-Transfer-Encoding: quoted-printable", mci);
35967545Seric 		putline("", mci);
36067545Seric 		mci->mci_flags &= ~MCIF_INHEADER;
36168515Seric 		linelen = fromstate = 0;
36267554Seric 		c2 = '\n';
363*68517Seric 		while ((c1 = mime_getchar(e->e_dfp, boundaries)) != EOF)
36467545Seric 		{
36567545Seric 			if (c1 == '\n')
36667545Seric 			{
36767545Seric 				if (c2 == ' ' || c2 == '\t')
36867545Seric 				{
36967545Seric 					fputc('=', mci->mci_out);
37067840Seric 					fputc(Base16Code[(c2 >> 4) & 0x0f],
37167840Seric 								mci->mci_out);
37267840Seric 					fputc(Base16Code[c2 & 0x0f],
37367840Seric 								mci->mci_out);
37467840Seric 					fputs(mci->mci_mailer->m_eol,
37567840Seric 								mci->mci_out);
37667545Seric 				}
37767545Seric 				fputs(mci->mci_mailer->m_eol, mci->mci_out);
37868515Seric 				linelen = fromstate = 0;
37967545Seric 				c2 = c1;
38067545Seric 				continue;
38167545Seric 			}
38268515Seric 			if (c2 == ' ' && linelen == 4 && fromstate == 4 &&
38368515Seric 			    bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
38467840Seric 			{
38568515Seric 				fputs("=20", mci->mci_out);
38668515Seric 				linelen += 3;
38768515Seric 			}
38868515Seric 			else if (c2 == ' ' || c2 == '\t')
38968515Seric 			{
39067840Seric 				fputc(c2, mci->mci_out);
39167840Seric 				linelen++;
39267840Seric 			}
39367545Seric 			if (linelen > 72)
39467545Seric 			{
39567545Seric 				fputc('=', mci->mci_out);
39667545Seric 				fputs(mci->mci_mailer->m_eol, mci->mci_out);
39768515Seric 				linelen = fromstate = 0;
39867554Seric 				c2 = '\n';
39967545Seric 			}
40067761Seric 			if (c2 == '\n' && c1 == '.' &&
40167761Seric 				 bitnset(M_XDOT, mci->mci_mailer->m_flags))
40267761Seric 			{
40367761Seric 				fputc('.', mci->mci_out);
40467761Seric 				linelen++;
40567761Seric 			}
40667547Seric 			if ((c1 < 0x20 && c1 != '\t') || c1 >= 0x7f || c1 == '=')
40767545Seric 			{
40867545Seric 				fputc('=', mci->mci_out);
40967545Seric 				fputc(Base16Code[(c1 >> 4) & 0x0f], mci->mci_out);
41067545Seric 				fputc(Base16Code[c1 & 0x0f], mci->mci_out);
41167545Seric 				linelen += 3;
41267545Seric 			}
41367840Seric 			else if (c1 != ' ' && c1 != '\t')
41467545Seric 			{
41568515Seric 				if (linelen < 4 && c1 == "From"[linelen])
41668515Seric 					fromstate++;
41767545Seric 				fputc(c1, mci->mci_out);
41867545Seric 				linelen++;
41967545Seric 			}
42067545Seric 			c2 = c1;
42167545Seric 		}
42267840Seric 
42367840Seric 		/* output any saved character */
42467840Seric 		if (c2 == ' ' || c2 == '\t')
42567840Seric 		{
42668515Seric 			fputc('=', mci->mci_out);
42768515Seric 			fputc(Base16Code[(c2 >> 4) & 0x0f], mci->mci_out);
42868515Seric 			fputc(Base16Code[c2 & 0x0f], mci->mci_out);
42968515Seric 			linelen += 3;
43067840Seric 		}
43167545Seric 	}
43267545Seric 	if (linelen > 0)
43367545Seric 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
43467547Seric 	return MimeBoundaryType;
43567545Seric }
43668515Seric /*
43768515Seric **  MIME_GETCHAR -- get a character for MIME processing
43868515Seric **
43968515Seric **	Treats boundaries as EOF.
44068515Seric **
44168515Seric **	Parameters:
44268515Seric **		fp -- the input file.
443*68517Seric **		boundaries -- the current MIME boundaries.
44468515Seric **
44568515Seric **	Returns:
44668515Seric **		The next character in the input stream.
44768515Seric */
44867545Seric 
44967545Seric int
450*68517Seric mime_getchar(fp, boundaries)
45167545Seric 	register FILE *fp;
452*68517Seric 	char **boundaries;
45367545Seric {
45467545Seric 	int c;
45567545Seric 	static char *bp = NULL;
45667545Seric 	static int buflen = 0;
45767545Seric 	static bool atbol = TRUE;	/* at beginning of line */
45867545Seric 	static char buf[128];		/* need not be a full line */
45967545Seric 
46067545Seric 	if (buflen > 0)
46167545Seric 	{
46267545Seric 		buflen--;
46367545Seric 		return *bp++;
46467545Seric 	}
46568515Seric 	bp = buf;
46668515Seric 	buflen = 0;
46767545Seric 	c = fgetc(fp);
46868515Seric 	if (c == '\n')
46968515Seric 	{
47068515Seric 		/* might be part of a MIME boundary */
47168515Seric 		*bp++ = c;
47268515Seric 		atbol = TRUE;
47368515Seric 		c = fgetc(fp);
47468515Seric 	}
47568515Seric 	if (c != EOF)
47668515Seric 		*bp++ = c;
477*68517Seric 	if (atbol && c == '-')
47867545Seric 	{
47967545Seric 		/* check for a message boundary */
48067545Seric 		c = fgetc(fp);
48167545Seric 		if (c != '-')
48267545Seric 		{
48367545Seric 			if (c != EOF)
48468515Seric 				*bp++ = c;
48568515Seric 			buflen = bp - buf - 1;
48668515Seric 			bp = buf;
48768515Seric 			return *bp++;
48867545Seric 		}
48967545Seric 
49067545Seric 		/* got "--", now check for rest of separator */
49167545Seric 		*bp++ = '-';
49267545Seric 		while (bp < &buf[sizeof buf - 1] &&
49367545Seric 		       (c = fgetc(fp)) != EOF && c != '\n')
49467545Seric 		{
49567545Seric 			*bp++ = c;
49667545Seric 		}
49767545Seric 		*bp = '\0';
498*68517Seric 		MimeBoundaryType = mimeboundary(buf, boundaries);
49967547Seric 		switch (MimeBoundaryType)
50067545Seric 		{
50167545Seric 		  case MBT_FINAL:
50267545Seric 		  case MBT_INTERMED:
50367545Seric 			/* we have a message boundary */
50467545Seric 			buflen = 0;
50567545Seric 			return EOF;
50667545Seric 		}
50767545Seric 
50867545Seric 		atbol = c == '\n';
50967545Seric 		if (c != EOF)
51067545Seric 			*bp++ = c;
51167545Seric 	}
51267545Seric 
51368515Seric 	buflen = bp - buf - 1;
51468515Seric 	if (buflen < 0)
51568515Seric 		return EOF;
51668515Seric 	bp = buf;
51768515Seric 	return *bp++;
51867545Seric }
51967545Seric /*
52067545Seric **  MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
52167545Seric **
52267545Seric **	Parameters:
52367545Seric **		line -- the input line.
524*68517Seric **		boundaries -- the set of currently pending boundaries.
52567545Seric **
52667545Seric **	Returns:
52767545Seric **		MBT_NOTSEP -- if this is not a separator line
52867545Seric **		MBT_INTERMED -- if this is an intermediate separator
52967545Seric **		MBT_FINAL -- if this is a final boundary
53067545Seric **		MBT_SYNTAX -- if this is a boundary for the wrong
53167545Seric **			enclosure -- i.e., a syntax error.
53267545Seric */
53367545Seric 
53467545Seric int
535*68517Seric mimeboundary(line, boundaries)
53667545Seric 	register char *line;
537*68517Seric 	char **boundaries;
53867545Seric {
53967545Seric 	int type;
54067545Seric 	int i;
541*68517Seric 	int savec;
54267545Seric 
543*68517Seric 	if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
54467545Seric 		return MBT_NOTSEP;
54567545Seric 	if (tTd(43, 5))
546*68517Seric 		printf("mimeboundary: line=\"%s\"... ", line);
54767545Seric 	i = strlen(line);
54867545Seric 	if (line[i - 1] == '\n')
54967545Seric 		i--;
55068515Seric 	while (line[i - 1] == ' ' || line[i - 1] == '\t')
55168515Seric 		i--;
55267545Seric 	if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0)
55367545Seric 	{
55467545Seric 		type = MBT_FINAL;
55567545Seric 		i -= 2;
55667545Seric 	}
55767545Seric 	else
55867545Seric 		type = MBT_INTERMED;
55967545Seric 
560*68517Seric 	savec = line[i];
561*68517Seric 	line[i] = '\0';
56267545Seric 	/* XXX should check for improper nesting here */
563*68517Seric 	if (isboundary(&line[2], boundaries) < 0)
56467545Seric 		type = MBT_NOTSEP;
565*68517Seric 	line[i] = savec;
56667545Seric 	if (tTd(43, 5))
56767545Seric 		printf("%d\n", type);
56867545Seric 	return type;
56967545Seric }
57067896Seric /*
57167896Seric **  DEFCHARSET -- return default character set for message
57267896Seric **
57367896Seric **	The first choice for character set is for the mailer
57467896Seric **	corresponding to the envelope sender.  If neither that
57567896Seric **	nor the global configuration file has a default character
57667896Seric **	set defined, return "unknown-8bit" as recommended by
57767896Seric **	RFC 1428 section 3.
57867896Seric **
57967896Seric **	Parameters:
58067896Seric **		e -- the envelope for this message.
58167896Seric **
58267896Seric **	Returns:
58367896Seric **		The default character set for that mailer.
58467896Seric */
58567896Seric 
58667896Seric char *
58767896Seric defcharset(e)
58867896Seric 	register ENVELOPE *e;
58967896Seric {
59067896Seric 	if (e != NULL && e->e_from.q_mailer != NULL &&
59167896Seric 	    e->e_from.q_mailer->m_defcharset != NULL)
59267896Seric 		return e->e_from.q_mailer->m_defcharset;
59367896Seric 	if (DefaultCharSet != NULL)
59467896Seric 		return DefaultCharSet;
59567896Seric 	return "unknown-8bit";
59667896Seric }
597*68517Seric /*
598*68517Seric **  ISBOUNDARY -- is a given string a currently valid boundary?
599*68517Seric **
600*68517Seric **	Parameters:
601*68517Seric **		line -- the current input line.
602*68517Seric **		boundaries -- the list of valid boundaries.
603*68517Seric **
604*68517Seric **	Returns:
605*68517Seric **		The index number in boundaries if the line is found.
606*68517Seric **		-1 -- otherwise.
607*68517Seric **
608*68517Seric */
609*68517Seric 
610*68517Seric int
611*68517Seric isboundary(line, boundaries)
612*68517Seric 	char *line;
613*68517Seric 	char **boundaries;
614*68517Seric {
615*68517Seric 	register int i;
616*68517Seric 
617*68517Seric 	i = 0;
618*68517Seric 	while (boundaries[i] != NULL)
619*68517Seric 	{
620*68517Seric 		if (strcmp(line, boundaries[i]) == 0)
621*68517Seric 			return i;
622*68517Seric 	}
623*68517Seric 	return -1;
624*68517Seric }
625