source: trunk/third/sendmail/src/mime.c @ 12554

Revision 12554, 26.7 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
RevLine 
[12553]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
17static 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 */
40char    Base16Code[] =  "0123456789ABCDEF";
41char    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
49static char     *MimeBoundaryNames[] =
50{
51        "SYNTAX",       "NOTSEP",       "INTERMED",     "FINAL"
52};
53
54bool    MapNLtoCRLF;
55
56extern int      mimeboundary __P((char *, char **));
57/*
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
84struct args
85{
86        char    *field;         /* name of field */
87        char    *value;         /* value of that field */
88};
89
90int
91mime8to7(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/*
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
664int
665mime_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/*
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
770int
771mime_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/*
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
807int
808mimeboundary(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/*
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
865char *
866defcharset(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/*
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
889int
890isboundary(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
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
933extern int      mime_fromqp __P((u_char *, u_char **, int, int));
934
935static 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
950void
951mime7to8(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/*
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
1117static 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
1131int
1132mime_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 */
Note: See TracBrowser for help on using the repository browser.