]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/mime.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / mime.c
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1994, 1996-1997 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1994
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12
13 # include "sendmail.h"
14 # include <string.h>
15
16 #ifndef lint
17 static char sccsid[] = "@(#)mime.c      8.71 (Berkeley) 1/18/1999";
18 #endif /* not lint */
19
20 /*
21 **  MIME support.
22 **
23 **      I am indebted to John Beck of Hewlett-Packard, who contributed
24 **      his code to me for inclusion.  As it turns out, I did not use
25 **      his code since he used a "minimum change" approach that used
26 **      several temp files, and I wanted a "minimum impact" approach
27 **      that would avoid copying.  However, looking over his code
28 **      helped me cement my understanding of the problem.
29 **
30 **      I also looked at, but did not directly use, Nathaniel
31 **      Borenstein's "code.c" module.  Again, it functioned as
32 **      a file-to-file translator, which did not fit within my
33 **      design bounds, but it was a useful base for understanding
34 **      the problem.
35 */
36
37 #if MIME8TO7
38
39 /* character set for hex and base64 encoding */
40 char    Base16Code[] =  "0123456789ABCDEF";
41 char    Base64Code[] =  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
42
43 /* types of MIME boundaries */
44 #define MBT_SYNTAX      0       /* syntax error */
45 #define MBT_NOTSEP      1       /* not a boundary */
46 #define MBT_INTERMED    2       /* intermediate boundary (no trailing --) */
47 #define MBT_FINAL       3       /* final boundary (trailing -- included) */
48
49 static char     *MimeBoundaryNames[] =
50 {
51         "SYNTAX",       "NOTSEP",       "INTERMED",     "FINAL"
52 };
53
54 bool    MapNLtoCRLF;
55
56 extern int      mimeboundary __P((char *, char **));
57 \f/*
58 **  MIME8TO7 -- output 8 bit body in 7 bit format
59 **
60 **      The header has already been output -- this has to do the
61 **      8 to 7 bit conversion.  It would be easy if we didn't have
62 **      to deal with nested formats (multipart/xxx and message/rfc822).
63 **
64 **      We won't be called if we don't have to do a conversion, and
65 **      appropriate MIME-Version: and Content-Type: fields have been
66 **      output.  Any Content-Transfer-Encoding: field has not been
67 **      output, and we can add it here.
68 **
69 **      Parameters:
70 **              mci -- mailer connection information.
71 **              header -- the header for this body part.
72 **              e -- envelope.
73 **              boundaries -- the currently pending message boundaries.
74 **                      NULL if we are processing the outer portion.
75 **              flags -- to tweak processing.
76 **
77 **      Returns:
78 **              An indicator of what terminated the message part:
79 **                MBT_FINAL -- the final boundary
80 **                MBT_INTERMED -- an intermediate boundary
81 **                MBT_NOTSEP -- an end of file
82 */
83
84 struct args
85 {
86         char    *field;         /* name of field */
87         char    *value;         /* value of that field */
88 };
89
90 int
91 mime8to7(mci, header, e, boundaries, flags)
92         register MCI *mci;
93         HDR *header;
94         register ENVELOPE *e;
95         char **boundaries;
96         int flags;
97 {
98         register char *p;
99         int linelen;
100         int bt;
101         off_t offset;
102         size_t sectionsize, sectionhighbits;
103         int i;
104         char *type;
105         char *subtype;
106         char *cte;
107         char **pvp;
108         int argc = 0;
109         char *bp;
110         bool use_qp = FALSE;
111         struct args argv[MAXMIMEARGS];
112         char bbuf[128];
113         char buf[MAXLINE];
114         char pvpbuf[MAXLINE];
115         extern u_char MimeTokenTab[256];
116         extern int mime_getchar __P((FILE *, char **, int *));
117         extern int mime_getchar_crlf __P((FILE *, char **, int *));
118
119         if (tTd(43, 1))
120         {
121                 printf("mime8to7: flags = %x, boundaries =", flags);
122                 if (boundaries[0] == NULL)
123                         printf(" <none>");
124                 else
125                 {
126                         for (i = 0; boundaries[i] != NULL; i++)
127                                 printf(" %s", boundaries[i]);
128                 }
129                 printf("\n");
130         }
131         MapNLtoCRLF = TRUE;
132         p = hvalue("Content-Transfer-Encoding", header);
133         if (p == NULL ||
134             (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
135                            MimeTokenTab)) == NULL ||
136             pvp[0] == NULL)
137         {
138                 cte = NULL;
139         }
140         else
141         {
142                 cataddr(pvp, NULL, buf, sizeof buf, '\0');
143                 cte = newstr(buf);
144         }
145
146         type = subtype = NULL;
147         p = hvalue("Content-Type", header);
148         if (p == NULL)
149         {
150                 if (bitset(M87F_DIGEST, flags))
151                         p = "message/rfc822";
152                 else
153                         p = "text/plain";
154         }
155         if (p != NULL &&
156             (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
157                            MimeTokenTab)) != NULL &&
158             pvp[0] != NULL)
159         {
160                 if (tTd(43, 40))
161                 {
162                         for (i = 0; pvp[i] != NULL; i++)
163                                 printf("pvp[%d] = \"%s\"\n", i, pvp[i]);
164                 }
165                 type = *pvp++;
166                 if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
167                     *++pvp != NULL)
168                 {
169                         subtype = *pvp++;
170                 }
171
172                 /* break out parameters */
173                 while (*pvp != NULL && argc < MAXMIMEARGS)
174                 {
175                         /* skip to semicolon separator */
176                         while (*pvp != NULL && strcmp(*pvp, ";") != 0)
177                                 pvp++;
178                         if (*pvp++ == NULL || *pvp == NULL)
179                                 break;
180
181                         /* extract field name */
182                         argv[argc].field = *pvp++;
183
184                         /* see if there is a value */
185                         if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
186                             (*++pvp == NULL || strcmp(*pvp, ";") != 0))
187                         {
188                                 argv[argc].value = *pvp;
189                                 argc++;
190                         }
191                 }
192         }
193
194         /* check for disaster cases */
195         if (type == NULL)
196                 type = "-none-";
197         if (subtype == NULL)
198                 subtype = "-none-";
199
200         /* don't propogate some flags more than one level into the message */
201         flags &= ~M87F_DIGEST;
202
203         /*
204         **  Check for cases that can not be encoded.
205         **
206         **      For example, you can't encode certain kinds of types
207         **      or already-encoded messages.  If we find this case,
208         **      just copy it through.
209         */
210
211         snprintf(buf, sizeof buf, "%.100s/%.100s", type, subtype);
212         if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
213                 flags |= M87F_NO8BIT;
214
215 #ifdef USE_B_CLASS
216         if (wordinclass(buf, 'b') || wordinclass(type, 'b'))
217                 MapNLtoCRLF = FALSE;
218 #endif
219         if (wordinclass(buf, 'q') || wordinclass(type, 'q'))
220                 use_qp = TRUE;
221
222         /*
223         **  Multipart requires special processing.
224         **
225         **      Do a recursive descent into the message.
226         */
227
228         if (strcasecmp(type, "multipart") == 0 &&
229             (!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags)))
230         {
231                 int blen;
232
233                 if (strcasecmp(subtype, "digest") == 0)
234                         flags |= M87F_DIGEST;
235
236                 for (i = 0; i < argc; i++)
237                 {
238                         if (strcasecmp(argv[i].field, "boundary") == 0)
239                                 break;
240                 }
241                 if (i >= argc || argv[i].value == NULL)
242                 {
243                         syserr("mime8to7: Content-Type: \"%s\": %s boundary",
244                                 i >= argc ? "missing" : "bogus", p);
245                         p = "---";
246
247                         /* avoid bounce loops */
248                         e->e_flags |= EF_DONT_MIME;
249                 }
250                 else
251                 {
252                         p = argv[i].value;
253                         stripquotes(p);
254                 }
255                 blen = strlen(p);
256                 if (blen > sizeof bbuf - 1)
257                 {
258                         syserr("mime8to7: multipart boundary \"%s\" too long",
259                                 p);
260                         blen = sizeof bbuf - 1;
261
262                         /* avoid bounce loops */
263                         e->e_flags |= EF_DONT_MIME;
264                 }
265                 strncpy(bbuf, p, blen);
266                 bbuf[blen] = '\0';
267                 if (tTd(43, 1))
268                         printf("mime8to7: multipart boundary \"%s\"\n", bbuf);
269                 for (i = 0; i < MAXMIMENESTING; i++)
270                         if (boundaries[i] == NULL)
271                                 break;
272                 if (i >= MAXMIMENESTING)
273                 {
274                         syserr("mime8to7: multipart nesting boundary too deep");
275
276                         /* avoid bounce loops */
277                         e->e_flags |= EF_DONT_MIME;
278                 }
279                 else
280                 {
281                         boundaries[i] = bbuf;
282                         boundaries[i + 1] = NULL;
283                 }
284                 mci->mci_flags |= MCIF_INMIME;
285
286                 /* skip the early "comment" prologue */
287                 putline("", mci);
288                 mci->mci_flags &= ~MCIF_INHEADER;
289                 while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
290                 {
291                         bt = mimeboundary(buf, boundaries);
292                         if (bt != MBT_NOTSEP)
293                                 break;
294                         putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
295                         if (tTd(43, 99))
296                                 printf("  ...%s", buf);
297                 }
298                 if (feof(e->e_dfp))
299                         bt = MBT_FINAL;
300                 while (bt != MBT_FINAL)
301                 {
302                         auto HDR *hdr = NULL;
303
304                         snprintf(buf, sizeof buf, "--%s", bbuf);
305                         putline(buf, mci);
306                         if (tTd(43, 35))
307                                 printf("  ...%s\n", buf);
308                         collect(e->e_dfp, FALSE, &hdr, e);
309                         if (tTd(43, 101))
310                                 putline("+++after collect", mci);
311                         putheader(mci, hdr, e, flags);
312                         if (tTd(43, 101))
313                                 putline("+++after putheader", mci);
314                         bt = mime8to7(mci, hdr, e, boundaries, flags);
315                 }
316                 snprintf(buf, sizeof buf, "--%s--", bbuf);
317                 putline(buf, mci);
318                 if (tTd(43, 35))
319                         printf("  ...%s\n", buf);
320                 boundaries[i] = NULL;
321                 mci->mci_flags &= ~MCIF_INMIME;
322
323                 /* skip the late "comment" epilogue */
324                 while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
325                 {
326                         bt = mimeboundary(buf, boundaries);
327                         if (bt != MBT_NOTSEP)
328                                 break;
329                         putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
330                         if (tTd(43, 99))
331                                 printf("  ...%s", buf);
332                 }
333                 if (feof(e->e_dfp))
334                         bt = MBT_FINAL;
335                 if (tTd(43, 3))
336                         printf("\t\t\tmime8to7=>%s (multipart)\n",
337                                 MimeBoundaryNames[bt]);
338                 return bt;
339         }
340
341         /*
342         **  Message/xxx types -- recurse exactly once.
343         **
344         **      Class 's' is predefined to have "rfc822" only.
345         */
346
347         if (strcasecmp(type, "message") == 0)
348         {
349                 if (!wordinclass(subtype, 's'))
350                 {
351                         flags |= M87F_NO8BIT;
352                 }
353                 else
354                 {
355                         auto HDR *hdr = NULL;
356
357                         putline("", mci);
358
359                         mci->mci_flags |= MCIF_INMIME;
360                         collect(e->e_dfp, FALSE, &hdr, e);
361                         if (tTd(43, 101))
362                                 putline("+++after collect", mci);
363                         putheader(mci, hdr, e, flags);
364                         if (tTd(43, 101))
365                                 putline("+++after putheader", mci);
366                         if (hvalue("MIME-Version", hdr) == NULL)
367                                 putline("MIME-Version: 1.0", mci);
368                         bt = mime8to7(mci, hdr, e, boundaries, flags);
369                         mci->mci_flags &= ~MCIF_INMIME;
370                         return bt;
371                 }
372         }
373
374         /*
375         **  Non-compound body type
376         **
377         **      Compute the ratio of seven to eight bit characters;
378         **      use that as a heuristic to decide how to do the
379         **      encoding.
380         */
381
382         sectionsize = sectionhighbits = 0;
383         if (!bitset(M87F_NO8BIT|M87F_NO8TO7, flags))
384         {
385                 /* remember where we were */
386                 offset = ftell(e->e_dfp);
387                 if (offset == -1)
388                         syserr("mime8to7: cannot ftell on df%s", e->e_id);
389
390                 /* do a scan of this body type to count character types */
391                 while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
392                 {
393                         if (mimeboundary(buf, boundaries) != MBT_NOTSEP)
394                                 break;
395                         for (p = buf; *p != '\0'; p++)
396                         {
397                                 /* count bytes with the high bit set */
398                                 sectionsize++;
399                                 if (bitset(0200, *p))
400                                         sectionhighbits++;
401                         }
402
403                         /*
404                         **  Heuristic: if 1/4 of the first 4K bytes are 8-bit,
405                         **  assume base64.  This heuristic avoids double-reading
406                         **  large graphics or video files.
407                         */
408
409                         if (sectionsize >= 4096 &&
410                             sectionhighbits > sectionsize / 4)
411                                 break;
412                 }
413
414                 /* return to the original offset for processing */
415                 /* XXX use relative seeks to handle >31 bit file sizes? */
416                 if (fseek(e->e_dfp, offset, SEEK_SET) < 0)
417                         syserr("mime8to7: cannot fseek on df%s", e->e_id);
418                 else
419                         clearerr(e->e_dfp);
420         }
421
422         /*
423         **  Heuristically determine encoding method.
424         **      If more than 1/8 of the total characters have the
425         **      eighth bit set, use base64; else use quoted-printable.
426         **      However, only encode binary encoded data as base64,
427         **      since otherwise the NL=>CRLF mapping will be a problem.
428         */
429
430         if (tTd(43, 8))
431         {
432                 printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n",
433                         (long) sectionhighbits, (long) sectionsize,
434                         cte == NULL ? "[none]" : cte,
435                         type == NULL ? "[none]" : type,
436                         subtype == NULL ? "[none]" : subtype);
437         }
438         if (cte != NULL && strcasecmp(cte, "binary") == 0)
439                 sectionsize = sectionhighbits;
440         linelen = 0;
441         bp = buf;
442         if (sectionhighbits == 0)
443         {
444                 /* no encoding necessary */
445                 if (cte != NULL &&
446                     bitset(MCIF_INMIME, mci->mci_flags) &&
447                     !bitset(M87F_NO8TO7, flags))
448                 {
449                         /*
450                         **  Skip _unless_ in MIME mode and potentially
451                         **  converting from 8 bit to 7 bit MIME.  See
452                         **  putheader() for the counterpart where the
453                         **  CTE header is skipped in the opposite
454                         **  situation.
455                         */
456
457                         snprintf(buf, sizeof buf,
458                                 "Content-Transfer-Encoding: %.200s", cte);
459                         putline(buf, mci);
460                         if (tTd(43, 36))
461                                 printf("  ...%s\n", buf);
462                 }
463                 putline("", mci);
464                 mci->mci_flags &= ~MCIF_INHEADER;
465                 while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
466                 {
467                         bt = mimeboundary(buf, boundaries);
468                         if (bt != MBT_NOTSEP)
469                                 break;
470                         putline(buf, mci);
471                 }
472                 if (feof(e->e_dfp))
473                         bt = MBT_FINAL;
474         }
475         else if (!MapNLtoCRLF ||
476                  (sectionsize / 8 < sectionhighbits && !use_qp))
477         {
478                 /* use base64 encoding */
479                 int c1, c2;
480
481                 if (tTd(43, 36))
482                         printf("  ...Content-Transfer-Encoding: base64\n");
483                 putline("Content-Transfer-Encoding: base64", mci);
484                 snprintf(buf, sizeof buf,
485                         "X-MIME-Autoconverted: from 8bit to base64 by %s id %s",
486                         MyHostName, e->e_id);
487                 putline(buf, mci);
488                 putline("", mci);
489                 mci->mci_flags &= ~MCIF_INHEADER;
490                 while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF)
491                 {
492                         if (linelen > 71)
493                         {
494                                 *bp = '\0';
495                                 putline(buf, mci);
496                                 linelen = 0;
497                                 bp = buf;
498                         }
499                         linelen += 4;
500                         *bp++ = Base64Code[(c1 >> 2)];
501                         c1 = (c1 & 0x03) << 4;
502                         c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
503                         if (c2 == EOF)
504                         {
505                                 *bp++ = Base64Code[c1];
506                                 *bp++ = '=';
507                                 *bp++ = '=';
508                                 break;
509                         }
510                         c1 |= (c2 >> 4) & 0x0f;
511                         *bp++ = Base64Code[c1];
512                         c1 = (c2 & 0x0f) << 2;
513                         c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
514                         if (c2 == EOF)
515                         {
516                                 *bp++ = Base64Code[c1];
517                                 *bp++ = '=';
518                                 break;
519                         }
520                         c1 |= (c2 >> 6) & 0x03;
521                         *bp++ = Base64Code[c1];
522                         *bp++ = Base64Code[c2 & 0x3f];
523                 }
524                 *bp = '\0';
525                 putline(buf, mci);
526         }
527         else
528         {
529                 /* use quoted-printable encoding */
530                 int c1, c2;
531                 int fromstate;
532                 BITMAP badchars;
533
534                 /* set up map of characters that must be mapped */
535                 clrbitmap(badchars);
536                 for (c1 = 0x00; c1 < 0x20; c1++)
537                         setbitn(c1, badchars);
538                 clrbitn('\t', badchars);
539                 for (c1 = 0x7f; c1 < 0x100; c1++)
540                         setbitn(c1, badchars);
541                 setbitn('=', badchars);
542                 if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags))
543                         for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++)
544                                 setbitn(*p, badchars);
545
546                 if (tTd(43, 36))
547                         printf("  ...Content-Transfer-Encoding: quoted-printable\n");
548                 putline("Content-Transfer-Encoding: quoted-printable", mci);
549                 snprintf(buf, sizeof buf,
550                         "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s",
551                         MyHostName, e->e_id);
552                 putline(buf, mci);
553                 putline("", mci);
554                 mci->mci_flags &= ~MCIF_INHEADER;
555                 fromstate = 0;
556                 c2 = '\n';
557                 while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF)
558                 {
559                         if (c1 == '\n')
560                         {
561                                 if (c2 == ' ' || c2 == '\t')
562                                 {
563                                         *bp++ = '=';
564                                         *bp++ = Base16Code[(c2 >> 4) & 0x0f];
565                                         *bp++ = Base16Code[c2 & 0x0f];
566                                 }
567                                 if (buf[0] == '.' && bp == &buf[1])
568                                 {
569                                         buf[0] = '=';
570                                         *bp++ = Base16Code[('.' >> 4) & 0x0f];
571                                         *bp++ = Base16Code['.' & 0x0f];
572                                 }
573                                 *bp = '\0';
574                                 putline(buf, mci);
575                                 linelen = fromstate = 0;
576                                 bp = buf;
577                                 c2 = c1;
578                                 continue;
579                         }
580                         if (c2 == ' ' && linelen == 4 && fromstate == 4 &&
581                             bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
582                         {
583                                 *bp++ = '=';
584                                 *bp++ = '2';
585                                 *bp++ = '0';
586                                 linelen += 3;
587                         }
588                         else if (c2 == ' ' || c2 == '\t')
589                         {
590                                 *bp++ = c2;
591                                 linelen++;
592                         }
593                         if (linelen > 72 &&
594                             (linelen > 75 || c1 != '.' ||
595                              (linelen > 73 && c2 == '.')))
596                         {
597                                 if (linelen > 73 && c2 == '.')
598                                         bp--;
599                                 else
600                                         c2 = '\n';
601                                 *bp++ = '=';
602                                 *bp = '\0';
603                                 putline(buf, mci);
604                                 linelen = fromstate = 0;
605                                 bp = buf;
606                                 if (c2 == '.')
607                                 {
608                                         *bp++ = '.';
609                                         linelen++;
610                                 }
611                         }
612                         if (bitnset(c1 & 0xff, badchars))
613                         {
614                                 *bp++ = '=';
615                                 *bp++ = Base16Code[(c1 >> 4) & 0x0f];
616                                 *bp++ = Base16Code[c1 & 0x0f];
617                                 linelen += 3;
618                         }
619                         else if (c1 != ' ' && c1 != '\t')
620                         {
621                                 if (linelen < 4 && c1 == "From"[linelen])
622                                         fromstate++;
623                                 *bp++ = c1;
624                                 linelen++;
625                         }
626                         c2 = c1;
627                 }
628
629                 /* output any saved character */
630                 if (c2 == ' ' || c2 == '\t')
631                 {
632                         *bp++ = '=';
633                         *bp++ = Base16Code[(c2 >> 4) & 0x0f];
634                         *bp++ = Base16Code[c2 & 0x0f];
635                         linelen += 3;
636                 }
637
638                 if (linelen > 0 || boundaries[0] != NULL)
639                 {
640                         *bp = '\0';
641                         putline(buf, mci);
642                 }
643
644         }
645         if (tTd(43, 3))
646                 printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
647         return bt;
648 }
649 \f/*
650 **  MIME_GETCHAR -- get a character for MIME processing
651 **
652 **      Treats boundaries as EOF.
653 **
654 **      Parameters:
655 **              fp -- the input file.
656 **              boundaries -- the current MIME boundaries.
657 **              btp -- if the return value is EOF, *btp is set to
658 **                      the type of the boundary.
659 **
660 **      Returns:
661 **              The next character in the input stream.
662 */
663
664 int
665 mime_getchar(fp, boundaries, btp)
666         register FILE *fp;
667         char **boundaries;
668         int *btp;
669 {
670         int c;
671         static u_char *bp = NULL;
672         static int buflen = 0;
673         static bool atbol = TRUE;       /* at beginning of line */
674         static int bt = MBT_SYNTAX;     /* boundary type of next EOF */
675         static u_char buf[128];         /* need not be a full line */
676         int start = 0;                  /* indicates position of - in buffer */
677
678         if (buflen == 1 && *bp == '\n')
679         {
680                 /* last \n in buffer may be part of next MIME boundary */
681                 c = *bp;
682         }
683         else if (buflen > 0)
684         {
685                 buflen--;
686                 return *bp++;
687         }
688         else 
689                 c = getc(fp);
690         bp = buf;
691         buflen = 0;
692         if (c == '\n')
693         {
694                 /* might be part of a MIME boundary */
695                 *bp++ = c;
696                 atbol = TRUE;
697                 c = getc(fp);
698                 if (c == '\n')
699                 {
700                         ungetc(c, fp);
701                         return c;
702                 }
703                 start = 1;
704         }
705         if (c != EOF)
706                 *bp++ = c;
707         else
708                 bt = MBT_FINAL;
709         if (atbol && c == '-')
710         {
711                 /* check for a message boundary */
712                 c = getc(fp);
713                 if (c != '-')
714                 {
715                         if (c != EOF)
716                                 *bp++ = c;
717                         else
718                                 bt = MBT_FINAL;
719                         buflen = bp - buf - 1;
720                         bp = buf;
721                         return *bp++;
722                 }
723
724                 /* got "--", now check for rest of separator */
725                 *bp++ = '-';
726                 while (bp < &buf[sizeof buf - 2] &&
727                        (c = getc(fp)) != EOF && c != '\n')
728                 {
729                         *bp++ = c;
730                 }
731                 *bp = '\0';
732                 bt = mimeboundary((char *) &buf[start], boundaries);
733                 switch (bt)
734                 {
735                   case MBT_FINAL:
736                   case MBT_INTERMED:
737                         /* we have a message boundary */
738                         buflen = 0;
739                         *btp = bt;
740                         return EOF;
741                 }
742
743                 atbol = c == '\n';
744                 if (c != EOF)
745                         *bp++ = c;
746         }
747
748         buflen = bp - buf - 1;
749         if (buflen < 0)
750         {
751                 *btp = bt;
752                 return EOF;
753         }
754         bp = buf;
755         return *bp++;
756 }
757 \f/*
758 **  MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF
759 **
760 **      Parameters:
761 **              fp -- the input file.
762 **              boundaries -- the current MIME boundaries.
763 **              btp -- if the return value is EOF, *btp is set to
764 **                      the type of the boundary.
765 **
766 **      Returns:
767 **              The next character in the input stream.
768 */
769
770 int
771 mime_getchar_crlf(fp, boundaries, btp)
772         register FILE *fp;
773         char **boundaries;
774         int *btp;
775 {
776         static bool sendlf = FALSE;
777         int c;
778
779         if (sendlf)
780         {
781                 sendlf = FALSE;
782                 return '\n';
783         }
784         c = mime_getchar(fp, boundaries, btp);
785         if (c == '\n' && MapNLtoCRLF)
786         {
787                 sendlf = TRUE;
788                 return '\r';
789         }
790         return c;
791 }
792 \f/*
793 **  MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
794 **
795 **      Parameters:
796 **              line -- the input line.
797 **              boundaries -- the set of currently pending boundaries.
798 **
799 **      Returns:
800 **              MBT_NOTSEP -- if this is not a separator line
801 **              MBT_INTERMED -- if this is an intermediate separator
802 **              MBT_FINAL -- if this is a final boundary
803 **              MBT_SYNTAX -- if this is a boundary for the wrong
804 **                      enclosure -- i.e., a syntax error.
805 */
806
807 int
808 mimeboundary(line, boundaries)
809         register char *line;
810         char **boundaries;
811 {
812         int type = MBT_NOTSEP;
813         int i;
814         int savec;
815         extern int isboundary __P((char *, char **));
816
817         if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
818                 return MBT_NOTSEP;
819         i = strlen(line);
820         if (line[i - 1] == '\n')
821                 i--;
822
823         /* strip off trailing whitespace */
824         while (line[i - 1] == ' ' || line[i - 1] == '\t')
825                 i--;
826         savec = line[i];
827         line[i] = '\0';
828
829         if (tTd(43, 5))
830                 printf("mimeboundary: line=\"%s\"... ", line);
831
832         /* check for this as an intermediate boundary */
833         if (isboundary(&line[2], boundaries) >= 0)
834                 type = MBT_INTERMED;
835         else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0)
836         {
837                 /* check for a final boundary */
838                 line[i - 2] = '\0';
839                 if (isboundary(&line[2], boundaries) >= 0)
840                         type = MBT_FINAL;
841                 line[i - 2] = '-';
842         }
843
844         line[i] = savec;
845         if (tTd(43, 5))
846                 printf("%s\n", MimeBoundaryNames[type]);
847         return type;
848 }
849 \f/*
850 **  DEFCHARSET -- return default character set for message
851 **
852 **      The first choice for character set is for the mailer
853 **      corresponding to the envelope sender.  If neither that
854 **      nor the global configuration file has a default character
855 **      set defined, return "unknown-8bit" as recommended by
856 **      RFC 1428 section 3.
857 **
858 **      Parameters:
859 **              e -- the envelope for this message.
860 **
861 **      Returns:
862 **              The default character set for that mailer.
863 */
864
865 char *
866 defcharset(e)
867         register ENVELOPE *e;
868 {
869         if (e != NULL && e->e_from.q_mailer != NULL &&
870             e->e_from.q_mailer->m_defcharset != NULL)
871                 return e->e_from.q_mailer->m_defcharset;
872         if (DefaultCharSet != NULL)
873                 return DefaultCharSet;
874         return "unknown-8bit";
875 }
876 \f/*
877 **  ISBOUNDARY -- is a given string a currently valid boundary?
878 **
879 **      Parameters:
880 **              line -- the current input line.
881 **              boundaries -- the list of valid boundaries.
882 **
883 **      Returns:
884 **              The index number in boundaries if the line is found.
885 **              -1 -- otherwise.
886 **
887 */
888
889 int
890 isboundary(line, boundaries)
891         char *line;
892         char **boundaries;
893 {
894         register int i;
895
896         for (i = 0; boundaries[i] != NULL; i++)
897         {
898                 if (strcmp(line, boundaries[i]) == 0)
899                         return i;
900         }
901         return -1;
902 }
903
904 #endif /* MIME8TO7 */
905 \f
906 #if MIME7TO8
907
908 /*
909 **  MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format
910 **
911 **  This is a hack. Supports translating the two 7-bit body-encodings
912 **  (quoted-printable and base64) to 8-bit coded bodies.
913 **
914 **  There is not much point in supporting multipart here, as the UA
915 **  will be able to deal with encoded MIME bodies if it can parse MIME
916 **  multipart messages.
917 **
918 **  Note also that we wont be called unless it is a text/plain MIME
919 **  message, encoded base64 or QP and mailer flag '9' has been defined
920 **  on mailer.
921 **
922 **  Contributed by Marius Olaffson <marius@rhi.hi.is>.
923 **
924 **      Parameters:
925 **              mci -- mailer connection information.
926 **              header -- the header for this body part.
927 **              e -- envelope.
928 **
929 **      Returns:
930 **              none.
931 */
932
933 extern int      mime_fromqp __P((u_char *, u_char **, int, int));
934
935 static char index_64[128] =
936 {
937         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
938         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
939         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
940         52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
941         -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
942         15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
943         -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
944         41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
945 };
946
947 #define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
948
949
950 void
951 mime7to8(mci, header, e)
952         register MCI *mci;
953         HDR *header;
954         register ENVELOPE *e;
955 {
956         register char *p;
957         char *cte;
958         char **pvp;
959         u_char *fbufp;
960         char buf[MAXLINE];
961         u_char fbuf[MAXLINE + 1];
962         char pvpbuf[MAXLINE];
963         extern u_char MimeTokenTab[256];
964
965         p = hvalue("Content-Transfer-Encoding", header);
966         if (p == NULL ||
967             (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
968                            MimeTokenTab)) == NULL ||
969             pvp[0] == NULL)
970         {
971                 /* "can't happen" -- upper level should have caught this */
972                 syserr("mime7to8: unparsable CTE %s", p == NULL ? "<NULL>" : p);
973
974                 /* avoid bounce loops */
975                 e->e_flags |= EF_DONT_MIME;
976
977                 /* cheap failsafe algorithm -- should work on text/plain */
978                 if (p != NULL)
979                 {
980                         snprintf(buf, sizeof buf,
981                                 "Content-Transfer-Encoding: %s", p);
982                         putline(buf, mci);
983                 }
984                 putline("", mci);
985                 mci->mci_flags &= ~MCIF_INHEADER;
986                 while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
987                         putline(buf, mci);
988                 return;
989         }
990         cataddr(pvp, NULL, buf, sizeof buf, '\0');
991         cte = newstr(buf);
992
993         mci->mci_flags |= MCIF_INHEADER;
994         putline("Content-Transfer-Encoding: 8bit", mci);
995         snprintf(buf, sizeof buf,
996                 "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s",
997                 cte, MyHostName, e->e_id);
998         putline(buf, mci);
999         putline("", mci);
1000         mci->mci_flags &= ~MCIF_INHEADER;
1001
1002         /*
1003         **  Translate body encoding to 8-bit.  Supports two types of
1004         **  encodings; "base64" and "quoted-printable". Assume qp if
1005         **  it is not base64.
1006         */
1007
1008         if (strcasecmp(cte, "base64") == 0)
1009         {
1010                 int c1, c2, c3, c4;
1011
1012                 fbufp = fbuf;
1013                 while ((c1 = fgetc(e->e_dfp)) != EOF)
1014                 {
1015                         if (isascii(c1) && isspace(c1))
1016                                 continue;
1017
1018                         do
1019                         {
1020                                 c2 = fgetc(e->e_dfp);
1021                         } while (isascii(c2) && isspace(c2));
1022                         if (c2 == EOF)
1023                                 break;
1024
1025                         do
1026                         {
1027                                 c3 = fgetc(e->e_dfp);
1028                         } while (isascii(c3) && isspace(c3));
1029                         if (c3 == EOF)
1030                                 break;
1031
1032                         do
1033                         {
1034                                 c4 = fgetc(e->e_dfp);
1035                         } while (isascii(c4) && isspace(c4));
1036                         if (c4 == EOF)
1037                                 break;
1038
1039                         if (c1 == '=' || c2 == '=')
1040                                 continue;
1041                         c1 = CHAR64(c1);
1042                         c2 = CHAR64(c2);
1043
1044                         *fbufp = (c1 << 2) | ((c2 & 0x30) >> 4);
1045                         if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE])
1046                         {
1047                                 if (*--fbufp != '\n' ||
1048                                     (fbufp > fbuf && *--fbufp != '\r'))
1049                                         fbufp++;
1050                                 putxline((char *) fbuf, fbufp - fbuf,
1051                                          mci, PXLF_MAPFROM);
1052                                 fbufp = fbuf;
1053                         }
1054                         if (c3 == '=')
1055                                 continue;
1056                         c3 = CHAR64(c3);
1057                         *fbufp = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
1058                         if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE])
1059                         {
1060                                 if (*--fbufp != '\n' ||
1061                                     (fbufp > fbuf && *--fbufp != '\r'))
1062                                         fbufp++;
1063                                 putxline((char *) fbuf, fbufp - fbuf,
1064                                          mci, PXLF_MAPFROM);
1065                                 fbufp = fbuf;
1066                         }
1067                         if (c4 == '=')
1068                                 continue;
1069                         c4 = CHAR64(c4);
1070                         *fbufp = ((c3 & 0x03) << 6) | c4;
1071                         if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE])
1072                         {
1073                                 if (*--fbufp != '\n' ||
1074                                     (fbufp > fbuf && *--fbufp != '\r'))
1075                                         fbufp++;
1076                                 putxline((char *) fbuf, fbufp - fbuf,
1077                                          mci, PXLF_MAPFROM);
1078                                 fbufp = fbuf;
1079                         }
1080                 }
1081         }
1082         else
1083         {
1084                 /* quoted-printable */
1085                 fbufp = fbuf;
1086                 while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
1087                 {
1088                         if (mime_fromqp((u_char *) buf, &fbufp, 0,
1089                                         &fbuf[MAXLINE] - fbufp) == 0)
1090                                 continue;
1091
1092                         if (fbufp - fbuf > 0)
1093                                 putxline((char *) fbuf, fbufp - fbuf - 1, mci,
1094                                          PXLF_MAPFROM);
1095                         fbufp = fbuf;
1096                 }
1097         }
1098
1099         /* force out partial last line */
1100         if (fbufp > fbuf)
1101         {
1102                 *fbufp = '\0';
1103                 putxline((char *) fbuf, fbufp - fbuf, mci, PXLF_MAPFROM);
1104         }
1105         if (tTd(43, 3))
1106                 printf("\t\t\tmime7to8 => %s to 8bit done\n", cte);
1107 }
1108 \f/*
1109 **  The following is based on Borenstein's "codes.c" module, with simplifying
1110 **  changes as we do not deal with multipart, and to do the translation in-core,
1111 **  with an attempt to prevent overrun of output buffers.
1112 **
1113 **  What is needed here are changes to defned this code better against
1114 **  bad encodings. Questionable to always return 0xFF for bad mappings.
1115 */
1116
1117 static char index_hex[128] =
1118 {
1119         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
1120         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
1121         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
1122         0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,
1123         -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
1124         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
1125         -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
1126         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
1127 };
1128
1129 #define HEXCHAR(c)  (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)])
1130
1131 int
1132 mime_fromqp(infile, outfile, state, maxlen)
1133         u_char *infile;
1134         u_char **outfile;
1135         int state;              /* Decoding body (0) or header (1) */
1136         int maxlen;             /* Max # of chars allowed in outfile */
1137 {
1138         int c1, c2;
1139         int nchar = 0;
1140
1141         while ((c1 = *infile++) != '\0')
1142         {
1143                 if (c1 == '=')
1144                 {
1145                         if ((c1 = *infile++) == 0)
1146                                 break;
1147
1148                         if (c1 == '\n' || (c1 = HEXCHAR(c1)) == -1)
1149                         {
1150                                 /* ignore it */
1151                                 if (state == 0)
1152                                         return 0;
1153                         }
1154                         else
1155                         {
1156                                 do
1157                                 {
1158                                         if ((c2 = *infile++) == '\0')
1159                                         {
1160                                                 c2 = -1;
1161                                                 break;
1162                                         }
1163                                 } while ((c2 = HEXCHAR(c2)) == -1);
1164
1165                                 if (c2 == -1 || ++nchar > maxlen)
1166                                         break;
1167
1168                                 *(*outfile)++ = c1 << 4 | c2;
1169                         }
1170                 }
1171                 else
1172                 {
1173                         if (state == 1 && c1 == '_')
1174                                 c1 = ' ';
1175
1176                         if (++nchar > maxlen)
1177                                 break;
1178
1179                         *(*outfile)++ = c1;
1180
1181                         if (c1 == '\n')
1182                                 break;
1183                 }
1184         }
1185         *(*outfile)++ = '\0';
1186         return 1;
1187 }
1188
1189
1190 #endif /* MIME7TO8 */