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*69281Seric static char sccsid[] = "@(#)mime.c 8.20 (Berkeley) 05/05/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 3367545Seric 3467545Seric /* character set for hex and base64 encoding */ 3567545Seric char Base16Code[] = "0123456789ABCDEF"; 3667545Seric char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 3767545Seric 3867545Seric /* types of MIME boundaries */ 3967545Seric #define MBT_SYNTAX 0 /* syntax error */ 4067545Seric #define MBT_NOTSEP 1 /* not a boundary */ 4167545Seric #define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */ 4267545Seric #define MBT_FINAL 3 /* final boundary (trailing -- included) */ 4367547Seric 4468717Seric static char *MimeBoundaryNames[] = 4568717Seric { 4668717Seric "SYNTAX", "NOTSEP", "INTERMED", "FINAL" 4768717Seric }; 4867545Seric /* 4967545Seric ** MIME8TO7 -- output 8 bit body in 7 bit format 5067545Seric ** 5167545Seric ** The header has already been output -- this has to do the 5267545Seric ** 8 to 7 bit conversion. It would be easy if we didn't have 5367545Seric ** to deal with nested formats (multipart/xxx and message/rfc822). 5467545Seric ** 5567545Seric ** We won't be called if we don't have to do a conversion, and 5667545Seric ** appropriate MIME-Version: and Content-Type: fields have been 5767545Seric ** output. Any Content-Transfer-Encoding: field has not been 5867545Seric ** output, and we can add it here. 5967545Seric ** 6067545Seric ** Parameters: 6167545Seric ** mci -- mailer connection information. 6267545Seric ** header -- the header for this body part. 6367545Seric ** e -- envelope. 64*69281Seric ** boundaries -- the currently pending message boundaries. 65*69281Seric ** NULL if we are processing the outer portion. 6668517Seric ** flags -- to tweak processing. 6767545Seric ** 6867545Seric ** Returns: 6967545Seric ** An indicator of what terminated the message part: 7067545Seric ** MBT_FINAL -- the final boundary 7167545Seric ** MBT_INTERMED -- an intermediate boundary 7267545Seric ** MBT_NOTSEP -- an end of file 7367545Seric */ 7467545Seric 7568517Seric struct args 7668517Seric { 7768517Seric char *field; /* name of field */ 7868517Seric char *value; /* value of that field */ 7968517Seric }; 8068517Seric 8167545Seric int 82*69281Seric mime8to7(mci, header, e, boundaries, flags) 8367545Seric register MCI *mci; 8468717Seric HDR *header; 8568717Seric register ENVELOPE *e; 8668517Seric char **boundaries; 8768517Seric int flags; 8867545Seric { 8967545Seric register char *p; 9067545Seric int linelen; 9167545Seric int bt; 9267545Seric off_t offset; 9367545Seric size_t sectionsize, sectionhighbits; 9468517Seric int i; 9568517Seric char *type; 9668517Seric char *subtype; 9768847Seric char *cte; 9868517Seric char **pvp; 9968517Seric int argc = 0; 10068876Seric char *bp; 10168517Seric struct args argv[MAXMIMEARGS]; 10267545Seric char bbuf[128]; 10367545Seric char buf[MAXLINE]; 10468517Seric char pvpbuf[MAXLINE]; 10568711Seric extern char MimeTokenTab[256]; 10667545Seric 10767545Seric if (tTd(43, 1)) 10867545Seric { 10968717Seric printf("mime8to7: flags = %x, boundaries =", flags); 11068717Seric if (boundaries[0] == NULL) 11168717Seric printf(" <none>"); 11268717Seric else 11368717Seric { 11468717Seric for (i = 0; boundaries[i] != NULL; i++) 11568717Seric printf(" %s", boundaries[i]); 11668717Seric } 11768717Seric printf("\n"); 11867545Seric } 11968876Seric p = hvalue("Content-Transfer-Encoding", header); 12068876Seric if (p == NULL || 12168876Seric (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, 12268876Seric MimeTokenTab)) == NULL || 12368876Seric pvp[0] == NULL) 12468876Seric { 12568876Seric cte = NULL; 12668876Seric } 12768876Seric else 12868876Seric { 12968876Seric cataddr(pvp, NULL, buf, sizeof buf, '\0'); 13068876Seric cte = newstr(buf); 13168876Seric } 13268876Seric 13368847Seric type = subtype = NULL; 13467545Seric p = hvalue("Content-Type", header); 13568847Seric if (p == NULL) 13668847Seric { 13768847Seric if (bitset(M87F_DIGEST, flags)) 13868847Seric p = "message/rfc822"; 13968847Seric else 14068847Seric p = "text/plain"; 14168847Seric } 14268517Seric if (p != NULL && 14368711Seric (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, 14468711Seric MimeTokenTab)) != NULL && 14568517Seric pvp[0] != NULL) 14667545Seric { 14768717Seric if (tTd(43, 40)) 14868717Seric { 14968717Seric for (i = 0; pvp[i] != NULL; i++) 15068717Seric printf("pvp[%d] = \"%s\"\n", i, pvp[i]); 15168717Seric } 15268517Seric type = *pvp++; 15368517Seric if (*pvp != NULL && strcmp(*pvp, "/") == 0 && 15468517Seric *++pvp != NULL) 15568517Seric { 15668517Seric subtype = *pvp++; 15768517Seric } 15868517Seric 15968517Seric /* break out parameters */ 16068517Seric while (*pvp != NULL && argc < MAXMIMEARGS) 16168517Seric { 16268517Seric /* skip to semicolon separator */ 16368517Seric while (*pvp != NULL && strcmp(*pvp, ";") != 0) 16468517Seric pvp++; 16568517Seric if (*pvp++ == NULL || *pvp == NULL) 16668517Seric break; 16768517Seric 16868517Seric /* extract field name */ 16968517Seric argv[argc].field = *pvp++; 17068517Seric 17168517Seric /* see if there is a value */ 17268517Seric if (*pvp != NULL && strcmp(*pvp, "=") == 0 && 17368517Seric (*++pvp == NULL || strcmp(*pvp, ";") != 0)) 17468517Seric { 17568517Seric argv[argc].value = *pvp; 17668517Seric argc++; 17768517Seric } 17868517Seric } 17968517Seric } 18068717Seric 18168847Seric /* check for disaster cases */ 18268847Seric if (type == NULL) 18368847Seric type = "-none-"; 18468847Seric if (subtype == NULL) 18568847Seric subtype = "-none-"; 18668847Seric 18768876Seric /* don't propogate some flags more than one level into the message */ 18868876Seric flags &= ~M87F_DIGEST; 18968876Seric 19068847Seric /* 19168847Seric ** Check for cases that can not be encoded. 19268847Seric ** 19368847Seric ** For example, you can't encode certain kinds of types 19468847Seric ** or already-encoded messages. If we find this case, 19568847Seric ** just copy it through. 19668847Seric */ 19768847Seric 19868717Seric sprintf(buf, "%s/%s", type, subtype); 19968847Seric if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) 20068717Seric flags |= M87F_NO8BIT; 20168717Seric 20268717Seric /* 20368717Seric ** Multipart requires special processing. 20468717Seric ** 20568717Seric ** Do a recursive descent into the message. 20668717Seric */ 20768717Seric 20868876Seric if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags)) 20968517Seric { 21068876Seric int blen; 21167545Seric 21268847Seric if (strcasecmp(subtype, "digest") == 0) 21368847Seric flags |= M87F_DIGEST; 21468847Seric 21568517Seric for (i = 0; i < argc; i++) 21667545Seric { 21768517Seric if (strcasecmp(argv[i].field, "boundary") == 0) 21868517Seric break; 21968517Seric } 22068517Seric if (i >= argc) 22168517Seric { 22267545Seric syserr("mime8to7: Content-Type: %s missing boundary", p); 22367545Seric p = "---"; 22467545Seric } 22567545Seric else 22668876Seric { 22768517Seric p = argv[i].value; 22868876Seric stripquotes(p); 22968876Seric } 23068876Seric blen = strlen(p); 23168876Seric if (blen > sizeof bbuf - 1) 23267545Seric { 23368876Seric syserr("mime8to7: multipart boundary \"%s\" too long", 23468876Seric p); 23568876Seric blen = sizeof bbuf - 1; 23667545Seric } 23768876Seric strncpy(bbuf, p, blen); 23868876Seric bbuf[blen] = '\0'; 23967545Seric if (tTd(43, 1)) 24067545Seric printf("mime8to7: multipart boundary \"%s\"\n", bbuf); 24168517Seric for (i = 0; i < MAXMIMENESTING; i++) 24268517Seric if (boundaries[i] == NULL) 24368517Seric break; 24468517Seric if (i >= MAXMIMENESTING) 24568517Seric syserr("mime8to7: multipart nesting boundary too deep"); 24668517Seric else 24768517Seric { 24868517Seric boundaries[i] = bbuf; 24968517Seric boundaries[i + 1] = NULL; 25068517Seric } 251*69281Seric mci->mci_flags |= MCIF_INMIME; 25267545Seric 25367545Seric /* skip the early "comment" prologue */ 25468717Seric putline("", mci); 25567545Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 25667545Seric { 25768517Seric bt = mimeboundary(buf, boundaries); 25867545Seric if (bt != MBT_NOTSEP) 25967545Seric break; 26068847Seric putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); 26168717Seric if (tTd(43, 99)) 26268717Seric printf(" ...%s", buf); 26367545Seric } 26468717Seric if (feof(e->e_dfp)) 26568717Seric bt = MBT_FINAL; 26667545Seric while (bt != MBT_FINAL) 26767545Seric { 26867545Seric auto HDR *hdr = NULL; 26967545Seric 27067545Seric sprintf(buf, "--%s", bbuf); 27167545Seric putline(buf, mci); 27268717Seric if (tTd(43, 35)) 27368717Seric printf(" ...%s\n", buf); 27467545Seric collect(e->e_dfp, FALSE, FALSE, &hdr, e); 27568717Seric if (tTd(43, 101)) 27668717Seric putline("+++after collect", mci); 27768876Seric putheader(mci, hdr, e); 27868717Seric if (tTd(43, 101)) 27968717Seric putline("+++after putheader", mci); 280*69281Seric bt = mime8to7(mci, hdr, e, boundaries, flags); 28167545Seric } 28267545Seric sprintf(buf, "--%s--", bbuf); 28367545Seric putline(buf, mci); 28468717Seric if (tTd(43, 35)) 28568717Seric printf(" ...%s\n", buf); 28668717Seric boundaries[i] = NULL; 287*69281Seric mci->mci_flags &= ~MCIF_INMIME; 28867545Seric 28967545Seric /* skip the late "comment" epilogue */ 29067545Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 29167545Seric { 29268517Seric bt = mimeboundary(buf, boundaries); 29367545Seric if (bt != MBT_NOTSEP) 29467545Seric break; 29568847Seric putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); 29668717Seric if (tTd(43, 99)) 29768717Seric printf(" ...%s", buf); 29867545Seric } 29968717Seric if (feof(e->e_dfp)) 30068717Seric bt = MBT_FINAL; 30168717Seric if (tTd(43, 3)) 30268717Seric printf("\t\t\tmime8to7=>%s (multipart)\n", 30368717Seric MimeBoundaryNames[bt]); 30467545Seric return bt; 30567545Seric } 30667545Seric 30767545Seric /* 30868847Seric ** Message/* types -- recurse exactly once. 30968847Seric */ 31068847Seric 31168847Seric if (strcasecmp(type, "message") == 0) 31268847Seric { 31368876Seric if (strcasecmp(subtype, "rfc822") != 0) 31468876Seric { 31568876Seric flags |= M87F_NO8BIT; 31668876Seric } 31768876Seric else 31868876Seric { 31968876Seric register char *q; 32068876Seric auto HDR *hdr = NULL; 32168847Seric 32268876Seric putline("", mci); 32368847Seric 324*69281Seric mci->mci_flags |= MCIF_INMIME; 32568876Seric collect(e->e_dfp, FALSE, FALSE, &hdr, e); 32668876Seric if (tTd(43, 101)) 32768876Seric putline("+++after collect", mci); 32868876Seric putheader(mci, hdr, e); 32968876Seric if (tTd(43, 101)) 33068876Seric putline("+++after putheader", mci); 33168876Seric bt = mime8to7(mci, hdr, e, boundaries, flags); 332*69281Seric mci->mci_flags &= ~MCIF_INMIME; 33368876Seric return bt; 33468876Seric } 33568847Seric } 33668847Seric 33768847Seric /* 33867545Seric ** Non-compound body type 33967545Seric ** 34067545Seric ** Compute the ratio of seven to eight bit characters; 34167545Seric ** use that as a heuristic to decide how to do the 34267545Seric ** encoding. 34367545Seric */ 34467545Seric 34567545Seric sectionsize = sectionhighbits = 0; 34668517Seric if (!bitset(M87F_NO8BIT, flags)) 34767545Seric { 34868515Seric /* remember where we were */ 34968515Seric offset = ftell(e->e_dfp); 35068515Seric if (offset == -1) 35168564Seric syserr("mime8to7: cannot ftell on df%s", e->e_id); 35268515Seric 35368515Seric /* do a scan of this body type to count character types */ 35468515Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 35567545Seric { 35668717Seric if (mimeboundary(buf, boundaries) != MBT_NOTSEP) 35768515Seric break; 35868515Seric for (p = buf; *p != '\0'; p++) 35968515Seric { 36068515Seric /* count bytes with the high bit set */ 36168515Seric sectionsize++; 36268515Seric if (bitset(0200, *p)) 36368515Seric sectionhighbits++; 36468515Seric } 36568515Seric 36668515Seric /* 36768515Seric ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, 36868515Seric ** assume base64. This heuristic avoids double-reading 36968515Seric ** large graphics or video files. 37068515Seric */ 37168515Seric 37268515Seric if (sectionsize >= 4096 && 37368515Seric sectionhighbits > sectionsize / 4) 37468515Seric break; 37567545Seric } 37667547Seric 37768515Seric /* return to the original offset for processing */ 37868515Seric /* XXX use relative seeks to handle >31 bit file sizes? */ 37968515Seric if (fseek(e->e_dfp, offset, SEEK_SET) < 0) 38068564Seric syserr("mime8to7: cannot fseek on df%s", e->e_id); 38168717Seric else 38268717Seric clearerr(e->e_dfp); 38367545Seric } 38467545Seric 38567547Seric /* 38667547Seric ** Heuristically determine encoding method. 38767547Seric ** If more than 1/8 of the total characters have the 38867547Seric ** eighth bit set, use base64; else use quoted-printable. 38968860Seric ** However, only encode binary encoded data as base64, 39068860Seric ** since otherwise the NL=>CRLF mapping will be a problem. 39167547Seric */ 39267547Seric 39367545Seric if (tTd(43, 8)) 39467545Seric { 39568860Seric printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s\n", 39668860Seric sectionhighbits, sectionsize, 39768860Seric cte == NULL ? "[none]" : cte); 39867545Seric } 39968860Seric if (cte != NULL && strcasecmp(cte, "binary") == 0) 40068860Seric sectionsize = sectionhighbits; 40168717Seric linelen = 0; 40268876Seric bp = buf; 40367554Seric if (sectionhighbits == 0) 40467545Seric { 40567554Seric /* no encoding necessary */ 40668847Seric if (cte != NULL) 40767695Seric { 40868847Seric sprintf(buf, "Content-Transfer-Encoding: %s", cte); 40967695Seric putline(buf, mci); 41068717Seric if (tTd(43, 36)) 41168717Seric printf(" ...%s\n", buf); 41267695Seric } 41367554Seric putline("", mci); 41467554Seric mci->mci_flags &= ~MCIF_INHEADER; 41567554Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 41667554Seric { 41768517Seric bt = mimeboundary(buf, boundaries); 41867554Seric if (bt != MBT_NOTSEP) 41967554Seric break; 42067554Seric putline(buf, mci); 42167554Seric } 42268717Seric if (feof(e->e_dfp)) 42368717Seric bt = MBT_FINAL; 42467554Seric } 42567554Seric else if (sectionsize / 8 < sectionhighbits) 42667554Seric { 42767545Seric /* use base64 encoding */ 42867545Seric int c1, c2; 42967545Seric 43067545Seric putline("Content-Transfer-Encoding: base64", mci); 43168717Seric if (tTd(43, 36)) 43268717Seric printf(" ...Content-Transfer-Encoding: base64\n"); 43367545Seric putline("", mci); 43467545Seric mci->mci_flags &= ~MCIF_INHEADER; 435*69281Seric while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF) 43667545Seric { 43767545Seric if (linelen > 71) 43867545Seric { 43968876Seric *bp = '\0'; 44068876Seric putline(buf, mci); 44167545Seric linelen = 0; 44268876Seric bp = buf; 44367545Seric } 44467545Seric linelen += 4; 44568876Seric *bp++ = Base64Code[(c1 >> 2)]; 44667545Seric c1 = (c1 & 0x03) << 4; 447*69281Seric c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 44867545Seric if (c2 == EOF) 44967545Seric { 45068876Seric *bp++ = Base64Code[c1]; 45168876Seric *bp++ = '='; 45268876Seric *bp++ = '='; 45367545Seric break; 45467545Seric } 45567545Seric c1 |= (c2 >> 4) & 0x0f; 45668876Seric *bp++ = Base64Code[c1]; 45767545Seric c1 = (c2 & 0x0f) << 2; 458*69281Seric c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 45967545Seric if (c2 == EOF) 46067545Seric { 46168876Seric *bp++ = Base64Code[c1]; 46268876Seric *bp++ = '='; 46367545Seric break; 46467545Seric } 46567545Seric c1 |= (c2 >> 6) & 0x03; 46668876Seric *bp++ = Base64Code[c1]; 46768876Seric *bp++ = Base64Code[c2 & 0x3f]; 46867545Seric } 46967545Seric } 47067545Seric else 47167545Seric { 47267545Seric /* use quoted-printable encoding */ 47367545Seric int c1, c2; 47468515Seric int fromstate; 47568862Seric BITMAP badchars; 47667545Seric 47768862Seric /* set up map of characters that must be mapped */ 47868862Seric clrbitmap(badchars); 47968862Seric for (c1 = 0x00; c1 < 0x20; c1++) 48068862Seric setbitn(c1, badchars); 48168862Seric clrbitn('\t', badchars); 48268862Seric for (c1 = 0x7f; c1 < 0x100; c1++) 48368862Seric setbitn(c1, badchars); 48468862Seric setbitn('=', badchars); 48568862Seric if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) 48668862Seric for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) 48768862Seric setbitn(*p, badchars); 48868862Seric 48967545Seric putline("Content-Transfer-Encoding: quoted-printable", mci); 49068717Seric if (tTd(43, 36)) 49168717Seric printf(" ...Content-Transfer-Encoding: quoted-printable\n"); 49267545Seric putline("", mci); 49367545Seric mci->mci_flags &= ~MCIF_INHEADER; 49468717Seric fromstate = 0; 49567554Seric c2 = '\n'; 496*69281Seric while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF) 49767545Seric { 49867545Seric if (c1 == '\n') 49967545Seric { 50067545Seric if (c2 == ' ' || c2 == '\t') 50167545Seric { 50268876Seric *bp++ = '='; 50368876Seric *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 50468876Seric *bp++ = Base16Code[c2 & 0x0f]; 50568876Seric *bp = '\0'; 50668876Seric putline(buf, mci); 50768876Seric bp = buf; 50867545Seric } 50968876Seric *bp = '\0'; 51068876Seric putline(buf, mci); 51168515Seric linelen = fromstate = 0; 51268876Seric bp = buf; 51367545Seric c2 = c1; 51467545Seric continue; 51567545Seric } 51668515Seric if (c2 == ' ' && linelen == 4 && fromstate == 4 && 51768515Seric bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 51867840Seric { 51968876Seric *bp++ = '='; 52068876Seric *bp++ = '2'; 52168876Seric *bp++ = '0'; 52268515Seric linelen += 3; 52368515Seric } 52468515Seric else if (c2 == ' ' || c2 == '\t') 52568515Seric { 52668876Seric *bp++ = c2; 52767840Seric linelen++; 52867840Seric } 52967545Seric if (linelen > 72) 53067545Seric { 53168876Seric *bp++ = '='; 53268876Seric *bp = '\0'; 53368876Seric putline(buf, mci); 53468515Seric linelen = fromstate = 0; 53568876Seric bp = buf; 53667554Seric c2 = '\n'; 53767545Seric } 53868862Seric if (bitnset(c1 & 0xff, badchars)) 53967545Seric { 54068876Seric *bp++ = '='; 54168876Seric *bp++ = Base16Code[(c1 >> 4) & 0x0f]; 54268876Seric *bp++ = Base16Code[c1 & 0x0f]; 54367545Seric linelen += 3; 54467545Seric } 54567840Seric else if (c1 != ' ' && c1 != '\t') 54667545Seric { 54768515Seric if (linelen < 4 && c1 == "From"[linelen]) 54868515Seric fromstate++; 54968876Seric *bp++ = c1; 55067545Seric linelen++; 55167545Seric } 55267545Seric c2 = c1; 55367545Seric } 55467840Seric 55567840Seric /* output any saved character */ 55667840Seric if (c2 == ' ' || c2 == '\t') 55767840Seric { 55868876Seric *bp++ = '='; 55968876Seric *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 56068876Seric *bp++ = Base16Code[c2 & 0x0f]; 56168515Seric linelen += 3; 56267840Seric } 56367545Seric } 56467545Seric if (linelen > 0) 56568876Seric { 56668876Seric *bp = '\0'; 56768876Seric putline(buf, mci); 56868876Seric } 56968717Seric if (tTd(43, 3)) 57068717Seric printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); 57168717Seric return bt; 57267545Seric } 57368515Seric /* 57468515Seric ** MIME_GETCHAR -- get a character for MIME processing 57568515Seric ** 57668515Seric ** Treats boundaries as EOF. 57768515Seric ** 57868515Seric ** Parameters: 57968515Seric ** fp -- the input file. 58068517Seric ** boundaries -- the current MIME boundaries. 58168717Seric ** btp -- if the return value is EOF, *btp is set to 58268717Seric ** the type of the boundary. 58368515Seric ** 58468515Seric ** Returns: 58568515Seric ** The next character in the input stream. 58668515Seric */ 58767545Seric 58867545Seric int 58968717Seric mime_getchar(fp, boundaries, btp) 59067545Seric register FILE *fp; 59168517Seric char **boundaries; 59268717Seric int *btp; 59367545Seric { 59467545Seric int c; 59568864Seric static u_char *bp = NULL; 59667545Seric static int buflen = 0; 59767545Seric static bool atbol = TRUE; /* at beginning of line */ 59868717Seric static int bt = MBT_SYNTAX; /* boundary type of next EOF */ 59968864Seric static u_char buf[128]; /* need not be a full line */ 60067545Seric 60167545Seric if (buflen > 0) 60267545Seric { 60367545Seric buflen--; 60467545Seric return *bp++; 60567545Seric } 60668515Seric bp = buf; 60768515Seric buflen = 0; 60868876Seric c = getc(fp); 60968515Seric if (c == '\n') 61068515Seric { 61168515Seric /* might be part of a MIME boundary */ 61268515Seric *bp++ = c; 61368515Seric atbol = TRUE; 61468876Seric c = getc(fp); 61568876Seric if (c == '\n') 61668876Seric { 61768876Seric ungetc(c, fp); 61868876Seric return c; 61968876Seric } 62068515Seric } 62168515Seric if (c != EOF) 62268515Seric *bp++ = c; 62368717Seric else 62468717Seric bt = MBT_FINAL; 62568517Seric if (atbol && c == '-') 62667545Seric { 62767545Seric /* check for a message boundary */ 62868876Seric c = getc(fp); 62967545Seric if (c != '-') 63067545Seric { 63167545Seric if (c != EOF) 63268515Seric *bp++ = c; 63368717Seric else 63468717Seric bt = MBT_FINAL; 63568515Seric buflen = bp - buf - 1; 63668515Seric bp = buf; 63768515Seric return *bp++; 63867545Seric } 63967545Seric 64067545Seric /* got "--", now check for rest of separator */ 64167545Seric *bp++ = '-'; 64268847Seric while (bp < &buf[sizeof buf - 2] && 64368876Seric (c = getc(fp)) != EOF && c != '\n') 64467545Seric { 64567545Seric *bp++ = c; 64667545Seric } 64767545Seric *bp = '\0'; 64868717Seric bt = mimeboundary(&buf[1], boundaries); 64968717Seric switch (bt) 65067545Seric { 65167545Seric case MBT_FINAL: 65267545Seric case MBT_INTERMED: 65367545Seric /* we have a message boundary */ 65467545Seric buflen = 0; 65568717Seric *btp = bt; 65667545Seric return EOF; 65767545Seric } 65867545Seric 65967545Seric atbol = c == '\n'; 66067545Seric if (c != EOF) 66167545Seric *bp++ = c; 66267545Seric } 66367545Seric 66468515Seric buflen = bp - buf - 1; 66568515Seric if (buflen < 0) 66668717Seric { 66768717Seric *btp = bt; 66868515Seric return EOF; 66968717Seric } 67068515Seric bp = buf; 67168515Seric return *bp++; 67267545Seric } 67367545Seric /* 67468847Seric ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF 67568847Seric ** 67668847Seric ** Parameters: 67768847Seric ** fp -- the input file. 67868847Seric ** boundaries -- the current MIME boundaries. 67968847Seric ** btp -- if the return value is EOF, *btp is set to 68068847Seric ** the type of the boundary. 68168847Seric ** 68268847Seric ** Returns: 68368847Seric ** The next character in the input stream. 68468847Seric */ 68568847Seric 68668847Seric int 68768847Seric mime_getchar_crlf(fp, boundaries, btp) 68868847Seric register FILE *fp; 68968847Seric char **boundaries; 69068847Seric int *btp; 69168847Seric { 69268847Seric static bool sendlf = FALSE; 69368847Seric int c; 69468847Seric 69568847Seric if (sendlf) 69668847Seric { 69768847Seric sendlf = FALSE; 69868847Seric return '\n'; 69968847Seric } 70068847Seric c = mime_getchar(fp, boundaries, btp); 70168847Seric if (c == '\n') 70268847Seric { 70368847Seric sendlf = TRUE; 70468847Seric return '\r'; 70568847Seric } 70668847Seric return c; 70768847Seric } 70868847Seric /* 70967545Seric ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type 71067545Seric ** 71167545Seric ** Parameters: 71267545Seric ** line -- the input line. 71368517Seric ** boundaries -- the set of currently pending boundaries. 71467545Seric ** 71567545Seric ** Returns: 71667545Seric ** MBT_NOTSEP -- if this is not a separator line 71767545Seric ** MBT_INTERMED -- if this is an intermediate separator 71867545Seric ** MBT_FINAL -- if this is a final boundary 71967545Seric ** MBT_SYNTAX -- if this is a boundary for the wrong 72067545Seric ** enclosure -- i.e., a syntax error. 72167545Seric */ 72267545Seric 72367545Seric int 72468517Seric mimeboundary(line, boundaries) 72567545Seric register char *line; 72668517Seric char **boundaries; 72767545Seric { 72867545Seric int type; 72967545Seric int i; 73068517Seric int savec; 73167545Seric 73268517Seric if (line[0] != '-' || line[1] != '-' || boundaries == NULL) 73367545Seric return MBT_NOTSEP; 73467545Seric i = strlen(line); 73567545Seric if (line[i - 1] == '\n') 73667545Seric i--; 73768717Seric if (tTd(43, 5)) 73868717Seric printf("mimeboundary: line=\"%.*s\"... ", i, line); 73968515Seric while (line[i - 1] == ' ' || line[i - 1] == '\t') 74068515Seric i--; 74167545Seric if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) 74267545Seric { 74367545Seric type = MBT_FINAL; 74467545Seric i -= 2; 74567545Seric } 74667545Seric else 74767545Seric type = MBT_INTERMED; 74867545Seric 74968517Seric savec = line[i]; 75068517Seric line[i] = '\0'; 75167545Seric /* XXX should check for improper nesting here */ 75268517Seric if (isboundary(&line[2], boundaries) < 0) 75367545Seric type = MBT_NOTSEP; 75468517Seric line[i] = savec; 75567545Seric if (tTd(43, 5)) 75668717Seric printf("%s\n", MimeBoundaryNames[type]); 75767545Seric return type; 75867545Seric } 75967896Seric /* 76067896Seric ** DEFCHARSET -- return default character set for message 76167896Seric ** 76267896Seric ** The first choice for character set is for the mailer 76367896Seric ** corresponding to the envelope sender. If neither that 76467896Seric ** nor the global configuration file has a default character 76567896Seric ** set defined, return "unknown-8bit" as recommended by 76667896Seric ** RFC 1428 section 3. 76767896Seric ** 76867896Seric ** Parameters: 76967896Seric ** e -- the envelope for this message. 77067896Seric ** 77167896Seric ** Returns: 77267896Seric ** The default character set for that mailer. 77367896Seric */ 77467896Seric 77567896Seric char * 77667896Seric defcharset(e) 77767896Seric register ENVELOPE *e; 77867896Seric { 77967896Seric if (e != NULL && e->e_from.q_mailer != NULL && 78067896Seric e->e_from.q_mailer->m_defcharset != NULL) 78167896Seric return e->e_from.q_mailer->m_defcharset; 78267896Seric if (DefaultCharSet != NULL) 78367896Seric return DefaultCharSet; 78467896Seric return "unknown-8bit"; 78567896Seric } 78668517Seric /* 78768517Seric ** ISBOUNDARY -- is a given string a currently valid boundary? 78868517Seric ** 78968517Seric ** Parameters: 79068517Seric ** line -- the current input line. 79168517Seric ** boundaries -- the list of valid boundaries. 79268517Seric ** 79368517Seric ** Returns: 79468517Seric ** The index number in boundaries if the line is found. 79568517Seric ** -1 -- otherwise. 79668517Seric ** 79768517Seric */ 79868517Seric 79968517Seric int 80068517Seric isboundary(line, boundaries) 80168517Seric char *line; 80268517Seric char **boundaries; 80368517Seric { 80468517Seric register int i; 80568517Seric 80668711Seric for (i = 0; boundaries[i] != NULL; i++) 80768517Seric { 80868517Seric if (strcmp(line, boundaries[i]) == 0) 80968517Seric return i; 81068517Seric } 81168517Seric return -1; 81268517Seric } 813