167545Seric /*
267545Seric * Copyright (c) 1994 Eric P. Allman
367545Seric * Copyright (c) 1994
467545Seric * The Regents of the University of California. All rights reserved.
567545Seric *
667545Seric * %sccs.include.redist.c%
767545Seric */
867545Seric
967545Seric # include "sendmail.h"
1067545Seric # include <string.h>
1167545Seric
1267545Seric #ifndef lint
13*69920Seric static char sccsid[] = "@(#)mime.c 8.26 (Berkeley) 06/18/95";
1467545Seric #endif /* not lint */
1567545Seric
1667545Seric /*
1767545Seric ** MIME support.
1867545Seric **
1967545Seric ** I am indebted to John Beck of Hewlett-Packard, who contributed
2067545Seric ** his code to me for inclusion. As it turns out, I did not use
2167545Seric ** his code since he used a "minimum change" approach that used
2267545Seric ** several temp files, and I wanted a "minimum impact" approach
2367545Seric ** that would avoid copying. However, looking over his code
2467545Seric ** helped me cement my understanding of the problem.
2567545Seric **
2667545Seric ** I also looked at, but did not directly use, Nathaniel
2767545Seric ** Borenstein's "code.c" module. Again, it functioned as
2867545Seric ** a file-to-file translator, which did not fit within my
2967545Seric ** design bounds, but it was a useful base for understanding
3067545Seric ** the problem.
3167545Seric */
3267545Seric
3369480Seric #if MIME8TO7
3467545Seric
3567545Seric /* character set for hex and base64 encoding */
3667545Seric char Base16Code[] = "0123456789ABCDEF";
3767545Seric char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3867545Seric
3967545Seric /* types of MIME boundaries */
4067545Seric #define MBT_SYNTAX 0 /* syntax error */
4167545Seric #define MBT_NOTSEP 1 /* not a boundary */
4267545Seric #define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */
4367545Seric #define MBT_FINAL 3 /* final boundary (trailing -- included) */
4467547Seric
4568717Seric static char *MimeBoundaryNames[] =
4668717Seric {
4768717Seric "SYNTAX", "NOTSEP", "INTERMED", "FINAL"
4868717Seric };
4967545Seric /*
5067545Seric ** MIME8TO7 -- output 8 bit body in 7 bit format
5167545Seric **
5267545Seric ** The header has already been output -- this has to do the
5367545Seric ** 8 to 7 bit conversion. It would be easy if we didn't have
5467545Seric ** to deal with nested formats (multipart/xxx and message/rfc822).
5567545Seric **
5667545Seric ** We won't be called if we don't have to do a conversion, and
5767545Seric ** appropriate MIME-Version: and Content-Type: fields have been
5867545Seric ** output. Any Content-Transfer-Encoding: field has not been
5967545Seric ** output, and we can add it here.
6067545Seric **
6167545Seric ** Parameters:
6267545Seric ** mci -- mailer connection information.
6367545Seric ** header -- the header for this body part.
6467545Seric ** e -- envelope.
6569281Seric ** boundaries -- the currently pending message boundaries.
6669281Seric ** NULL if we are processing the outer portion.
6768517Seric ** flags -- to tweak processing.
6867545Seric **
6967545Seric ** Returns:
7067545Seric ** An indicator of what terminated the message part:
7167545Seric ** MBT_FINAL -- the final boundary
7267545Seric ** MBT_INTERMED -- an intermediate boundary
7367545Seric ** MBT_NOTSEP -- an end of file
7467545Seric */
7567545Seric
7668517Seric struct args
7768517Seric {
7868517Seric char *field; /* name of field */
7968517Seric char *value; /* value of that field */
8068517Seric };
8168517Seric
8267545Seric int
mime8to7(mci,header,e,boundaries,flags)8369281Seric mime8to7(mci, header, e, boundaries, flags)
8467545Seric register MCI *mci;
8568717Seric HDR *header;
8668717Seric register ENVELOPE *e;
8768517Seric char **boundaries;
8868517Seric int flags;
8967545Seric {
9067545Seric register char *p;
9167545Seric int linelen;
9267545Seric int bt;
9367545Seric off_t offset;
9467545Seric size_t sectionsize, sectionhighbits;
9568517Seric int i;
9668517Seric char *type;
9768517Seric char *subtype;
9868847Seric char *cte;
9968517Seric char **pvp;
10068517Seric int argc = 0;
10168876Seric char *bp;
10268517Seric struct args argv[MAXMIMEARGS];
10367545Seric char bbuf[128];
10467545Seric char buf[MAXLINE];
10568517Seric char pvpbuf[MAXLINE];
10669748Seric extern u_char MimeTokenTab[256];
10767545Seric
10867545Seric if (tTd(43, 1))
10967545Seric {
11068717Seric printf("mime8to7: flags = %x, boundaries =", flags);
11168717Seric if (boundaries[0] == NULL)
11268717Seric printf(" <none>");
11368717Seric else
11468717Seric {
11568717Seric for (i = 0; boundaries[i] != NULL; i++)
11668717Seric printf(" %s", boundaries[i]);
11768717Seric }
11868717Seric printf("\n");
11967545Seric }
12068876Seric p = hvalue("Content-Transfer-Encoding", header);
12168876Seric if (p == NULL ||
12268876Seric (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
12368876Seric MimeTokenTab)) == NULL ||
12468876Seric pvp[0] == NULL)
12568876Seric {
12668876Seric cte = NULL;
12768876Seric }
12868876Seric else
12968876Seric {
13068876Seric cataddr(pvp, NULL, buf, sizeof buf, '\0');
13168876Seric cte = newstr(buf);
13268876Seric }
13368876Seric
13468847Seric type = subtype = NULL;
13567545Seric p = hvalue("Content-Type", header);
13668847Seric if (p == NULL)
13768847Seric {
13868847Seric if (bitset(M87F_DIGEST, flags))
13968847Seric p = "message/rfc822";
14068847Seric else
14168847Seric p = "text/plain";
14268847Seric }
14368517Seric if (p != NULL &&
14468711Seric (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
14568711Seric MimeTokenTab)) != NULL &&
14668517Seric pvp[0] != NULL)
14767545Seric {
14868717Seric if (tTd(43, 40))
14968717Seric {
15068717Seric for (i = 0; pvp[i] != NULL; i++)
15168717Seric printf("pvp[%d] = \"%s\"\n", i, pvp[i]);
15268717Seric }
15368517Seric type = *pvp++;
15468517Seric if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
15568517Seric *++pvp != NULL)
15668517Seric {
15768517Seric subtype = *pvp++;
15868517Seric }
15968517Seric
16068517Seric /* break out parameters */
16168517Seric while (*pvp != NULL && argc < MAXMIMEARGS)
16268517Seric {
16368517Seric /* skip to semicolon separator */
16468517Seric while (*pvp != NULL && strcmp(*pvp, ";") != 0)
16568517Seric pvp++;
16668517Seric if (*pvp++ == NULL || *pvp == NULL)
16768517Seric break;
16868517Seric
16968517Seric /* extract field name */
17068517Seric argv[argc].field = *pvp++;
17168517Seric
17268517Seric /* see if there is a value */
17368517Seric if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
17468517Seric (*++pvp == NULL || strcmp(*pvp, ";") != 0))
17568517Seric {
17668517Seric argv[argc].value = *pvp;
17768517Seric argc++;
17868517Seric }
17968517Seric }
18068517Seric }
18168717Seric
18268847Seric /* check for disaster cases */
18368847Seric if (type == NULL)
18468847Seric type = "-none-";
18568847Seric if (subtype == NULL)
18668847Seric subtype = "-none-";
18768847Seric
18868876Seric /* don't propogate some flags more than one level into the message */
18968876Seric flags &= ~M87F_DIGEST;
19068876Seric
19168847Seric /*
19268847Seric ** Check for cases that can not be encoded.
19368847Seric **
19468847Seric ** For example, you can't encode certain kinds of types
19568847Seric ** or already-encoded messages. If we find this case,
19668847Seric ** just copy it through.
19768847Seric */
19868847Seric
19968717Seric sprintf(buf, "%s/%s", type, subtype);
20068847Seric if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
20168717Seric flags |= M87F_NO8BIT;
20268717Seric
20368717Seric /*
20468717Seric ** Multipart requires special processing.
20568717Seric **
20668717Seric ** Do a recursive descent into the message.
20768717Seric */
20868717Seric
20968876Seric if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags))
21068517Seric {
21168876Seric int blen;
21267545Seric
21368847Seric if (strcasecmp(subtype, "digest") == 0)
21468847Seric flags |= M87F_DIGEST;
21568847Seric
21668517Seric for (i = 0; i < argc; i++)
21767545Seric {
21868517Seric if (strcasecmp(argv[i].field, "boundary") == 0)
21968517Seric break;
22068517Seric }
22168517Seric if (i >= argc)
22268517Seric {
22367545Seric syserr("mime8to7: Content-Type: %s missing boundary", p);
22467545Seric p = "---";
22567545Seric }
22667545Seric else
22768876Seric {
22868517Seric p = argv[i].value;
22968876Seric stripquotes(p);
23068876Seric }
23168876Seric blen = strlen(p);
23268876Seric if (blen > sizeof bbuf - 1)
23367545Seric {
23468876Seric syserr("mime8to7: multipart boundary \"%s\" too long",
23568876Seric p);
23668876Seric blen = sizeof bbuf - 1;
23767545Seric }
23868876Seric strncpy(bbuf, p, blen);
23968876Seric bbuf[blen] = '\0';
24067545Seric if (tTd(43, 1))
24167545Seric printf("mime8to7: multipart boundary \"%s\"\n", bbuf);
24268517Seric for (i = 0; i < MAXMIMENESTING; i++)
24368517Seric if (boundaries[i] == NULL)
24468517Seric break;
24568517Seric if (i >= MAXMIMENESTING)
24668517Seric syserr("mime8to7: multipart nesting boundary too deep");
24768517Seric else
24868517Seric {
24968517Seric boundaries[i] = bbuf;
25068517Seric boundaries[i + 1] = NULL;
25168517Seric }
25269281Seric mci->mci_flags |= MCIF_INMIME;
25367545Seric
25467545Seric /* skip the early "comment" prologue */
25568717Seric putline("", mci);
25667545Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
25767545Seric {
25868517Seric bt = mimeboundary(buf, boundaries);
25967545Seric if (bt != MBT_NOTSEP)
26067545Seric break;
26168847Seric putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
26268717Seric if (tTd(43, 99))
26368717Seric printf(" ...%s", buf);
26467545Seric }
26568717Seric if (feof(e->e_dfp))
26668717Seric bt = MBT_FINAL;
26767545Seric while (bt != MBT_FINAL)
26867545Seric {
26967545Seric auto HDR *hdr = NULL;
27067545Seric
27167545Seric sprintf(buf, "--%s", bbuf);
27267545Seric putline(buf, mci);
27368717Seric if (tTd(43, 35))
27468717Seric printf(" ...%s\n", buf);
27567545Seric collect(e->e_dfp, FALSE, FALSE, &hdr, e);
27668717Seric if (tTd(43, 101))
27768717Seric putline("+++after collect", mci);
27868876Seric putheader(mci, hdr, e);
27968717Seric if (tTd(43, 101))
28068717Seric putline("+++after putheader", mci);
28169281Seric bt = mime8to7(mci, hdr, e, boundaries, flags);
28267545Seric }
28367545Seric sprintf(buf, "--%s--", bbuf);
28467545Seric putline(buf, mci);
28568717Seric if (tTd(43, 35))
28668717Seric printf(" ...%s\n", buf);
28768717Seric boundaries[i] = NULL;
28869281Seric mci->mci_flags &= ~MCIF_INMIME;
28967545Seric
29067545Seric /* skip the late "comment" epilogue */
29167545Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
29267545Seric {
29368517Seric bt = mimeboundary(buf, boundaries);
29467545Seric if (bt != MBT_NOTSEP)
29567545Seric break;
29668847Seric putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
29768717Seric if (tTd(43, 99))
29868717Seric printf(" ...%s", buf);
29967545Seric }
30068717Seric if (feof(e->e_dfp))
30168717Seric bt = MBT_FINAL;
30268717Seric if (tTd(43, 3))
30368717Seric printf("\t\t\tmime8to7=>%s (multipart)\n",
30468717Seric MimeBoundaryNames[bt]);
30567545Seric return bt;
30667545Seric }
30767545Seric
30867545Seric /*
30968847Seric ** Message/* types -- recurse exactly once.
310*69920Seric **
311*69920Seric ** Class 'm' is predefined to have "rfc822" only.
31268847Seric */
31368847Seric
31468847Seric if (strcasecmp(type, "message") == 0)
31568847Seric {
316*69920Seric if (!wordinclass(subtype, 'm'))
31768876Seric {
31868876Seric flags |= M87F_NO8BIT;
31968876Seric }
32068876Seric else
32168876Seric {
32268876Seric auto HDR *hdr = NULL;
32368847Seric
32468876Seric putline("", mci);
32568847Seric
32669281Seric mci->mci_flags |= MCIF_INMIME;
32768876Seric collect(e->e_dfp, FALSE, FALSE, &hdr, e);
32868876Seric if (tTd(43, 101))
32968876Seric putline("+++after collect", mci);
33068876Seric putheader(mci, hdr, e);
33168876Seric if (tTd(43, 101))
33268876Seric putline("+++after putheader", mci);
33369918Seric if (hvalue("MIME-Version", hdr) == NULL)
33469918Seric putline("MIME-Version: 1.0", mci);
33568876Seric bt = mime8to7(mci, hdr, e, boundaries, flags);
33669281Seric mci->mci_flags &= ~MCIF_INMIME;
33768876Seric return bt;
33868876Seric }
33968847Seric }
34068847Seric
34168847Seric /*
34267545Seric ** Non-compound body type
34367545Seric **
34467545Seric ** Compute the ratio of seven to eight bit characters;
34567545Seric ** use that as a heuristic to decide how to do the
34667545Seric ** encoding.
34767545Seric */
34867545Seric
34967545Seric sectionsize = sectionhighbits = 0;
35068517Seric if (!bitset(M87F_NO8BIT, flags))
35167545Seric {
35268515Seric /* remember where we were */
35368515Seric offset = ftell(e->e_dfp);
35468515Seric if (offset == -1)
35568564Seric syserr("mime8to7: cannot ftell on df%s", e->e_id);
35668515Seric
35768515Seric /* do a scan of this body type to count character types */
35868515Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
35967545Seric {
36068717Seric if (mimeboundary(buf, boundaries) != MBT_NOTSEP)
36168515Seric break;
36268515Seric for (p = buf; *p != '\0'; p++)
36368515Seric {
36468515Seric /* count bytes with the high bit set */
36568515Seric sectionsize++;
36668515Seric if (bitset(0200, *p))
36768515Seric sectionhighbits++;
36868515Seric }
36968515Seric
37068515Seric /*
37168515Seric ** Heuristic: if 1/4 of the first 4K bytes are 8-bit,
37268515Seric ** assume base64. This heuristic avoids double-reading
37368515Seric ** large graphics or video files.
37468515Seric */
37568515Seric
37668515Seric if (sectionsize >= 4096 &&
37768515Seric sectionhighbits > sectionsize / 4)
37868515Seric break;
37967545Seric }
38067547Seric
38168515Seric /* return to the original offset for processing */
38268515Seric /* XXX use relative seeks to handle >31 bit file sizes? */
38368515Seric if (fseek(e->e_dfp, offset, SEEK_SET) < 0)
38468564Seric syserr("mime8to7: cannot fseek on df%s", e->e_id);
38568717Seric else
38668717Seric clearerr(e->e_dfp);
38767545Seric }
38867545Seric
38967547Seric /*
39067547Seric ** Heuristically determine encoding method.
39167547Seric ** If more than 1/8 of the total characters have the
39267547Seric ** eighth bit set, use base64; else use quoted-printable.
39368860Seric ** However, only encode binary encoded data as base64,
39468860Seric ** since otherwise the NL=>CRLF mapping will be a problem.
39567547Seric */
39667547Seric
39767545Seric if (tTd(43, 8))
39867545Seric {
39968860Seric printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s\n",
40068860Seric sectionhighbits, sectionsize,
40168860Seric cte == NULL ? "[none]" : cte);
40267545Seric }
40368860Seric if (cte != NULL && strcasecmp(cte, "binary") == 0)
40468860Seric sectionsize = sectionhighbits;
40568717Seric linelen = 0;
40668876Seric bp = buf;
40767554Seric if (sectionhighbits == 0)
40867545Seric {
40967554Seric /* no encoding necessary */
41068847Seric if (cte != NULL)
41167695Seric {
41268847Seric sprintf(buf, "Content-Transfer-Encoding: %s", cte);
41367695Seric putline(buf, mci);
41468717Seric if (tTd(43, 36))
41568717Seric printf(" ...%s\n", buf);
41667695Seric }
41767554Seric putline("", mci);
41867554Seric mci->mci_flags &= ~MCIF_INHEADER;
41967554Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
42067554Seric {
42168517Seric bt = mimeboundary(buf, boundaries);
42267554Seric if (bt != MBT_NOTSEP)
42367554Seric break;
42467554Seric putline(buf, mci);
42567554Seric }
42668717Seric if (feof(e->e_dfp))
42768717Seric bt = MBT_FINAL;
42867554Seric }
42967554Seric else if (sectionsize / 8 < sectionhighbits)
43067554Seric {
43167545Seric /* use base64 encoding */
43267545Seric int c1, c2;
43367545Seric
43467545Seric putline("Content-Transfer-Encoding: base64", mci);
43568717Seric if (tTd(43, 36))
43668717Seric printf(" ...Content-Transfer-Encoding: base64\n");
43767545Seric putline("", mci);
43867545Seric mci->mci_flags &= ~MCIF_INHEADER;
43969281Seric while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF)
44067545Seric {
44167545Seric if (linelen > 71)
44267545Seric {
44368876Seric *bp = '\0';
44468876Seric putline(buf, mci);
44567545Seric linelen = 0;
44668876Seric bp = buf;
44767545Seric }
44867545Seric linelen += 4;
44968876Seric *bp++ = Base64Code[(c1 >> 2)];
45067545Seric c1 = (c1 & 0x03) << 4;
45169281Seric c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
45267545Seric if (c2 == EOF)
45367545Seric {
45468876Seric *bp++ = Base64Code[c1];
45568876Seric *bp++ = '=';
45668876Seric *bp++ = '=';
45767545Seric break;
45867545Seric }
45967545Seric c1 |= (c2 >> 4) & 0x0f;
46068876Seric *bp++ = Base64Code[c1];
46167545Seric c1 = (c2 & 0x0f) << 2;
46269281Seric c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
46367545Seric if (c2 == EOF)
46467545Seric {
46568876Seric *bp++ = Base64Code[c1];
46668876Seric *bp++ = '=';
46767545Seric break;
46867545Seric }
46967545Seric c1 |= (c2 >> 6) & 0x03;
47068876Seric *bp++ = Base64Code[c1];
47168876Seric *bp++ = Base64Code[c2 & 0x3f];
47267545Seric }
47369600Seric *bp = '\0';
47469600Seric putline(buf, mci);
47567545Seric }
47667545Seric else
47767545Seric {
47867545Seric /* use quoted-printable encoding */
47967545Seric int c1, c2;
48068515Seric int fromstate;
48168862Seric BITMAP badchars;
48267545Seric
48368862Seric /* set up map of characters that must be mapped */
48468862Seric clrbitmap(badchars);
48568862Seric for (c1 = 0x00; c1 < 0x20; c1++)
48668862Seric setbitn(c1, badchars);
48768862Seric clrbitn('\t', badchars);
48868862Seric for (c1 = 0x7f; c1 < 0x100; c1++)
48968862Seric setbitn(c1, badchars);
49068862Seric setbitn('=', badchars);
49168862Seric if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags))
49268862Seric for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++)
49368862Seric setbitn(*p, badchars);
49468862Seric
49567545Seric putline("Content-Transfer-Encoding: quoted-printable", mci);
49668717Seric if (tTd(43, 36))
49768717Seric printf(" ...Content-Transfer-Encoding: quoted-printable\n");
49867545Seric putline("", mci);
49967545Seric mci->mci_flags &= ~MCIF_INHEADER;
50068717Seric fromstate = 0;
50167554Seric c2 = '\n';
50269281Seric while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF)
50367545Seric {
50467545Seric if (c1 == '\n')
50567545Seric {
50667545Seric if (c2 == ' ' || c2 == '\t')
50767545Seric {
50868876Seric *bp++ = '=';
50968876Seric *bp++ = Base16Code[(c2 >> 4) & 0x0f];
51068876Seric *bp++ = Base16Code[c2 & 0x0f];
51167545Seric }
51268876Seric *bp = '\0';
51368876Seric putline(buf, mci);
51468515Seric linelen = fromstate = 0;
51568876Seric bp = buf;
51667545Seric c2 = c1;
51767545Seric continue;
51867545Seric }
51968515Seric if (c2 == ' ' && linelen == 4 && fromstate == 4 &&
52068515Seric bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
52167840Seric {
52268876Seric *bp++ = '=';
52368876Seric *bp++ = '2';
52468876Seric *bp++ = '0';
52568515Seric linelen += 3;
52668515Seric }
52768515Seric else if (c2 == ' ' || c2 == '\t')
52868515Seric {
52968876Seric *bp++ = c2;
53067840Seric linelen++;
53167840Seric }
53267545Seric if (linelen > 72)
53367545Seric {
53468876Seric *bp++ = '=';
53568876Seric *bp = '\0';
53668876Seric putline(buf, mci);
53768515Seric linelen = fromstate = 0;
53868876Seric bp = buf;
53967554Seric c2 = '\n';
54067545Seric }
54168862Seric if (bitnset(c1 & 0xff, badchars))
54267545Seric {
54368876Seric *bp++ = '=';
54468876Seric *bp++ = Base16Code[(c1 >> 4) & 0x0f];
54568876Seric *bp++ = Base16Code[c1 & 0x0f];
54667545Seric linelen += 3;
54767545Seric }
54867840Seric else if (c1 != ' ' && c1 != '\t')
54967545Seric {
55068515Seric if (linelen < 4 && c1 == "From"[linelen])
55168515Seric fromstate++;
55268876Seric *bp++ = c1;
55367545Seric linelen++;
55467545Seric }
55567545Seric c2 = c1;
55667545Seric }
55767840Seric
55867840Seric /* output any saved character */
55967840Seric if (c2 == ' ' || c2 == '\t')
56067840Seric {
56168876Seric *bp++ = '=';
56268876Seric *bp++ = Base16Code[(c2 >> 4) & 0x0f];
56368876Seric *bp++ = Base16Code[c2 & 0x0f];
56468515Seric linelen += 3;
56567840Seric }
56669600Seric
56769600Seric if (linelen > 0 || boundaries[0] != NULL)
56869600Seric {
56969600Seric *bp = '\0';
57069600Seric putline(buf, mci);
57169600Seric }
57269600Seric
57367545Seric }
57468717Seric if (tTd(43, 3))
57568717Seric printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
57668717Seric return bt;
57767545Seric }
57868515Seric /*
57968515Seric ** MIME_GETCHAR -- get a character for MIME processing
58068515Seric **
58168515Seric ** Treats boundaries as EOF.
58268515Seric **
58368515Seric ** Parameters:
58468515Seric ** fp -- the input file.
58568517Seric ** boundaries -- the current MIME boundaries.
58668717Seric ** btp -- if the return value is EOF, *btp is set to
58768717Seric ** the type of the boundary.
58868515Seric **
58968515Seric ** Returns:
59068515Seric ** The next character in the input stream.
59168515Seric */
59267545Seric
59367545Seric int
mime_getchar(fp,boundaries,btp)59468717Seric mime_getchar(fp, boundaries, btp)
59567545Seric register FILE *fp;
59668517Seric char **boundaries;
59768717Seric int *btp;
59867545Seric {
59967545Seric int c;
60068864Seric static u_char *bp = NULL;
60167545Seric static int buflen = 0;
60267545Seric static bool atbol = TRUE; /* at beginning of line */
60368717Seric static int bt = MBT_SYNTAX; /* boundary type of next EOF */
60468864Seric static u_char buf[128]; /* need not be a full line */
60567545Seric
60667545Seric if (buflen > 0)
60767545Seric {
60867545Seric buflen--;
60967545Seric return *bp++;
61067545Seric }
61168515Seric bp = buf;
61268515Seric buflen = 0;
61368876Seric c = getc(fp);
61468515Seric if (c == '\n')
61568515Seric {
61668515Seric /* might be part of a MIME boundary */
61768515Seric *bp++ = c;
61868515Seric atbol = TRUE;
61968876Seric c = getc(fp);
62068876Seric if (c == '\n')
62168876Seric {
62268876Seric ungetc(c, fp);
62368876Seric return c;
62468876Seric }
62568515Seric }
62668515Seric if (c != EOF)
62768515Seric *bp++ = c;
62868717Seric else
62968717Seric bt = MBT_FINAL;
63068517Seric if (atbol && c == '-')
63167545Seric {
63267545Seric /* check for a message boundary */
63368876Seric c = getc(fp);
63467545Seric if (c != '-')
63567545Seric {
63667545Seric if (c != EOF)
63768515Seric *bp++ = c;
63868717Seric else
63968717Seric bt = MBT_FINAL;
64068515Seric buflen = bp - buf - 1;
64168515Seric bp = buf;
64268515Seric return *bp++;
64367545Seric }
64467545Seric
64567545Seric /* got "--", now check for rest of separator */
64667545Seric *bp++ = '-';
64768847Seric while (bp < &buf[sizeof buf - 2] &&
64868876Seric (c = getc(fp)) != EOF && c != '\n')
64967545Seric {
65067545Seric *bp++ = c;
65167545Seric }
65267545Seric *bp = '\0';
65368717Seric bt = mimeboundary(&buf[1], boundaries);
65468717Seric switch (bt)
65567545Seric {
65667545Seric case MBT_FINAL:
65767545Seric case MBT_INTERMED:
65867545Seric /* we have a message boundary */
65967545Seric buflen = 0;
66068717Seric *btp = bt;
66167545Seric return EOF;
66267545Seric }
66367545Seric
66467545Seric atbol = c == '\n';
66567545Seric if (c != EOF)
66667545Seric *bp++ = c;
66767545Seric }
66867545Seric
66968515Seric buflen = bp - buf - 1;
67068515Seric if (buflen < 0)
67168717Seric {
67268717Seric *btp = bt;
67368515Seric return EOF;
67468717Seric }
67568515Seric bp = buf;
67668515Seric return *bp++;
67767545Seric }
67867545Seric /*
67968847Seric ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF
68068847Seric **
68168847Seric ** Parameters:
68268847Seric ** fp -- the input file.
68368847Seric ** boundaries -- the current MIME boundaries.
68468847Seric ** btp -- if the return value is EOF, *btp is set to
68568847Seric ** the type of the boundary.
68668847Seric **
68768847Seric ** Returns:
68868847Seric ** The next character in the input stream.
68968847Seric */
69068847Seric
69168847Seric int
mime_getchar_crlf(fp,boundaries,btp)69268847Seric mime_getchar_crlf(fp, boundaries, btp)
69368847Seric register FILE *fp;
69468847Seric char **boundaries;
69568847Seric int *btp;
69668847Seric {
69768847Seric static bool sendlf = FALSE;
69868847Seric int c;
69968847Seric
70068847Seric if (sendlf)
70168847Seric {
70268847Seric sendlf = FALSE;
70368847Seric return '\n';
70468847Seric }
70568847Seric c = mime_getchar(fp, boundaries, btp);
70668847Seric if (c == '\n')
70768847Seric {
70868847Seric sendlf = TRUE;
70968847Seric return '\r';
71068847Seric }
71168847Seric return c;
71268847Seric }
71368847Seric /*
71467545Seric ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
71567545Seric **
71667545Seric ** Parameters:
71767545Seric ** line -- the input line.
71868517Seric ** boundaries -- the set of currently pending boundaries.
71967545Seric **
72067545Seric ** Returns:
72167545Seric ** MBT_NOTSEP -- if this is not a separator line
72267545Seric ** MBT_INTERMED -- if this is an intermediate separator
72367545Seric ** MBT_FINAL -- if this is a final boundary
72467545Seric ** MBT_SYNTAX -- if this is a boundary for the wrong
72567545Seric ** enclosure -- i.e., a syntax error.
72667545Seric */
72767545Seric
72867545Seric int
mimeboundary(line,boundaries)72968517Seric mimeboundary(line, boundaries)
73067545Seric register char *line;
73168517Seric char **boundaries;
73267545Seric {
73369474Seric int type = MBT_NOTSEP;
73467545Seric int i;
73568517Seric int savec;
73667545Seric
73768517Seric if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
73867545Seric return MBT_NOTSEP;
73967545Seric i = strlen(line);
74067545Seric if (line[i - 1] == '\n')
74167545Seric i--;
74269474Seric
74369474Seric /* strip off trailing whitespace */
74468515Seric while (line[i - 1] == ' ' || line[i - 1] == '\t')
74568515Seric i--;
74669474Seric savec = line[i];
74769474Seric line[i] = '\0';
74869474Seric
74969474Seric if (tTd(43, 5))
75069474Seric printf("mimeboundary: line=\"%s\"... ", line);
75169474Seric
75269474Seric /* check for this as an intermediate boundary */
75369474Seric if (isboundary(&line[2], boundaries) >= 0)
75469474Seric type = MBT_INTERMED;
75569474Seric else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0)
75667545Seric {
75769474Seric /* check for a final boundary */
75869474Seric line[i - 2] = '\0';
75969474Seric if (isboundary(&line[2], boundaries) >= 0)
76069474Seric type = MBT_FINAL;
76169474Seric line[i - 2] = '-';
76267545Seric }
76367545Seric
76468517Seric line[i] = savec;
76567545Seric if (tTd(43, 5))
76668717Seric printf("%s\n", MimeBoundaryNames[type]);
76767545Seric return type;
76867545Seric }
76967896Seric /*
77067896Seric ** DEFCHARSET -- return default character set for message
77167896Seric **
77267896Seric ** The first choice for character set is for the mailer
77367896Seric ** corresponding to the envelope sender. If neither that
77467896Seric ** nor the global configuration file has a default character
77567896Seric ** set defined, return "unknown-8bit" as recommended by
77667896Seric ** RFC 1428 section 3.
77767896Seric **
77867896Seric ** Parameters:
77967896Seric ** e -- the envelope for this message.
78067896Seric **
78167896Seric ** Returns:
78267896Seric ** The default character set for that mailer.
78367896Seric */
78467896Seric
78567896Seric char *
defcharset(e)78667896Seric defcharset(e)
78767896Seric register ENVELOPE *e;
78867896Seric {
78967896Seric if (e != NULL && e->e_from.q_mailer != NULL &&
79067896Seric e->e_from.q_mailer->m_defcharset != NULL)
79167896Seric return e->e_from.q_mailer->m_defcharset;
79267896Seric if (DefaultCharSet != NULL)
79367896Seric return DefaultCharSet;
79467896Seric return "unknown-8bit";
79567896Seric }
79668517Seric /*
79768517Seric ** ISBOUNDARY -- is a given string a currently valid boundary?
79868517Seric **
79968517Seric ** Parameters:
80068517Seric ** line -- the current input line.
80168517Seric ** boundaries -- the list of valid boundaries.
80268517Seric **
80368517Seric ** Returns:
80468517Seric ** The index number in boundaries if the line is found.
80568517Seric ** -1 -- otherwise.
80668517Seric **
80768517Seric */
80868517Seric
80968517Seric int
isboundary(line,boundaries)81068517Seric isboundary(line, boundaries)
81168517Seric char *line;
81268517Seric char **boundaries;
81368517Seric {
81468517Seric register int i;
81568517Seric
81668711Seric for (i = 0; boundaries[i] != NULL; i++)
81768517Seric {
81868517Seric if (strcmp(line, boundaries[i]) == 0)
81968517Seric return i;
82068517Seric }
82168517Seric return -1;
82268517Seric }
82369480Seric
82469480Seric #endif /* MIME */
825