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