xref: /original-bsd/usr.sbin/sendmail/src/mime.c (revision 27393bdf)
1 /*
2  * Copyright (c) 1994 Eric P. Allman
3  * Copyright (c) 1994
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 # include "sendmail.h"
10 # include <string.h>
11 
12 #ifndef lint
13 static char sccsid[] = "@(#)mime.c	8.19 (Berkeley) 04/24/95";
14 #endif /* not lint */
15 
16 /*
17 **  MIME support.
18 **
19 **	I am indebted to John Beck of Hewlett-Packard, who contributed
20 **	his code to me for inclusion.  As it turns out, I did not use
21 **	his code since he used a "minimum change" approach that used
22 **	several temp files, and I wanted a "minimum impact" approach
23 **	that would avoid copying.  However, looking over his code
24 **	helped me cement my understanding of the problem.
25 **
26 **	I also looked at, but did not directly use, Nathaniel
27 **	Borenstein's "code.c" module.  Again, it functioned as
28 **	a file-to-file translator, which did not fit within my
29 **	design bounds, but it was a useful base for understanding
30 **	the problem.
31 */
32 
33 
34 /* character set for hex and base64 encoding */
35 char	Base16Code[] =	"0123456789ABCDEF";
36 char	Base64Code[] =	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
37 
38 /* types of MIME boundaries */
39 #define MBT_SYNTAX	0	/* syntax error */
40 #define MBT_NOTSEP	1	/* not a boundary */
41 #define MBT_INTERMED	2	/* intermediate boundary (no trailing --) */
42 #define MBT_FINAL	3	/* final boundary (trailing -- included) */
43 
44 static char	*MimeBoundaryNames[] =
45 {
46 	"SYNTAX",	"NOTSEP",	"INTERMED",	"FINAL"
47 };
48 /*
49 **  MIME8TO7 -- output 8 bit body in 7 bit format
50 **
51 **	The header has already been output -- this has to do the
52 **	8 to 7 bit conversion.  It would be easy if we didn't have
53 **	to deal with nested formats (multipart/xxx and message/rfc822).
54 **
55 **	We won't be called if we don't have to do a conversion, and
56 **	appropriate MIME-Version: and Content-Type: fields have been
57 **	output.  Any Content-Transfer-Encoding: field has not been
58 **	output, and we can add it here.
59 **
60 **	Parameters:
61 **		mci -- mailer connection information.
62 **		header -- the header for this body part.
63 **		e -- envelope.
64 **		boundaries -- the currently pending message boundaries.
65 **			NULL if we are processing the outer portion.
66 **		flags -- to tweak processing.
67 **
68 **	Returns:
69 **		An indicator of what terminated the message part:
70 **		  MBT_FINAL -- the final boundary
71 **		  MBT_INTERMED -- an intermediate boundary
72 **		  MBT_NOTSEP -- an end of file
73 */
74 
75 struct args
76 {
77 	char	*field;		/* name of field */
78 	char	*value;		/* value of that field */
79 };
80 
81 int
82 mime8to7(mci, header, e, boundaries, flags)
83 	register MCI *mci;
84 	HDR *header;
85 	register ENVELOPE *e;
86 	char **boundaries;
87 	int flags;
88 {
89 	register char *p;
90 	int linelen;
91 	int bt;
92 	off_t offset;
93 	size_t sectionsize, sectionhighbits;
94 	int i;
95 	char *type;
96 	char *subtype;
97 	char *cte;
98 	char **pvp;
99 	int argc = 0;
100 	char *bp;
101 	struct args argv[MAXMIMEARGS];
102 	char bbuf[128];
103 	char buf[MAXLINE];
104 	char pvpbuf[MAXLINE];
105 	extern char MimeTokenTab[256];
106 
107 	if (tTd(43, 1))
108 	{
109 		printf("mime8to7: flags = %x, boundaries =", flags);
110 		if (boundaries[0] == NULL)
111 			printf(" <none>");
112 		else
113 		{
114 			for (i = 0; boundaries[i] != NULL; i++)
115 				printf(" %s", boundaries[i]);
116 		}
117 		printf("\n");
118 	}
119 	p = hvalue("Content-Transfer-Encoding", header);
120 	if (p == NULL ||
121 	    (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
122 			   MimeTokenTab)) == NULL ||
123 	    pvp[0] == NULL)
124 	{
125 		cte = NULL;
126 	}
127 	else
128 	{
129 		cataddr(pvp, NULL, buf, sizeof buf, '\0');
130 		cte = newstr(buf);
131 	}
132 
133 	type = subtype = NULL;
134 	p = hvalue("Content-Type", header);
135 	if (p == NULL)
136 	{
137 		if (bitset(M87F_DIGEST, flags))
138 			p = "message/rfc822";
139 		else
140 			p = "text/plain";
141 	}
142 	if (p != NULL &&
143 	    (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
144 			   MimeTokenTab)) != NULL &&
145 	    pvp[0] != NULL)
146 	{
147 		if (tTd(43, 40))
148 		{
149 			for (i = 0; pvp[i] != NULL; i++)
150 				printf("pvp[%d] = \"%s\"\n", i, pvp[i]);
151 		}
152 		type = *pvp++;
153 		if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
154 		    *++pvp != NULL)
155 		{
156 			subtype = *pvp++;
157 		}
158 
159 		/* break out parameters */
160 		while (*pvp != NULL && argc < MAXMIMEARGS)
161 		{
162 			/* skip to semicolon separator */
163 			while (*pvp != NULL && strcmp(*pvp, ";") != 0)
164 				pvp++;
165 			if (*pvp++ == NULL || *pvp == NULL)
166 				break;
167 
168 			/* extract field name */
169 			argv[argc].field = *pvp++;
170 
171 			/* see if there is a value */
172 			if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
173 			    (*++pvp == NULL || strcmp(*pvp, ";") != 0))
174 			{
175 				argv[argc].value = *pvp;
176 				argc++;
177 			}
178 		}
179 	}
180 
181 	/* check for disaster cases */
182 	if (type == NULL)
183 		type = "-none-";
184 	if (subtype == NULL)
185 		subtype = "-none-";
186 
187 	/* don't propogate some flags more than one level into the message */
188 	flags &= ~M87F_DIGEST;
189 
190 	/*
191 	**  Check for cases that can not be encoded.
192 	**
193 	**	For example, you can't encode certain kinds of types
194 	**	or already-encoded messages.  If we find this case,
195 	**	just copy it through.
196 	*/
197 
198 	sprintf(buf, "%s/%s", type, subtype);
199 	if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
200 		flags |= M87F_NO8BIT;
201 
202 	/*
203 	**  Multipart requires special processing.
204 	**
205 	**	Do a recursive descent into the message.
206 	*/
207 
208 	if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags))
209 	{
210 		int blen;
211 
212 		if (strcasecmp(subtype, "digest") == 0)
213 			flags |= M87F_DIGEST;
214 
215 		for (i = 0; i < argc; i++)
216 		{
217 			if (strcasecmp(argv[i].field, "boundary") == 0)
218 				break;
219 		}
220 		if (i >= argc)
221 		{
222 			syserr("mime8to7: Content-Type: %s missing boundary", p);
223 			p = "---";
224 		}
225 		else
226 		{
227 			p = argv[i].value;
228 			stripquotes(p);
229 		}
230 		blen = strlen(p);
231 		if (blen > sizeof bbuf - 1)
232 		{
233 			syserr("mime8to7: multipart boundary \"%s\" too long",
234 				p);
235 			blen = sizeof bbuf - 1;
236 		}
237 		strncpy(bbuf, p, blen);
238 		bbuf[blen] = '\0';
239 		if (tTd(43, 1))
240 			printf("mime8to7: multipart boundary \"%s\"\n", bbuf);
241 		for (i = 0; i < MAXMIMENESTING; i++)
242 			if (boundaries[i] == NULL)
243 				break;
244 		if (i >= MAXMIMENESTING)
245 			syserr("mime8to7: multipart nesting boundary too deep");
246 		else
247 		{
248 			boundaries[i] = bbuf;
249 			boundaries[i + 1] = NULL;
250 		}
251 
252 		/* skip the early "comment" prologue */
253 		putline("", mci);
254 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
255 		{
256 			bt = mimeboundary(buf, boundaries);
257 			if (bt != MBT_NOTSEP)
258 				break;
259 			putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
260 			if (tTd(43, 99))
261 				printf("  ...%s", buf);
262 		}
263 		if (feof(e->e_dfp))
264 			bt = MBT_FINAL;
265 		while (bt != MBT_FINAL)
266 		{
267 			auto HDR *hdr = NULL;
268 
269 			sprintf(buf, "--%s", bbuf);
270 			putline(buf, mci);
271 			if (tTd(43, 35))
272 				printf("  ...%s\n", buf);
273 			collect(e->e_dfp, FALSE, FALSE, &hdr, e);
274 			if (tTd(43, 101))
275 				putline("+++after collect", mci);
276 			putheader(mci, hdr, e);
277 			if (tTd(43, 101))
278 				putline("+++after putheader", mci);
279 			bt = mime8to7(mci, hdr, e, boundaries, flags);
280 		}
281 		sprintf(buf, "--%s--", bbuf);
282 		putline(buf, mci);
283 		if (tTd(43, 35))
284 			printf("  ...%s\n", buf);
285 		boundaries[i] = NULL;
286 
287 		/* skip the late "comment" epilogue */
288 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
289 		{
290 			bt = mimeboundary(buf, boundaries);
291 			if (bt != MBT_NOTSEP)
292 				break;
293 			putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
294 			if (tTd(43, 99))
295 				printf("  ...%s", buf);
296 		}
297 		if (feof(e->e_dfp))
298 			bt = MBT_FINAL;
299 		if (tTd(43, 3))
300 			printf("\t\t\tmime8to7=>%s (multipart)\n",
301 				MimeBoundaryNames[bt]);
302 		return bt;
303 	}
304 
305 	/*
306 	**  Message/* types -- recurse exactly once.
307 	*/
308 
309 	if (strcasecmp(type, "message") == 0)
310 	{
311 		if (strcasecmp(subtype, "rfc822") != 0)
312 		{
313 			flags |= M87F_NO8BIT;
314 		}
315 		else
316 		{
317 			register char *q;
318 			auto HDR *hdr = NULL;
319 
320 			putline("", mci);
321 
322 			collect(e->e_dfp, FALSE, FALSE, &hdr, e);
323 			if (tTd(43, 101))
324 				putline("+++after collect", mci);
325 			putheader(mci, hdr, e);
326 			if (tTd(43, 101))
327 				putline("+++after putheader", mci);
328 			bt = mime8to7(mci, hdr, e, boundaries, flags);
329 			return bt;
330 		}
331 	}
332 
333 	/*
334 	**  Non-compound body type
335 	**
336 	**	Compute the ratio of seven to eight bit characters;
337 	**	use that as a heuristic to decide how to do the
338 	**	encoding.
339 	*/
340 
341 	sectionsize = sectionhighbits = 0;
342 	if (!bitset(M87F_NO8BIT, flags))
343 	{
344 		/* remember where we were */
345 		offset = ftell(e->e_dfp);
346 		if (offset == -1)
347 			syserr("mime8to7: cannot ftell on df%s", e->e_id);
348 
349 		/* do a scan of this body type to count character types */
350 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
351 		{
352 			if (mimeboundary(buf, boundaries) != MBT_NOTSEP)
353 				break;
354 			for (p = buf; *p != '\0'; p++)
355 			{
356 				/* count bytes with the high bit set */
357 				sectionsize++;
358 				if (bitset(0200, *p))
359 					sectionhighbits++;
360 			}
361 
362 			/*
363 			**  Heuristic: if 1/4 of the first 4K bytes are 8-bit,
364 			**  assume base64.  This heuristic avoids double-reading
365 			**  large graphics or video files.
366 			*/
367 
368 			if (sectionsize >= 4096 &&
369 			    sectionhighbits > sectionsize / 4)
370 				break;
371 		}
372 
373 		/* return to the original offset for processing */
374 		/* XXX use relative seeks to handle >31 bit file sizes? */
375 		if (fseek(e->e_dfp, offset, SEEK_SET) < 0)
376 			syserr("mime8to7: cannot fseek on df%s", e->e_id);
377 		else
378 			clearerr(e->e_dfp);
379 	}
380 
381 	/*
382 	**  Heuristically determine encoding method.
383 	**	If more than 1/8 of the total characters have the
384 	**	eighth bit set, use base64; else use quoted-printable.
385 	**	However, only encode binary encoded data as base64,
386 	**	since otherwise the NL=>CRLF mapping will be a problem.
387 	*/
388 
389 	if (tTd(43, 8))
390 	{
391 		printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s\n",
392 			sectionhighbits, sectionsize,
393 			cte == NULL ? "[none]" : cte);
394 	}
395 	if (cte != NULL && strcasecmp(cte, "binary") == 0)
396 		sectionsize = sectionhighbits;
397 	linelen = 0;
398 	bp = buf;
399 	if (sectionhighbits == 0)
400 	{
401 		/* no encoding necessary */
402 		if (cte != NULL)
403 		{
404 			sprintf(buf, "Content-Transfer-Encoding: %s", cte);
405 			putline(buf, mci);
406 			if (tTd(43, 36))
407 				printf("  ...%s\n", buf);
408 		}
409 		putline("", mci);
410 		mci->mci_flags &= ~MCIF_INHEADER;
411 		while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
412 		{
413 			bt = mimeboundary(buf, boundaries);
414 			if (bt != MBT_NOTSEP)
415 				break;
416 			putline(buf, mci);
417 		}
418 		if (feof(e->e_dfp))
419 			bt = MBT_FINAL;
420 	}
421 	else if (sectionsize / 8 < sectionhighbits)
422 	{
423 		/* use base64 encoding */
424 		int c1, c2;
425 
426 		putline("Content-Transfer-Encoding: base64", mci);
427 		if (tTd(43, 36))
428 			printf("  ...Content-Transfer-Encoding: base64\n");
429 		putline("", mci);
430 		mci->mci_flags &= ~MCIF_INHEADER;
431 		while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF)
432 		{
433 			if (linelen > 71)
434 			{
435 				*bp = '\0';
436 				putline(buf, mci);
437 				linelen = 0;
438 				bp = buf;
439 			}
440 			linelen += 4;
441 			*bp++ = Base64Code[(c1 >> 2)];
442 			c1 = (c1 & 0x03) << 4;
443 			c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
444 			if (c2 == EOF)
445 			{
446 				*bp++ = Base64Code[c1];
447 				*bp++ = '=';
448 				*bp++ = '=';
449 				break;
450 			}
451 			c1 |= (c2 >> 4) & 0x0f;
452 			*bp++ = Base64Code[c1];
453 			c1 = (c2 & 0x0f) << 2;
454 			c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
455 			if (c2 == EOF)
456 			{
457 				*bp++ = Base64Code[c1];
458 				*bp++ = '=';
459 				break;
460 			}
461 			c1 |= (c2 >> 6) & 0x03;
462 			*bp++ = Base64Code[c1];
463 			*bp++ = Base64Code[c2 & 0x3f];
464 		}
465 	}
466 	else
467 	{
468 		/* use quoted-printable encoding */
469 		int c1, c2;
470 		int fromstate;
471 		BITMAP badchars;
472 
473 		/* set up map of characters that must be mapped */
474 		clrbitmap(badchars);
475 		for (c1 = 0x00; c1 < 0x20; c1++)
476 			setbitn(c1, badchars);
477 		clrbitn('\t', badchars);
478 		for (c1 = 0x7f; c1 < 0x100; c1++)
479 			setbitn(c1, badchars);
480 		setbitn('=', badchars);
481 		if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags))
482 			for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++)
483 				setbitn(*p, badchars);
484 
485 		putline("Content-Transfer-Encoding: quoted-printable", mci);
486 		if (tTd(43, 36))
487 			printf("  ...Content-Transfer-Encoding: quoted-printable\n");
488 		putline("", mci);
489 		mci->mci_flags &= ~MCIF_INHEADER;
490 		fromstate = 0;
491 		c2 = '\n';
492 		while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF)
493 		{
494 			if (c1 == '\n')
495 			{
496 				if (c2 == ' ' || c2 == '\t')
497 				{
498 					*bp++ = '=';
499 					*bp++ = Base16Code[(c2 >> 4) & 0x0f];
500 					*bp++ = Base16Code[c2 & 0x0f];
501 					*bp = '\0';
502 					putline(buf, mci);
503 					bp = buf;
504 				}
505 				*bp = '\0';
506 				putline(buf, mci);
507 				linelen = fromstate = 0;
508 				bp = buf;
509 				c2 = c1;
510 				continue;
511 			}
512 			if (c2 == ' ' && linelen == 4 && fromstate == 4 &&
513 			    bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
514 			{
515 				*bp++ = '=';
516 				*bp++ = '2';
517 				*bp++ = '0';
518 				linelen += 3;
519 			}
520 			else if (c2 == ' ' || c2 == '\t')
521 			{
522 				*bp++ = c2;
523 				linelen++;
524 			}
525 			if (linelen > 72)
526 			{
527 				*bp++ = '=';
528 				*bp = '\0';
529 				putline(buf, mci);
530 				linelen = fromstate = 0;
531 				bp = buf;
532 				c2 = '\n';
533 			}
534 			if (bitnset(c1 & 0xff, badchars))
535 			{
536 				*bp++ = '=';
537 				*bp++ = Base16Code[(c1 >> 4) & 0x0f];
538 				*bp++ = Base16Code[c1 & 0x0f];
539 				linelen += 3;
540 			}
541 			else if (c1 != ' ' && c1 != '\t')
542 			{
543 				if (linelen < 4 && c1 == "From"[linelen])
544 					fromstate++;
545 				*bp++ = c1;
546 				linelen++;
547 			}
548 			c2 = c1;
549 		}
550 
551 		/* output any saved character */
552 		if (c2 == ' ' || c2 == '\t')
553 		{
554 			*bp++ = '=';
555 			*bp++ = Base16Code[(c2 >> 4) & 0x0f];
556 			*bp++ = Base16Code[c2 & 0x0f];
557 			linelen += 3;
558 		}
559 	}
560 	if (linelen > 0)
561 	{
562 		*bp = '\0';
563 		putline(buf, mci);
564 	}
565 	if (tTd(43, 3))
566 		printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
567 	return bt;
568 }
569 /*
570 **  MIME_GETCHAR -- get a character for MIME processing
571 **
572 **	Treats boundaries as EOF.
573 **
574 **	Parameters:
575 **		fp -- the input file.
576 **		boundaries -- the current MIME boundaries.
577 **		btp -- if the return value is EOF, *btp is set to
578 **			the type of the boundary.
579 **
580 **	Returns:
581 **		The next character in the input stream.
582 */
583 
584 int
585 mime_getchar(fp, boundaries, btp)
586 	register FILE *fp;
587 	char **boundaries;
588 	int *btp;
589 {
590 	int c;
591 	static u_char *bp = NULL;
592 	static int buflen = 0;
593 	static bool atbol = TRUE;	/* at beginning of line */
594 	static int bt = MBT_SYNTAX;	/* boundary type of next EOF */
595 	static u_char buf[128];		/* need not be a full line */
596 
597 	if (buflen > 0)
598 	{
599 		buflen--;
600 		return *bp++;
601 	}
602 	bp = buf;
603 	buflen = 0;
604 	c = getc(fp);
605 	if (c == '\n')
606 	{
607 		/* might be part of a MIME boundary */
608 		*bp++ = c;
609 		atbol = TRUE;
610 		c = getc(fp);
611 		if (c == '\n')
612 		{
613 			ungetc(c, fp);
614 			return c;
615 		}
616 	}
617 	if (c != EOF)
618 		*bp++ = c;
619 	else
620 		bt = MBT_FINAL;
621 	if (atbol && c == '-')
622 	{
623 		/* check for a message boundary */
624 		c = getc(fp);
625 		if (c != '-')
626 		{
627 			if (c != EOF)
628 				*bp++ = c;
629 			else
630 				bt = MBT_FINAL;
631 			buflen = bp - buf - 1;
632 			bp = buf;
633 			return *bp++;
634 		}
635 
636 		/* got "--", now check for rest of separator */
637 		*bp++ = '-';
638 		while (bp < &buf[sizeof buf - 2] &&
639 		       (c = getc(fp)) != EOF && c != '\n')
640 		{
641 			*bp++ = c;
642 		}
643 		*bp = '\0';
644 		bt = mimeboundary(&buf[1], boundaries);
645 		switch (bt)
646 		{
647 		  case MBT_FINAL:
648 		  case MBT_INTERMED:
649 			/* we have a message boundary */
650 			buflen = 0;
651 			*btp = bt;
652 			return EOF;
653 		}
654 
655 		atbol = c == '\n';
656 		if (c != EOF)
657 			*bp++ = c;
658 	}
659 
660 	buflen = bp - buf - 1;
661 	if (buflen < 0)
662 	{
663 		*btp = bt;
664 		return EOF;
665 	}
666 	bp = buf;
667 	return *bp++;
668 }
669 /*
670 **  MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF
671 **
672 **	Parameters:
673 **		fp -- the input file.
674 **		boundaries -- the current MIME boundaries.
675 **		btp -- if the return value is EOF, *btp is set to
676 **			the type of the boundary.
677 **
678 **	Returns:
679 **		The next character in the input stream.
680 */
681 
682 int
683 mime_getchar_crlf(fp, boundaries, btp)
684 	register FILE *fp;
685 	char **boundaries;
686 	int *btp;
687 {
688 	static bool sendlf = FALSE;
689 	int c;
690 
691 	if (sendlf)
692 	{
693 		sendlf = FALSE;
694 		return '\n';
695 	}
696 	c = mime_getchar(fp, boundaries, btp);
697 	if (c == '\n')
698 	{
699 		sendlf = TRUE;
700 		return '\r';
701 	}
702 	return c;
703 }
704 /*
705 **  MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
706 **
707 **	Parameters:
708 **		line -- the input line.
709 **		boundaries -- the set of currently pending boundaries.
710 **
711 **	Returns:
712 **		MBT_NOTSEP -- if this is not a separator line
713 **		MBT_INTERMED -- if this is an intermediate separator
714 **		MBT_FINAL -- if this is a final boundary
715 **		MBT_SYNTAX -- if this is a boundary for the wrong
716 **			enclosure -- i.e., a syntax error.
717 */
718 
719 int
720 mimeboundary(line, boundaries)
721 	register char *line;
722 	char **boundaries;
723 {
724 	int type;
725 	int i;
726 	int savec;
727 
728 	if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
729 		return MBT_NOTSEP;
730 	i = strlen(line);
731 	if (line[i - 1] == '\n')
732 		i--;
733 	if (tTd(43, 5))
734 		printf("mimeboundary: line=\"%.*s\"... ", i, line);
735 	while (line[i - 1] == ' ' || line[i - 1] == '\t')
736 		i--;
737 	if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0)
738 	{
739 		type = MBT_FINAL;
740 		i -= 2;
741 	}
742 	else
743 		type = MBT_INTERMED;
744 
745 	savec = line[i];
746 	line[i] = '\0';
747 	/* XXX should check for improper nesting here */
748 	if (isboundary(&line[2], boundaries) < 0)
749 		type = MBT_NOTSEP;
750 	line[i] = savec;
751 	if (tTd(43, 5))
752 		printf("%s\n", MimeBoundaryNames[type]);
753 	return type;
754 }
755 /*
756 **  DEFCHARSET -- return default character set for message
757 **
758 **	The first choice for character set is for the mailer
759 **	corresponding to the envelope sender.  If neither that
760 **	nor the global configuration file has a default character
761 **	set defined, return "unknown-8bit" as recommended by
762 **	RFC 1428 section 3.
763 **
764 **	Parameters:
765 **		e -- the envelope for this message.
766 **
767 **	Returns:
768 **		The default character set for that mailer.
769 */
770 
771 char *
772 defcharset(e)
773 	register ENVELOPE *e;
774 {
775 	if (e != NULL && e->e_from.q_mailer != NULL &&
776 	    e->e_from.q_mailer->m_defcharset != NULL)
777 		return e->e_from.q_mailer->m_defcharset;
778 	if (DefaultCharSet != NULL)
779 		return DefaultCharSet;
780 	return "unknown-8bit";
781 }
782 /*
783 **  ISBOUNDARY -- is a given string a currently valid boundary?
784 **
785 **	Parameters:
786 **		line -- the current input line.
787 **		boundaries -- the list of valid boundaries.
788 **
789 **	Returns:
790 **		The index number in boundaries if the line is found.
791 **		-1 -- otherwise.
792 **
793 */
794 
795 int
796 isboundary(line, boundaries)
797 	char *line;
798 	char **boundaries;
799 {
800 	register int i;
801 
802 	for (i = 0; boundaries[i] != NULL; i++)
803 	{
804 		if (strcmp(line, boundaries[i]) == 0)
805 			return i;
806 	}
807 	return -1;
808 }
809