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*69918Seric static char sccsid[] = "@(#)mime.c 8.25 (Berkeley) 06/17/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 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. 31068847Seric */ 31168847Seric 31268847Seric if (strcasecmp(type, "message") == 0) 31368847Seric { 31468876Seric if (strcasecmp(subtype, "rfc822") != 0) 31568876Seric { 31668876Seric flags |= M87F_NO8BIT; 31768876Seric } 31868876Seric else 31968876Seric { 32068876Seric auto HDR *hdr = NULL; 32168847Seric 32268876Seric putline("", mci); 32368847Seric 32469281Seric 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); 331*69918Seric if (hvalue("MIME-Version", hdr) == NULL) 332*69918Seric putline("MIME-Version: 1.0", mci); 33368876Seric bt = mime8to7(mci, hdr, e, boundaries, flags); 33469281Seric mci->mci_flags &= ~MCIF_INMIME; 33568876Seric return bt; 33668876Seric } 33768847Seric } 33868847Seric 33968847Seric /* 34067545Seric ** Non-compound body type 34167545Seric ** 34267545Seric ** Compute the ratio of seven to eight bit characters; 34367545Seric ** use that as a heuristic to decide how to do the 34467545Seric ** encoding. 34567545Seric */ 34667545Seric 34767545Seric sectionsize = sectionhighbits = 0; 34868517Seric if (!bitset(M87F_NO8BIT, flags)) 34967545Seric { 35068515Seric /* remember where we were */ 35168515Seric offset = ftell(e->e_dfp); 35268515Seric if (offset == -1) 35368564Seric syserr("mime8to7: cannot ftell on df%s", e->e_id); 35468515Seric 35568515Seric /* do a scan of this body type to count character types */ 35668515Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 35767545Seric { 35868717Seric if (mimeboundary(buf, boundaries) != MBT_NOTSEP) 35968515Seric break; 36068515Seric for (p = buf; *p != '\0'; p++) 36168515Seric { 36268515Seric /* count bytes with the high bit set */ 36368515Seric sectionsize++; 36468515Seric if (bitset(0200, *p)) 36568515Seric sectionhighbits++; 36668515Seric } 36768515Seric 36868515Seric /* 36968515Seric ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, 37068515Seric ** assume base64. This heuristic avoids double-reading 37168515Seric ** large graphics or video files. 37268515Seric */ 37368515Seric 37468515Seric if (sectionsize >= 4096 && 37568515Seric sectionhighbits > sectionsize / 4) 37668515Seric break; 37767545Seric } 37867547Seric 37968515Seric /* return to the original offset for processing */ 38068515Seric /* XXX use relative seeks to handle >31 bit file sizes? */ 38168515Seric if (fseek(e->e_dfp, offset, SEEK_SET) < 0) 38268564Seric syserr("mime8to7: cannot fseek on df%s", e->e_id); 38368717Seric else 38468717Seric clearerr(e->e_dfp); 38567545Seric } 38667545Seric 38767547Seric /* 38867547Seric ** Heuristically determine encoding method. 38967547Seric ** If more than 1/8 of the total characters have the 39067547Seric ** eighth bit set, use base64; else use quoted-printable. 39168860Seric ** However, only encode binary encoded data as base64, 39268860Seric ** since otherwise the NL=>CRLF mapping will be a problem. 39367547Seric */ 39467547Seric 39567545Seric if (tTd(43, 8)) 39667545Seric { 39768860Seric printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s\n", 39868860Seric sectionhighbits, sectionsize, 39968860Seric cte == NULL ? "[none]" : cte); 40067545Seric } 40168860Seric if (cte != NULL && strcasecmp(cte, "binary") == 0) 40268860Seric sectionsize = sectionhighbits; 40368717Seric linelen = 0; 40468876Seric bp = buf; 40567554Seric if (sectionhighbits == 0) 40667545Seric { 40767554Seric /* no encoding necessary */ 40868847Seric if (cte != NULL) 40967695Seric { 41068847Seric sprintf(buf, "Content-Transfer-Encoding: %s", cte); 41167695Seric putline(buf, mci); 41268717Seric if (tTd(43, 36)) 41368717Seric printf(" ...%s\n", buf); 41467695Seric } 41567554Seric putline("", mci); 41667554Seric mci->mci_flags &= ~MCIF_INHEADER; 41767554Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 41867554Seric { 41968517Seric bt = mimeboundary(buf, boundaries); 42067554Seric if (bt != MBT_NOTSEP) 42167554Seric break; 42267554Seric putline(buf, mci); 42367554Seric } 42468717Seric if (feof(e->e_dfp)) 42568717Seric bt = MBT_FINAL; 42667554Seric } 42767554Seric else if (sectionsize / 8 < sectionhighbits) 42867554Seric { 42967545Seric /* use base64 encoding */ 43067545Seric int c1, c2; 43167545Seric 43267545Seric putline("Content-Transfer-Encoding: base64", mci); 43368717Seric if (tTd(43, 36)) 43468717Seric printf(" ...Content-Transfer-Encoding: base64\n"); 43567545Seric putline("", mci); 43667545Seric mci->mci_flags &= ~MCIF_INHEADER; 43769281Seric while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF) 43867545Seric { 43967545Seric if (linelen > 71) 44067545Seric { 44168876Seric *bp = '\0'; 44268876Seric putline(buf, mci); 44367545Seric linelen = 0; 44468876Seric bp = buf; 44567545Seric } 44667545Seric linelen += 4; 44768876Seric *bp++ = Base64Code[(c1 >> 2)]; 44867545Seric c1 = (c1 & 0x03) << 4; 44969281Seric c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 45067545Seric if (c2 == EOF) 45167545Seric { 45268876Seric *bp++ = Base64Code[c1]; 45368876Seric *bp++ = '='; 45468876Seric *bp++ = '='; 45567545Seric break; 45667545Seric } 45767545Seric c1 |= (c2 >> 4) & 0x0f; 45868876Seric *bp++ = Base64Code[c1]; 45967545Seric c1 = (c2 & 0x0f) << 2; 46069281Seric c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 46167545Seric if (c2 == EOF) 46267545Seric { 46368876Seric *bp++ = Base64Code[c1]; 46468876Seric *bp++ = '='; 46567545Seric break; 46667545Seric } 46767545Seric c1 |= (c2 >> 6) & 0x03; 46868876Seric *bp++ = Base64Code[c1]; 46968876Seric *bp++ = Base64Code[c2 & 0x3f]; 47067545Seric } 47169600Seric *bp = '\0'; 47269600Seric putline(buf, mci); 47367545Seric } 47467545Seric else 47567545Seric { 47667545Seric /* use quoted-printable encoding */ 47767545Seric int c1, c2; 47868515Seric int fromstate; 47968862Seric BITMAP badchars; 48067545Seric 48168862Seric /* set up map of characters that must be mapped */ 48268862Seric clrbitmap(badchars); 48368862Seric for (c1 = 0x00; c1 < 0x20; c1++) 48468862Seric setbitn(c1, badchars); 48568862Seric clrbitn('\t', badchars); 48668862Seric for (c1 = 0x7f; c1 < 0x100; c1++) 48768862Seric setbitn(c1, badchars); 48868862Seric setbitn('=', badchars); 48968862Seric if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) 49068862Seric for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) 49168862Seric setbitn(*p, badchars); 49268862Seric 49367545Seric putline("Content-Transfer-Encoding: quoted-printable", mci); 49468717Seric if (tTd(43, 36)) 49568717Seric printf(" ...Content-Transfer-Encoding: quoted-printable\n"); 49667545Seric putline("", mci); 49767545Seric mci->mci_flags &= ~MCIF_INHEADER; 49868717Seric fromstate = 0; 49967554Seric c2 = '\n'; 50069281Seric while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF) 50167545Seric { 50267545Seric if (c1 == '\n') 50367545Seric { 50467545Seric if (c2 == ' ' || c2 == '\t') 50567545Seric { 50668876Seric *bp++ = '='; 50768876Seric *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 50868876Seric *bp++ = Base16Code[c2 & 0x0f]; 50967545Seric } 51068876Seric *bp = '\0'; 51168876Seric putline(buf, mci); 51268515Seric linelen = fromstate = 0; 51368876Seric bp = buf; 51467545Seric c2 = c1; 51567545Seric continue; 51667545Seric } 51768515Seric if (c2 == ' ' && linelen == 4 && fromstate == 4 && 51868515Seric bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 51967840Seric { 52068876Seric *bp++ = '='; 52168876Seric *bp++ = '2'; 52268876Seric *bp++ = '0'; 52368515Seric linelen += 3; 52468515Seric } 52568515Seric else if (c2 == ' ' || c2 == '\t') 52668515Seric { 52768876Seric *bp++ = c2; 52867840Seric linelen++; 52967840Seric } 53067545Seric if (linelen > 72) 53167545Seric { 53268876Seric *bp++ = '='; 53368876Seric *bp = '\0'; 53468876Seric putline(buf, mci); 53568515Seric linelen = fromstate = 0; 53668876Seric bp = buf; 53767554Seric c2 = '\n'; 53867545Seric } 53968862Seric if (bitnset(c1 & 0xff, badchars)) 54067545Seric { 54168876Seric *bp++ = '='; 54268876Seric *bp++ = Base16Code[(c1 >> 4) & 0x0f]; 54368876Seric *bp++ = Base16Code[c1 & 0x0f]; 54467545Seric linelen += 3; 54567545Seric } 54667840Seric else if (c1 != ' ' && c1 != '\t') 54767545Seric { 54868515Seric if (linelen < 4 && c1 == "From"[linelen]) 54968515Seric fromstate++; 55068876Seric *bp++ = c1; 55167545Seric linelen++; 55267545Seric } 55367545Seric c2 = c1; 55467545Seric } 55567840Seric 55667840Seric /* output any saved character */ 55767840Seric if (c2 == ' ' || c2 == '\t') 55867840Seric { 55968876Seric *bp++ = '='; 56068876Seric *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 56168876Seric *bp++ = Base16Code[c2 & 0x0f]; 56268515Seric linelen += 3; 56367840Seric } 56469600Seric 56569600Seric if (linelen > 0 || boundaries[0] != NULL) 56669600Seric { 56769600Seric *bp = '\0'; 56869600Seric putline(buf, mci); 56969600Seric } 57069600Seric 57167545Seric } 57268717Seric if (tTd(43, 3)) 57368717Seric printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); 57468717Seric return bt; 57567545Seric } 57668515Seric /* 57768515Seric ** MIME_GETCHAR -- get a character for MIME processing 57868515Seric ** 57968515Seric ** Treats boundaries as EOF. 58068515Seric ** 58168515Seric ** Parameters: 58268515Seric ** fp -- the input file. 58368517Seric ** boundaries -- the current MIME boundaries. 58468717Seric ** btp -- if the return value is EOF, *btp is set to 58568717Seric ** the type of the boundary. 58668515Seric ** 58768515Seric ** Returns: 58868515Seric ** The next character in the input stream. 58968515Seric */ 59067545Seric 59167545Seric int 59268717Seric mime_getchar(fp, boundaries, btp) 59367545Seric register FILE *fp; 59468517Seric char **boundaries; 59568717Seric int *btp; 59667545Seric { 59767545Seric int c; 59868864Seric static u_char *bp = NULL; 59967545Seric static int buflen = 0; 60067545Seric static bool atbol = TRUE; /* at beginning of line */ 60168717Seric static int bt = MBT_SYNTAX; /* boundary type of next EOF */ 60268864Seric static u_char buf[128]; /* need not be a full line */ 60367545Seric 60467545Seric if (buflen > 0) 60567545Seric { 60667545Seric buflen--; 60767545Seric return *bp++; 60867545Seric } 60968515Seric bp = buf; 61068515Seric buflen = 0; 61168876Seric c = getc(fp); 61268515Seric if (c == '\n') 61368515Seric { 61468515Seric /* might be part of a MIME boundary */ 61568515Seric *bp++ = c; 61668515Seric atbol = TRUE; 61768876Seric c = getc(fp); 61868876Seric if (c == '\n') 61968876Seric { 62068876Seric ungetc(c, fp); 62168876Seric return c; 62268876Seric } 62368515Seric } 62468515Seric if (c != EOF) 62568515Seric *bp++ = c; 62668717Seric else 62768717Seric bt = MBT_FINAL; 62868517Seric if (atbol && c == '-') 62967545Seric { 63067545Seric /* check for a message boundary */ 63168876Seric c = getc(fp); 63267545Seric if (c != '-') 63367545Seric { 63467545Seric if (c != EOF) 63568515Seric *bp++ = c; 63668717Seric else 63768717Seric bt = MBT_FINAL; 63868515Seric buflen = bp - buf - 1; 63968515Seric bp = buf; 64068515Seric return *bp++; 64167545Seric } 64267545Seric 64367545Seric /* got "--", now check for rest of separator */ 64467545Seric *bp++ = '-'; 64568847Seric while (bp < &buf[sizeof buf - 2] && 64668876Seric (c = getc(fp)) != EOF && c != '\n') 64767545Seric { 64867545Seric *bp++ = c; 64967545Seric } 65067545Seric *bp = '\0'; 65168717Seric bt = mimeboundary(&buf[1], boundaries); 65268717Seric switch (bt) 65367545Seric { 65467545Seric case MBT_FINAL: 65567545Seric case MBT_INTERMED: 65667545Seric /* we have a message boundary */ 65767545Seric buflen = 0; 65868717Seric *btp = bt; 65967545Seric return EOF; 66067545Seric } 66167545Seric 66267545Seric atbol = c == '\n'; 66367545Seric if (c != EOF) 66467545Seric *bp++ = c; 66567545Seric } 66667545Seric 66768515Seric buflen = bp - buf - 1; 66868515Seric if (buflen < 0) 66968717Seric { 67068717Seric *btp = bt; 67168515Seric return EOF; 67268717Seric } 67368515Seric bp = buf; 67468515Seric return *bp++; 67567545Seric } 67667545Seric /* 67768847Seric ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF 67868847Seric ** 67968847Seric ** Parameters: 68068847Seric ** fp -- the input file. 68168847Seric ** boundaries -- the current MIME boundaries. 68268847Seric ** btp -- if the return value is EOF, *btp is set to 68368847Seric ** the type of the boundary. 68468847Seric ** 68568847Seric ** Returns: 68668847Seric ** The next character in the input stream. 68768847Seric */ 68868847Seric 68968847Seric int 69068847Seric mime_getchar_crlf(fp, boundaries, btp) 69168847Seric register FILE *fp; 69268847Seric char **boundaries; 69368847Seric int *btp; 69468847Seric { 69568847Seric static bool sendlf = FALSE; 69668847Seric int c; 69768847Seric 69868847Seric if (sendlf) 69968847Seric { 70068847Seric sendlf = FALSE; 70168847Seric return '\n'; 70268847Seric } 70368847Seric c = mime_getchar(fp, boundaries, btp); 70468847Seric if (c == '\n') 70568847Seric { 70668847Seric sendlf = TRUE; 70768847Seric return '\r'; 70868847Seric } 70968847Seric return c; 71068847Seric } 71168847Seric /* 71267545Seric ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type 71367545Seric ** 71467545Seric ** Parameters: 71567545Seric ** line -- the input line. 71668517Seric ** boundaries -- the set of currently pending boundaries. 71767545Seric ** 71867545Seric ** Returns: 71967545Seric ** MBT_NOTSEP -- if this is not a separator line 72067545Seric ** MBT_INTERMED -- if this is an intermediate separator 72167545Seric ** MBT_FINAL -- if this is a final boundary 72267545Seric ** MBT_SYNTAX -- if this is a boundary for the wrong 72367545Seric ** enclosure -- i.e., a syntax error. 72467545Seric */ 72567545Seric 72667545Seric int 72768517Seric mimeboundary(line, boundaries) 72867545Seric register char *line; 72968517Seric char **boundaries; 73067545Seric { 73169474Seric int type = MBT_NOTSEP; 73267545Seric int i; 73368517Seric int savec; 73467545Seric 73568517Seric if (line[0] != '-' || line[1] != '-' || boundaries == NULL) 73667545Seric return MBT_NOTSEP; 73767545Seric i = strlen(line); 73867545Seric if (line[i - 1] == '\n') 73967545Seric i--; 74069474Seric 74169474Seric /* strip off trailing whitespace */ 74268515Seric while (line[i - 1] == ' ' || line[i - 1] == '\t') 74368515Seric i--; 74469474Seric savec = line[i]; 74569474Seric line[i] = '\0'; 74669474Seric 74769474Seric if (tTd(43, 5)) 74869474Seric printf("mimeboundary: line=\"%s\"... ", line); 74969474Seric 75069474Seric /* check for this as an intermediate boundary */ 75169474Seric if (isboundary(&line[2], boundaries) >= 0) 75269474Seric type = MBT_INTERMED; 75369474Seric else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) 75467545Seric { 75569474Seric /* check for a final boundary */ 75669474Seric line[i - 2] = '\0'; 75769474Seric if (isboundary(&line[2], boundaries) >= 0) 75869474Seric type = MBT_FINAL; 75969474Seric line[i - 2] = '-'; 76067545Seric } 76167545Seric 76268517Seric line[i] = savec; 76367545Seric if (tTd(43, 5)) 76468717Seric printf("%s\n", MimeBoundaryNames[type]); 76567545Seric return type; 76667545Seric } 76767896Seric /* 76867896Seric ** DEFCHARSET -- return default character set for message 76967896Seric ** 77067896Seric ** The first choice for character set is for the mailer 77167896Seric ** corresponding to the envelope sender. If neither that 77267896Seric ** nor the global configuration file has a default character 77367896Seric ** set defined, return "unknown-8bit" as recommended by 77467896Seric ** RFC 1428 section 3. 77567896Seric ** 77667896Seric ** Parameters: 77767896Seric ** e -- the envelope for this message. 77867896Seric ** 77967896Seric ** Returns: 78067896Seric ** The default character set for that mailer. 78167896Seric */ 78267896Seric 78367896Seric char * 78467896Seric defcharset(e) 78567896Seric register ENVELOPE *e; 78667896Seric { 78767896Seric if (e != NULL && e->e_from.q_mailer != NULL && 78867896Seric e->e_from.q_mailer->m_defcharset != NULL) 78967896Seric return e->e_from.q_mailer->m_defcharset; 79067896Seric if (DefaultCharSet != NULL) 79167896Seric return DefaultCharSet; 79267896Seric return "unknown-8bit"; 79367896Seric } 79468517Seric /* 79568517Seric ** ISBOUNDARY -- is a given string a currently valid boundary? 79668517Seric ** 79768517Seric ** Parameters: 79868517Seric ** line -- the current input line. 79968517Seric ** boundaries -- the list of valid boundaries. 80068517Seric ** 80168517Seric ** Returns: 80268517Seric ** The index number in boundaries if the line is found. 80368517Seric ** -1 -- otherwise. 80468517Seric ** 80568517Seric */ 80668517Seric 80768517Seric int 80868517Seric isboundary(line, boundaries) 80968517Seric char *line; 81068517Seric char **boundaries; 81168517Seric { 81268517Seric register int i; 81368517Seric 81468711Seric for (i = 0; boundaries[i] != NULL; i++) 81568517Seric { 81668517Seric if (strcmp(line, boundaries[i]) == 0) 81768517Seric return i; 81868517Seric } 81968517Seric return -1; 82068517Seric } 82169480Seric 82269480Seric #endif /* MIME */ 823