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*68862Seric static char sccsid[] = "@(#)mime.c 8.17 (Berkeley) 04/23/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. 6468517Seric ** boundaries -- the currently pending message boundaries. 6568517Seric ** 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 8268517Seric 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; 10068517Seric struct args argv[MAXMIMEARGS]; 10167545Seric char bbuf[128]; 10267545Seric char buf[MAXLINE]; 10368517Seric char pvpbuf[MAXLINE]; 10468711Seric extern char MimeTokenTab[256]; 10567545Seric 10667545Seric if (tTd(43, 1)) 10767545Seric { 10868717Seric printf("mime8to7: flags = %x, boundaries =", flags); 10968717Seric if (boundaries[0] == NULL) 11068717Seric printf(" <none>"); 11168717Seric else 11268717Seric { 11368717Seric for (i = 0; boundaries[i] != NULL; i++) 11468717Seric printf(" %s", boundaries[i]); 11568717Seric } 11668717Seric printf("\n"); 11767545Seric } 11868847Seric type = subtype = NULL; 11967545Seric p = hvalue("Content-Type", header); 12068847Seric if (p == NULL) 12168847Seric { 12268847Seric if (bitset(M87F_DIGEST, flags)) 12368847Seric p = "message/rfc822"; 12468847Seric else 12568847Seric p = "text/plain"; 12668847Seric } 12768517Seric if (p != NULL && 12868711Seric (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, 12968711Seric MimeTokenTab)) != NULL && 13068517Seric pvp[0] != NULL) 13167545Seric { 13268717Seric if (tTd(43, 40)) 13368717Seric { 13468717Seric for (i = 0; pvp[i] != NULL; i++) 13568717Seric printf("pvp[%d] = \"%s\"\n", i, pvp[i]); 13668717Seric } 13768517Seric type = *pvp++; 13868517Seric if (*pvp != NULL && strcmp(*pvp, "/") == 0 && 13968517Seric *++pvp != NULL) 14068517Seric { 14168517Seric subtype = *pvp++; 14268517Seric } 14368517Seric 14468517Seric /* break out parameters */ 14568517Seric while (*pvp != NULL && argc < MAXMIMEARGS) 14668517Seric { 14768517Seric /* skip to semicolon separator */ 14868517Seric while (*pvp != NULL && strcmp(*pvp, ";") != 0) 14968517Seric pvp++; 15068517Seric if (*pvp++ == NULL || *pvp == NULL) 15168517Seric break; 15268517Seric 15368517Seric /* extract field name */ 15468517Seric argv[argc].field = *pvp++; 15568517Seric 15668517Seric /* see if there is a value */ 15768517Seric if (*pvp != NULL && strcmp(*pvp, "=") == 0 && 15868517Seric (*++pvp == NULL || strcmp(*pvp, ";") != 0)) 15968517Seric { 16068517Seric argv[argc].value = *pvp; 16168517Seric argc++; 16268517Seric } 16368517Seric } 16468517Seric } 16568717Seric 16668847Seric /* check for disaster cases */ 16768847Seric if (type == NULL) 16868847Seric type = "-none-"; 16968847Seric if (subtype == NULL) 17068847Seric subtype = "-none-"; 17168847Seric 17268847Seric /* 17368847Seric ** Check for cases that can not be encoded. 17468847Seric ** 17568847Seric ** For example, you can't encode certain kinds of types 17668847Seric ** or already-encoded messages. If we find this case, 17768847Seric ** just copy it through. 17868847Seric */ 17968847Seric 18068847Seric cte = hvalue("content-transfer-encoding", header); 18168717Seric sprintf(buf, "%s/%s", type, subtype); 18268847Seric if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) 18368717Seric flags |= M87F_NO8BIT; 18468717Seric 18568717Seric /* 18668717Seric ** Multipart requires special processing. 18768717Seric ** 18868717Seric ** Do a recursive descent into the message. 18968717Seric */ 19068717Seric 19168517Seric if (strcasecmp(type, "multipart") == 0) 19268517Seric { 19367545Seric register char *q; 19467545Seric 19568847Seric if (strcasecmp(subtype, "digest") == 0) 19668847Seric flags |= M87F_DIGEST; 19768847Seric 19868517Seric for (i = 0; i < argc; i++) 19967545Seric { 20068517Seric if (strcasecmp(argv[i].field, "boundary") == 0) 20168517Seric break; 20268517Seric } 20368517Seric if (i >= argc) 20468517Seric { 20567545Seric syserr("mime8to7: Content-Type: %s missing boundary", p); 20667545Seric p = "---"; 20767545Seric } 20867545Seric else 20968517Seric p = argv[i].value; 21067545Seric if (*p == '"') 21168711Seric q = strchr(++p, '"'); 21267545Seric else 21367545Seric q = p + strlen(p); 21467545Seric if (q - p > sizeof bbuf - 1) 21567545Seric { 21667545Seric syserr("mime8to7: multipart boundary \"%.*s\" too long", 21767545Seric q - p, p); 21867545Seric q = p + sizeof bbuf - 1; 21967545Seric } 22067545Seric strncpy(bbuf, p, q - p); 22167545Seric bbuf[q - p] = '\0'; 22267545Seric if (tTd(43, 1)) 22367545Seric printf("mime8to7: multipart boundary \"%s\"\n", bbuf); 22468517Seric for (i = 0; i < MAXMIMENESTING; i++) 22568517Seric if (boundaries[i] == NULL) 22668517Seric break; 22768517Seric if (i >= MAXMIMENESTING) 22868517Seric syserr("mime8to7: multipart nesting boundary too deep"); 22968517Seric else 23068517Seric { 23168517Seric boundaries[i] = bbuf; 23268517Seric boundaries[i + 1] = NULL; 23368517Seric } 23467545Seric 23567545Seric /* skip the early "comment" prologue */ 23668717Seric putline("", mci); 23767545Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 23867545Seric { 23968517Seric bt = mimeboundary(buf, boundaries); 24067545Seric if (bt != MBT_NOTSEP) 24167545Seric break; 24268847Seric putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); 24368717Seric if (tTd(43, 99)) 24468717Seric printf(" ...%s", buf); 24567545Seric } 24668717Seric if (feof(e->e_dfp)) 24768717Seric bt = MBT_FINAL; 24867545Seric while (bt != MBT_FINAL) 24967545Seric { 25067545Seric auto HDR *hdr = NULL; 25167545Seric 25267545Seric sprintf(buf, "--%s", bbuf); 25367545Seric putline(buf, mci); 25468717Seric if (tTd(43, 35)) 25568717Seric printf(" ...%s\n", buf); 25667545Seric collect(e->e_dfp, FALSE, FALSE, &hdr, e); 25768717Seric if (tTd(43, 101)) 25868717Seric putline("+++after collect", mci); 25967936Seric putheader(mci, hdr, e, 0); 26068717Seric if (tTd(43, 101)) 26168717Seric putline("+++after putheader", mci); 26268517Seric bt = mime8to7(mci, hdr, e, boundaries, flags); 26367545Seric } 26467545Seric sprintf(buf, "--%s--", bbuf); 26567545Seric putline(buf, mci); 26668717Seric if (tTd(43, 35)) 26768717Seric printf(" ...%s\n", buf); 26868717Seric boundaries[i] = NULL; 26967545Seric 27067545Seric /* skip the late "comment" epilogue */ 27167545Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 27267545Seric { 27368517Seric bt = mimeboundary(buf, boundaries); 27467545Seric if (bt != MBT_NOTSEP) 27567545Seric break; 27668847Seric putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); 27768717Seric if (tTd(43, 99)) 27868717Seric printf(" ...%s", buf); 27967545Seric } 28068717Seric if (feof(e->e_dfp)) 28168717Seric bt = MBT_FINAL; 28268717Seric if (tTd(43, 3)) 28368717Seric printf("\t\t\tmime8to7=>%s (multipart)\n", 28468717Seric MimeBoundaryNames[bt]); 28567545Seric return bt; 28667545Seric } 28767545Seric 28867545Seric /* 28968847Seric ** Message/* types -- recurse exactly once. 29068847Seric */ 29168847Seric 29268847Seric if (strcasecmp(type, "message") == 0) 29368847Seric { 29468847Seric register char *q; 29568847Seric auto HDR *hdr = NULL; 29668847Seric 29768847Seric putline("", mci); 29868847Seric 29968847Seric collect(e->e_dfp, FALSE, FALSE, &hdr, e); 30068847Seric if (tTd(43, 101)) 30168847Seric putline("+++after collect", mci); 30268847Seric putheader(mci, hdr, e, 0); 30368847Seric if (tTd(43, 101)) 30468847Seric putline("+++after putheader", mci); 30568847Seric bt = mime8to7(mci, hdr, e, boundaries, flags); 30668847Seric return bt; 30768847Seric } 30868847Seric 30968847Seric /* 31067545Seric ** Non-compound body type 31167545Seric ** 31267545Seric ** Compute the ratio of seven to eight bit characters; 31367545Seric ** use that as a heuristic to decide how to do the 31467545Seric ** encoding. 31567545Seric */ 31667545Seric 31767545Seric sectionsize = sectionhighbits = 0; 31868517Seric if (!bitset(M87F_NO8BIT, flags)) 31967545Seric { 32068515Seric /* remember where we were */ 32168515Seric offset = ftell(e->e_dfp); 32268515Seric if (offset == -1) 32368564Seric syserr("mime8to7: cannot ftell on df%s", e->e_id); 32468515Seric 32568515Seric /* do a scan of this body type to count character types */ 32668515Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 32767545Seric { 32868717Seric if (mimeboundary(buf, boundaries) != MBT_NOTSEP) 32968515Seric break; 33068515Seric for (p = buf; *p != '\0'; p++) 33168515Seric { 33268515Seric /* count bytes with the high bit set */ 33368515Seric sectionsize++; 33468515Seric if (bitset(0200, *p)) 33568515Seric sectionhighbits++; 33668515Seric } 33768515Seric 33868515Seric /* 33968515Seric ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, 34068515Seric ** assume base64. This heuristic avoids double-reading 34168515Seric ** large graphics or video files. 34268515Seric */ 34368515Seric 34468515Seric if (sectionsize >= 4096 && 34568515Seric sectionhighbits > sectionsize / 4) 34668515Seric break; 34767545Seric } 34867547Seric 34968515Seric /* return to the original offset for processing */ 35068515Seric /* XXX use relative seeks to handle >31 bit file sizes? */ 35168515Seric if (fseek(e->e_dfp, offset, SEEK_SET) < 0) 35268564Seric syserr("mime8to7: cannot fseek on df%s", e->e_id); 35368717Seric else 35468717Seric clearerr(e->e_dfp); 35567545Seric } 35667545Seric 35767547Seric /* 35867547Seric ** Heuristically determine encoding method. 35967547Seric ** If more than 1/8 of the total characters have the 36067547Seric ** eighth bit set, use base64; else use quoted-printable. 36168860Seric ** However, only encode binary encoded data as base64, 36268860Seric ** since otherwise the NL=>CRLF mapping will be a problem. 36367547Seric */ 36467547Seric 36567545Seric if (tTd(43, 8)) 36667545Seric { 36768860Seric printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s\n", 36868860Seric sectionhighbits, sectionsize, 36968860Seric cte == NULL ? "[none]" : cte); 37067545Seric } 37168860Seric if (cte != NULL && strcasecmp(cte, "binary") == 0) 37268860Seric sectionsize = sectionhighbits; 37368717Seric linelen = 0; 37467554Seric if (sectionhighbits == 0) 37567545Seric { 37667554Seric /* no encoding necessary */ 37768847Seric if (cte != NULL) 37867695Seric { 37968847Seric sprintf(buf, "Content-Transfer-Encoding: %s", cte); 38067695Seric putline(buf, mci); 38168717Seric if (tTd(43, 36)) 38268717Seric printf(" ...%s\n", buf); 38367695Seric } 38467554Seric putline("", mci); 38567554Seric mci->mci_flags &= ~MCIF_INHEADER; 38667554Seric while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 38767554Seric { 38868517Seric bt = mimeboundary(buf, boundaries); 38967554Seric if (bt != MBT_NOTSEP) 39067554Seric break; 39167554Seric putline(buf, mci); 39267554Seric } 39368717Seric if (feof(e->e_dfp)) 39468717Seric bt = MBT_FINAL; 39567554Seric } 39667554Seric else if (sectionsize / 8 < sectionhighbits) 39767554Seric { 39867545Seric /* use base64 encoding */ 39967545Seric int c1, c2; 40068847Seric int (*getcharf) __P((FILE *, char **, int *)); 40168847Seric extern int mime_getchar __P((FILE *, char **, int *)); 40268847Seric extern int mime_getchar_crlf __P((FILE *, char **, int *)); 40367545Seric 40468860Seric if (cte != NULL && strcasecmp(cte, "binary") == 0) 40568860Seric getcharf = mime_getchar; 40668860Seric else 40768847Seric getcharf = mime_getchar_crlf; 40867545Seric putline("Content-Transfer-Encoding: base64", mci); 40968717Seric if (tTd(43, 36)) 41068717Seric printf(" ...Content-Transfer-Encoding: base64\n"); 41167545Seric putline("", mci); 41267545Seric mci->mci_flags &= ~MCIF_INHEADER; 41368847Seric while ((c1 = (*getcharf)(e->e_dfp, boundaries, &bt)) != EOF) 41467545Seric { 41567545Seric if (linelen > 71) 41667545Seric { 41767545Seric fputs(mci->mci_mailer->m_eol, mci->mci_out); 41867545Seric linelen = 0; 41967545Seric } 42067545Seric linelen += 4; 42168847Seric fputc(Base64Code[(c1 >> 2) & 0x3f], mci->mci_out); 42267545Seric c1 = (c1 & 0x03) << 4; 42368847Seric c2 = (*getcharf)(e->e_dfp, boundaries, &bt); 42467545Seric if (c2 == EOF) 42567545Seric { 42668847Seric fputc(Base64Code[c1 & 0x3f], mci->mci_out); 42767545Seric fputc('=', mci->mci_out); 42867545Seric fputc('=', mci->mci_out); 42967545Seric break; 43067545Seric } 43167545Seric c1 |= (c2 >> 4) & 0x0f; 43268847Seric fputc(Base64Code[c1 & 0x3f], mci->mci_out); 43367545Seric c1 = (c2 & 0x0f) << 2; 43468847Seric c2 = (*getcharf)(e->e_dfp, boundaries, &bt); 43567545Seric if (c2 == EOF) 43667545Seric { 43768847Seric fputc(Base64Code[c1 & 0x3f], mci->mci_out); 43867545Seric fputc('=', mci->mci_out); 43967545Seric break; 44067545Seric } 44167545Seric c1 |= (c2 >> 6) & 0x03; 44268847Seric fputc(Base64Code[c1 & 0x3f], mci->mci_out); 44367545Seric fputc(Base64Code[c2 & 0x3f], mci->mci_out); 44467545Seric } 44567545Seric } 44667545Seric else 44767545Seric { 44867545Seric /* use quoted-printable encoding */ 44967545Seric int c1, c2; 45068515Seric int fromstate; 451*68862Seric BITMAP badchars; 45267545Seric 453*68862Seric /* set up map of characters that must be mapped */ 454*68862Seric clrbitmap(badchars); 455*68862Seric for (c1 = 0x00; c1 < 0x20; c1++) 456*68862Seric setbitn(c1, badchars); 457*68862Seric clrbitn('\t', badchars); 458*68862Seric for (c1 = 0x7f; c1 < 0x100; c1++) 459*68862Seric setbitn(c1, badchars); 460*68862Seric setbitn('=', badchars); 461*68862Seric if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) 462*68862Seric for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) 463*68862Seric setbitn(*p, badchars); 464*68862Seric 46567545Seric putline("Content-Transfer-Encoding: quoted-printable", mci); 46668717Seric if (tTd(43, 36)) 46768717Seric printf(" ...Content-Transfer-Encoding: quoted-printable\n"); 46867545Seric putline("", mci); 46967545Seric mci->mci_flags &= ~MCIF_INHEADER; 47068717Seric fromstate = 0; 47167554Seric c2 = '\n'; 47268717Seric while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF) 47367545Seric { 47467545Seric if (c1 == '\n') 47567545Seric { 47667545Seric if (c2 == ' ' || c2 == '\t') 47767545Seric { 47867545Seric fputc('=', mci->mci_out); 47967840Seric fputc(Base16Code[(c2 >> 4) & 0x0f], 48067840Seric mci->mci_out); 48167840Seric fputc(Base16Code[c2 & 0x0f], 48267840Seric mci->mci_out); 48367840Seric fputs(mci->mci_mailer->m_eol, 48467840Seric mci->mci_out); 48567545Seric } 48667545Seric fputs(mci->mci_mailer->m_eol, mci->mci_out); 48768515Seric linelen = fromstate = 0; 48867545Seric c2 = c1; 48967545Seric continue; 49067545Seric } 49168515Seric if (c2 == ' ' && linelen == 4 && fromstate == 4 && 49268515Seric bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 49367840Seric { 49468515Seric fputs("=20", mci->mci_out); 49568515Seric linelen += 3; 49668515Seric } 49768515Seric else if (c2 == ' ' || c2 == '\t') 49868515Seric { 49967840Seric fputc(c2, mci->mci_out); 50067840Seric linelen++; 50167840Seric } 50267545Seric if (linelen > 72) 50367545Seric { 50467545Seric fputc('=', mci->mci_out); 50567545Seric fputs(mci->mci_mailer->m_eol, mci->mci_out); 50668515Seric linelen = fromstate = 0; 50767554Seric c2 = '\n'; 50867545Seric } 50967761Seric if (c2 == '\n' && c1 == '.' && 51067761Seric bitnset(M_XDOT, mci->mci_mailer->m_flags)) 51167761Seric { 51267761Seric fputc('.', mci->mci_out); 51367761Seric linelen++; 51467761Seric } 515*68862Seric if (bitnset(c1 & 0xff, badchars)) 51667545Seric { 51767545Seric fputc('=', mci->mci_out); 51867545Seric fputc(Base16Code[(c1 >> 4) & 0x0f], mci->mci_out); 51967545Seric fputc(Base16Code[c1 & 0x0f], mci->mci_out); 52067545Seric linelen += 3; 52167545Seric } 52267840Seric else if (c1 != ' ' && c1 != '\t') 52367545Seric { 52468515Seric if (linelen < 4 && c1 == "From"[linelen]) 52568515Seric fromstate++; 52667545Seric fputc(c1, mci->mci_out); 52767545Seric linelen++; 52867545Seric } 52967545Seric c2 = c1; 53067545Seric } 53167840Seric 53267840Seric /* output any saved character */ 53367840Seric if (c2 == ' ' || c2 == '\t') 53467840Seric { 53568515Seric fputc('=', mci->mci_out); 53668515Seric fputc(Base16Code[(c2 >> 4) & 0x0f], mci->mci_out); 53768515Seric fputc(Base16Code[c2 & 0x0f], mci->mci_out); 53868515Seric linelen += 3; 53967840Seric } 54067545Seric } 54167545Seric if (linelen > 0) 54267545Seric fputs(mci->mci_mailer->m_eol, mci->mci_out); 54368717Seric if (tTd(43, 3)) 54468717Seric printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); 54568717Seric return bt; 54667545Seric } 54768515Seric /* 54868515Seric ** MIME_GETCHAR -- get a character for MIME processing 54968515Seric ** 55068515Seric ** Treats boundaries as EOF. 55168515Seric ** 55268515Seric ** Parameters: 55368515Seric ** fp -- the input file. 55468517Seric ** boundaries -- the current MIME boundaries. 55568717Seric ** btp -- if the return value is EOF, *btp is set to 55668717Seric ** the type of the boundary. 55768515Seric ** 55868515Seric ** Returns: 55968515Seric ** The next character in the input stream. 56068515Seric */ 56167545Seric 56267545Seric int 56368717Seric mime_getchar(fp, boundaries, btp) 56467545Seric register FILE *fp; 56568517Seric char **boundaries; 56668717Seric int *btp; 56767545Seric { 56867545Seric int c; 56967545Seric static char *bp = NULL; 57067545Seric static int buflen = 0; 57167545Seric static bool atbol = TRUE; /* at beginning of line */ 57268717Seric static int bt = MBT_SYNTAX; /* boundary type of next EOF */ 57367545Seric static char buf[128]; /* need not be a full line */ 57467545Seric 57567545Seric if (buflen > 0) 57667545Seric { 57767545Seric buflen--; 57867545Seric return *bp++; 57967545Seric } 58068515Seric bp = buf; 58168515Seric buflen = 0; 58267545Seric c = fgetc(fp); 58368515Seric if (c == '\n') 58468515Seric { 58568515Seric /* might be part of a MIME boundary */ 58668515Seric *bp++ = c; 58768515Seric atbol = TRUE; 58868515Seric c = fgetc(fp); 58968515Seric } 59068515Seric if (c != EOF) 59168515Seric *bp++ = c; 59268717Seric else 59368717Seric bt = MBT_FINAL; 59468517Seric if (atbol && c == '-') 59567545Seric { 59667545Seric /* check for a message boundary */ 59767545Seric c = fgetc(fp); 59867545Seric if (c != '-') 59967545Seric { 60067545Seric if (c != EOF) 60168515Seric *bp++ = c; 60268717Seric else 60368717Seric bt = MBT_FINAL; 60468515Seric buflen = bp - buf - 1; 60568515Seric bp = buf; 60668515Seric return *bp++; 60767545Seric } 60867545Seric 60967545Seric /* got "--", now check for rest of separator */ 61067545Seric *bp++ = '-'; 61168847Seric while (bp < &buf[sizeof buf - 2] && 61267545Seric (c = fgetc(fp)) != EOF && c != '\n') 61367545Seric { 61467545Seric *bp++ = c; 61567545Seric } 61667545Seric *bp = '\0'; 61768717Seric bt = mimeboundary(&buf[1], boundaries); 61868717Seric switch (bt) 61967545Seric { 62067545Seric case MBT_FINAL: 62167545Seric case MBT_INTERMED: 62267545Seric /* we have a message boundary */ 62367545Seric buflen = 0; 62468717Seric *btp = bt; 62567545Seric return EOF; 62667545Seric } 62767545Seric 62867545Seric atbol = c == '\n'; 62967545Seric if (c != EOF) 63067545Seric *bp++ = c; 63167545Seric } 63267545Seric 63368515Seric buflen = bp - buf - 1; 63468515Seric if (buflen < 0) 63568717Seric { 63668717Seric *btp = bt; 63768515Seric return EOF; 63868717Seric } 63968515Seric bp = buf; 64068515Seric return *bp++; 64167545Seric } 64267545Seric /* 64368847Seric ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF 64468847Seric ** 64568847Seric ** Parameters: 64668847Seric ** fp -- the input file. 64768847Seric ** boundaries -- the current MIME boundaries. 64868847Seric ** btp -- if the return value is EOF, *btp is set to 64968847Seric ** the type of the boundary. 65068847Seric ** 65168847Seric ** Returns: 65268847Seric ** The next character in the input stream. 65368847Seric */ 65468847Seric 65568847Seric int 65668847Seric mime_getchar_crlf(fp, boundaries, btp) 65768847Seric register FILE *fp; 65868847Seric char **boundaries; 65968847Seric int *btp; 66068847Seric { 66168847Seric static bool sendlf = FALSE; 66268847Seric int c; 66368847Seric 66468847Seric if (sendlf) 66568847Seric { 66668847Seric sendlf = FALSE; 66768847Seric return '\n'; 66868847Seric } 66968847Seric c = mime_getchar(fp, boundaries, btp); 67068847Seric if (c == '\n') 67168847Seric { 67268847Seric sendlf = TRUE; 67368847Seric return '\r'; 67468847Seric } 67568847Seric return c; 67668847Seric } 67768847Seric /* 67867545Seric ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type 67967545Seric ** 68067545Seric ** Parameters: 68167545Seric ** line -- the input line. 68268517Seric ** boundaries -- the set of currently pending boundaries. 68367545Seric ** 68467545Seric ** Returns: 68567545Seric ** MBT_NOTSEP -- if this is not a separator line 68667545Seric ** MBT_INTERMED -- if this is an intermediate separator 68767545Seric ** MBT_FINAL -- if this is a final boundary 68867545Seric ** MBT_SYNTAX -- if this is a boundary for the wrong 68967545Seric ** enclosure -- i.e., a syntax error. 69067545Seric */ 69167545Seric 69267545Seric int 69368517Seric mimeboundary(line, boundaries) 69467545Seric register char *line; 69568517Seric char **boundaries; 69667545Seric { 69767545Seric int type; 69867545Seric int i; 69968517Seric int savec; 70067545Seric 70168517Seric if (line[0] != '-' || line[1] != '-' || boundaries == NULL) 70267545Seric return MBT_NOTSEP; 70367545Seric i = strlen(line); 70467545Seric if (line[i - 1] == '\n') 70567545Seric i--; 70668717Seric if (tTd(43, 5)) 70768717Seric printf("mimeboundary: line=\"%.*s\"... ", i, line); 70868515Seric while (line[i - 1] == ' ' || line[i - 1] == '\t') 70968515Seric i--; 71067545Seric if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) 71167545Seric { 71267545Seric type = MBT_FINAL; 71367545Seric i -= 2; 71467545Seric } 71567545Seric else 71667545Seric type = MBT_INTERMED; 71767545Seric 71868517Seric savec = line[i]; 71968517Seric line[i] = '\0'; 72067545Seric /* XXX should check for improper nesting here */ 72168517Seric if (isboundary(&line[2], boundaries) < 0) 72267545Seric type = MBT_NOTSEP; 72368517Seric line[i] = savec; 72467545Seric if (tTd(43, 5)) 72568717Seric printf("%s\n", MimeBoundaryNames[type]); 72667545Seric return type; 72767545Seric } 72867896Seric /* 72967896Seric ** DEFCHARSET -- return default character set for message 73067896Seric ** 73167896Seric ** The first choice for character set is for the mailer 73267896Seric ** corresponding to the envelope sender. If neither that 73367896Seric ** nor the global configuration file has a default character 73467896Seric ** set defined, return "unknown-8bit" as recommended by 73567896Seric ** RFC 1428 section 3. 73667896Seric ** 73767896Seric ** Parameters: 73867896Seric ** e -- the envelope for this message. 73967896Seric ** 74067896Seric ** Returns: 74167896Seric ** The default character set for that mailer. 74267896Seric */ 74367896Seric 74467896Seric char * 74567896Seric defcharset(e) 74667896Seric register ENVELOPE *e; 74767896Seric { 74867896Seric if (e != NULL && e->e_from.q_mailer != NULL && 74967896Seric e->e_from.q_mailer->m_defcharset != NULL) 75067896Seric return e->e_from.q_mailer->m_defcharset; 75167896Seric if (DefaultCharSet != NULL) 75267896Seric return DefaultCharSet; 75367896Seric return "unknown-8bit"; 75467896Seric } 75568517Seric /* 75668517Seric ** ISBOUNDARY -- is a given string a currently valid boundary? 75768517Seric ** 75868517Seric ** Parameters: 75968517Seric ** line -- the current input line. 76068517Seric ** boundaries -- the list of valid boundaries. 76168517Seric ** 76268517Seric ** Returns: 76368517Seric ** The index number in boundaries if the line is found. 76468517Seric ** -1 -- otherwise. 76568517Seric ** 76668517Seric */ 76768517Seric 76868517Seric int 76968517Seric isboundary(line, boundaries) 77068517Seric char *line; 77168517Seric char **boundaries; 77268517Seric { 77368517Seric register int i; 77468517Seric 77568711Seric for (i = 0; boundaries[i] != NULL; i++) 77668517Seric { 77768517Seric if (strcmp(line, boundaries[i]) == 0) 77868517Seric return i; 77968517Seric } 78068517Seric return -1; 78168517Seric } 782