138032Speter/* 2249729Sgshapiro * Copyright (c) 1998-2003, 2006, 2013 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1994, 1996-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1994 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 1564562Sgshapiro#include <string.h> 1638032Speter 17249729SgshapiroSM_RCSID("@(#)$Id: mime.c,v 8.148 2013/03/12 15:24:53 ca Exp $") 1838032Speter 1938032Speter/* 2038032Speter** MIME support. 2138032Speter** 2238032Speter** I am indebted to John Beck of Hewlett-Packard, who contributed 2338032Speter** his code to me for inclusion. As it turns out, I did not use 2438032Speter** his code since he used a "minimum change" approach that used 2538032Speter** several temp files, and I wanted a "minimum impact" approach 2638032Speter** that would avoid copying. However, looking over his code 2738032Speter** helped me cement my understanding of the problem. 2838032Speter** 2938032Speter** I also looked at, but did not directly use, Nathaniel 3038032Speter** Borenstein's "code.c" module. Again, it functioned as 3138032Speter** a file-to-file translator, which did not fit within my 3238032Speter** design bounds, but it was a useful base for understanding 3338032Speter** the problem. 3438032Speter*/ 3538032Speter 36125820Sgshapiro/* use "old" mime 7 to 8 algorithm by default */ 37125820Sgshapiro#ifndef MIME7TO8_OLD 38125820Sgshapiro# define MIME7TO8_OLD 1 39125820Sgshapiro#endif /* ! MIME7TO8_OLD */ 40125820Sgshapiro 4138032Speter#if MIME8TO7 4290792Sgshapirostatic int isboundary __P((char *, char **)); 4390792Sgshapirostatic int mimeboundary __P((char *, char **)); 4490792Sgshapirostatic int mime_getchar __P((SM_FILE_T *, char **, int *)); 4590792Sgshapirostatic int mime_getchar_crlf __P((SM_FILE_T *, char **, int *)); 4638032Speter 4738032Speter/* character set for hex and base64 encoding */ 4864562Sgshapirostatic char Base16Code[] = "0123456789ABCDEF"; 4964562Sgshapirostatic char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 5038032Speter 5138032Speter/* types of MIME boundaries */ 5264562Sgshapiro# define MBT_SYNTAX 0 /* syntax error */ 5364562Sgshapiro# define MBT_NOTSEP 1 /* not a boundary */ 5464562Sgshapiro# define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */ 5564562Sgshapiro# define MBT_FINAL 3 /* final boundary (trailing -- included) */ 5638032Speter 5738032Speterstatic char *MimeBoundaryNames[] = 5838032Speter{ 5938032Speter "SYNTAX", "NOTSEP", "INTERMED", "FINAL" 6038032Speter}; 6138032Speter 6264562Sgshapirostatic bool MapNLtoCRLF; 6338032Speter 6490792Sgshapiro/* 6538032Speter** MIME8TO7 -- output 8 bit body in 7 bit format 6638032Speter** 6738032Speter** The header has already been output -- this has to do the 6838032Speter** 8 to 7 bit conversion. It would be easy if we didn't have 6938032Speter** to deal with nested formats (multipart/xxx and message/rfc822). 7038032Speter** 7138032Speter** We won't be called if we don't have to do a conversion, and 7238032Speter** appropriate MIME-Version: and Content-Type: fields have been 7338032Speter** output. Any Content-Transfer-Encoding: field has not been 7438032Speter** output, and we can add it here. 7538032Speter** 7638032Speter** Parameters: 7738032Speter** mci -- mailer connection information. 7838032Speter** header -- the header for this body part. 7938032Speter** e -- envelope. 8038032Speter** boundaries -- the currently pending message boundaries. 8138032Speter** NULL if we are processing the outer portion. 8238032Speter** flags -- to tweak processing. 83159609Sgshapiro** level -- recursion level. 8438032Speter** 8538032Speter** Returns: 8638032Speter** An indicator of what terminated the message part: 8738032Speter** MBT_FINAL -- the final boundary 8838032Speter** MBT_INTERMED -- an intermediate boundary 8938032Speter** MBT_NOTSEP -- an end of file 90157001Sgshapiro** SM_IO_EOF -- I/O error occurred 9138032Speter*/ 9238032Speter 9338032Speterstruct args 9438032Speter{ 9564562Sgshapiro char *a_field; /* name of field */ 9664562Sgshapiro char *a_value; /* value of that field */ 9738032Speter}; 9838032Speter 9938032Speterint 100159609Sgshapiromime8to7(mci, header, e, boundaries, flags, level) 10138032Speter register MCI *mci; 10238032Speter HDR *header; 10338032Speter register ENVELOPE *e; 10438032Speter char **boundaries; 10538032Speter int flags; 106159609Sgshapiro int level; 10738032Speter{ 10838032Speter register char *p; 10938032Speter int linelen; 110249729Sgshapiro int blen; 11138032Speter int bt; 11238032Speter off_t offset; 11338032Speter size_t sectionsize, sectionhighbits; 11438032Speter int i; 11538032Speter char *type; 11638032Speter char *subtype; 11738032Speter char *cte; 11838032Speter char **pvp; 11938032Speter int argc = 0; 12038032Speter char *bp; 12190792Sgshapiro bool use_qp = false; 12238032Speter struct args argv[MAXMIMEARGS]; 12338032Speter char bbuf[128]; 12438032Speter char buf[MAXLINE]; 12538032Speter char pvpbuf[MAXLINE]; 12690792Sgshapiro extern unsigned char MimeTokenTab[256]; 12738032Speter 128159609Sgshapiro if (level > MAXMIMENESTING) 129159609Sgshapiro { 130159609Sgshapiro if (!bitset(EF_TOODEEP, e->e_flags)) 131159609Sgshapiro { 132159609Sgshapiro if (tTd(43, 4)) 133159609Sgshapiro sm_dprintf("mime8to7: too deep, level=%d\n", 134159609Sgshapiro level); 135159609Sgshapiro usrerr("mime8to7: recursion level %d exceeded", 136159609Sgshapiro level); 137159609Sgshapiro e->e_flags |= EF_DONT_MIME|EF_TOODEEP; 138159609Sgshapiro } 139159609Sgshapiro } 14038032Speter if (tTd(43, 1)) 14138032Speter { 14290792Sgshapiro sm_dprintf("mime8to7: flags = %x, boundaries =", flags); 14338032Speter if (boundaries[0] == NULL) 14490792Sgshapiro sm_dprintf(" <none>"); 14538032Speter else 14638032Speter { 14738032Speter for (i = 0; boundaries[i] != NULL; i++) 14890792Sgshapiro sm_dprintf(" %s", boundaries[i]); 14938032Speter } 15090792Sgshapiro sm_dprintf("\n"); 15138032Speter } 15290792Sgshapiro MapNLtoCRLF = true; 15338032Speter p = hvalue("Content-Transfer-Encoding", header); 15438032Speter if (p == NULL || 155168515Sgshapiro (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL, 156132943Sgshapiro MimeTokenTab, false)) == NULL || 15738032Speter pvp[0] == NULL) 15838032Speter { 15938032Speter cte = NULL; 16038032Speter } 16138032Speter else 16238032Speter { 163168515Sgshapiro cataddr(pvp, NULL, buf, sizeof(buf), '\0', false); 16490792Sgshapiro cte = sm_rpool_strdup_x(e->e_rpool, buf); 16538032Speter } 16638032Speter 16738032Speter type = subtype = NULL; 16838032Speter p = hvalue("Content-Type", header); 16938032Speter if (p == NULL) 17038032Speter { 17138032Speter if (bitset(M87F_DIGEST, flags)) 17238032Speter p = "message/rfc822"; 17338032Speter else 17438032Speter p = "text/plain"; 17538032Speter } 17638032Speter if (p != NULL && 177168515Sgshapiro (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL, 178132943Sgshapiro MimeTokenTab, false)) != NULL && 17938032Speter pvp[0] != NULL) 18038032Speter { 18138032Speter if (tTd(43, 40)) 18238032Speter { 18338032Speter for (i = 0; pvp[i] != NULL; i++) 18490792Sgshapiro sm_dprintf("pvp[%d] = \"%s\"\n", i, pvp[i]); 18538032Speter } 18638032Speter type = *pvp++; 18738032Speter if (*pvp != NULL && strcmp(*pvp, "/") == 0 && 18838032Speter *++pvp != NULL) 18938032Speter { 19038032Speter subtype = *pvp++; 19138032Speter } 19238032Speter 19338032Speter /* break out parameters */ 19438032Speter while (*pvp != NULL && argc < MAXMIMEARGS) 19538032Speter { 19638032Speter /* skip to semicolon separator */ 19738032Speter while (*pvp != NULL && strcmp(*pvp, ";") != 0) 19838032Speter pvp++; 19938032Speter if (*pvp++ == NULL || *pvp == NULL) 20038032Speter break; 20138032Speter 20264562Sgshapiro /* complain about empty values */ 20364562Sgshapiro if (strcmp(*pvp, ";") == 0) 20464562Sgshapiro { 20564562Sgshapiro usrerr("mime8to7: Empty parameter in Content-Type header"); 20664562Sgshapiro 20764562Sgshapiro /* avoid bounce loops */ 20864562Sgshapiro e->e_flags |= EF_DONT_MIME; 20964562Sgshapiro continue; 21064562Sgshapiro } 21164562Sgshapiro 21238032Speter /* extract field name */ 21364562Sgshapiro argv[argc].a_field = *pvp++; 21438032Speter 21538032Speter /* see if there is a value */ 21638032Speter if (*pvp != NULL && strcmp(*pvp, "=") == 0 && 21738032Speter (*++pvp == NULL || strcmp(*pvp, ";") != 0)) 21838032Speter { 21964562Sgshapiro argv[argc].a_value = *pvp; 22038032Speter argc++; 22138032Speter } 22238032Speter } 22338032Speter } 22438032Speter 22538032Speter /* check for disaster cases */ 22638032Speter if (type == NULL) 22738032Speter type = "-none-"; 22838032Speter if (subtype == NULL) 22938032Speter subtype = "-none-"; 23038032Speter 231159609Sgshapiro /* don't propagate some flags more than one level into the message */ 23238032Speter flags &= ~M87F_DIGEST; 23338032Speter 23438032Speter /* 23538032Speter ** Check for cases that can not be encoded. 23638032Speter ** 23738032Speter ** For example, you can't encode certain kinds of types 23838032Speter ** or already-encoded messages. If we find this case, 23938032Speter ** just copy it through. 24038032Speter */ 24138032Speter 242168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%.100s/%.100s", type, subtype); 24338032Speter if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) 24438032Speter flags |= M87F_NO8BIT; 24538032Speter 24664562Sgshapiro# ifdef USE_B_CLASS 24738032Speter if (wordinclass(buf, 'b') || wordinclass(type, 'b')) 24890792Sgshapiro MapNLtoCRLF = false; 24964562Sgshapiro# endif /* USE_B_CLASS */ 25038032Speter if (wordinclass(buf, 'q') || wordinclass(type, 'q')) 25190792Sgshapiro use_qp = true; 25238032Speter 25338032Speter /* 25438032Speter ** Multipart requires special processing. 25538032Speter ** 25638032Speter ** Do a recursive descent into the message. 25738032Speter */ 25838032Speter 25990792Sgshapiro if (sm_strcasecmp(type, "multipart") == 0 && 260159609Sgshapiro (!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags)) && 261159609Sgshapiro !bitset(EF_TOODEEP, e->e_flags) 262159609Sgshapiro ) 26338032Speter { 26438032Speter 26590792Sgshapiro if (sm_strcasecmp(subtype, "digest") == 0) 26638032Speter flags |= M87F_DIGEST; 26738032Speter 26838032Speter for (i = 0; i < argc; i++) 26938032Speter { 27090792Sgshapiro if (sm_strcasecmp(argv[i].a_field, "boundary") == 0) 27138032Speter break; 27238032Speter } 27364562Sgshapiro if (i >= argc || argv[i].a_value == NULL) 27438032Speter { 27564562Sgshapiro usrerr("mime8to7: Content-Type: \"%s\": %s boundary", 27638032Speter i >= argc ? "missing" : "bogus", p); 27738032Speter p = "---"; 27838032Speter 27938032Speter /* avoid bounce loops */ 28038032Speter e->e_flags |= EF_DONT_MIME; 28138032Speter } 28238032Speter else 28338032Speter { 28464562Sgshapiro p = argv[i].a_value; 28538032Speter stripquotes(p); 28638032Speter } 287168515Sgshapiro if (sm_strlcpy(bbuf, p, sizeof(bbuf)) >= sizeof(bbuf)) 28838032Speter { 28964562Sgshapiro usrerr("mime8to7: multipart boundary \"%s\" too long", 29038032Speter p); 29138032Speter 29238032Speter /* avoid bounce loops */ 29338032Speter e->e_flags |= EF_DONT_MIME; 29438032Speter } 29564562Sgshapiro 29638032Speter if (tTd(43, 1)) 29790792Sgshapiro sm_dprintf("mime8to7: multipart boundary \"%s\"\n", 29890792Sgshapiro bbuf); 29938032Speter for (i = 0; i < MAXMIMENESTING; i++) 30071345Sgshapiro { 30138032Speter if (boundaries[i] == NULL) 30238032Speter break; 30371345Sgshapiro } 30438032Speter if (i >= MAXMIMENESTING) 30538032Speter { 306159609Sgshapiro if (tTd(43, 4)) 307159609Sgshapiro sm_dprintf("mime8to7: too deep, i=%d\n", i); 308159609Sgshapiro if (!bitset(EF_TOODEEP, e->e_flags)) 309159609Sgshapiro usrerr("mime8to7: multipart nesting boundary too deep"); 31038032Speter 31138032Speter /* avoid bounce loops */ 312159609Sgshapiro e->e_flags |= EF_DONT_MIME|EF_TOODEEP; 31338032Speter } 31438032Speter else 31538032Speter { 31638032Speter boundaries[i] = bbuf; 31738032Speter boundaries[i + 1] = NULL; 31838032Speter } 31938032Speter mci->mci_flags |= MCIF_INMIME; 32038032Speter 32138032Speter /* skip the early "comment" prologue */ 322157001Sgshapiro if (!putline("", mci)) 323157001Sgshapiro goto writeerr; 32438032Speter mci->mci_flags &= ~MCIF_INHEADER; 32564562Sgshapiro bt = MBT_FINAL; 326249729Sgshapiro while ((blen = sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, 327249729Sgshapiro sizeof(buf))) >= 0) 32838032Speter { 32938032Speter bt = mimeboundary(buf, boundaries); 33038032Speter if (bt != MBT_NOTSEP) 33138032Speter break; 332249729Sgshapiro if (!putxline(buf, blen, mci, 333157001Sgshapiro PXLF_MAPFROM|PXLF_STRIP8BIT)) 334157001Sgshapiro goto writeerr; 33538032Speter if (tTd(43, 99)) 33690792Sgshapiro sm_dprintf(" ...%s", buf); 33738032Speter } 33890792Sgshapiro if (sm_io_eof(e->e_dfp)) 33938032Speter bt = MBT_FINAL; 34038032Speter while (bt != MBT_FINAL) 34138032Speter { 34238032Speter auto HDR *hdr = NULL; 34338032Speter 344168515Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", bbuf); 345157001Sgshapiro if (!putline(buf, mci)) 346157001Sgshapiro goto writeerr; 34738032Speter if (tTd(43, 35)) 34890792Sgshapiro sm_dprintf(" ...%s\n", buf); 349120256Sgshapiro collect(e->e_dfp, false, &hdr, e, false); 35038032Speter if (tTd(43, 101)) 35138032Speter putline("+++after collect", mci); 352157001Sgshapiro if (!putheader(mci, hdr, e, flags)) 353157001Sgshapiro goto writeerr; 35438032Speter if (tTd(43, 101)) 35538032Speter putline("+++after putheader", mci); 356159609Sgshapiro bt = mime8to7(mci, hdr, e, boundaries, flags, 357159609Sgshapiro level + 1); 358157001Sgshapiro if (bt == SM_IO_EOF) 359157001Sgshapiro goto writeerr; 36038032Speter } 361168515Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 3, "--", bbuf, "--"); 362157001Sgshapiro if (!putline(buf, mci)) 363157001Sgshapiro goto writeerr; 36438032Speter if (tTd(43, 35)) 36590792Sgshapiro sm_dprintf(" ...%s\n", buf); 36638032Speter boundaries[i] = NULL; 36738032Speter mci->mci_flags &= ~MCIF_INMIME; 36838032Speter 36938032Speter /* skip the late "comment" epilogue */ 370249729Sgshapiro while ((blen = sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, 371249729Sgshapiro sizeof(buf))) >= 0) 37238032Speter { 37338032Speter bt = mimeboundary(buf, boundaries); 37438032Speter if (bt != MBT_NOTSEP) 37538032Speter break; 376249729Sgshapiro if (!putxline(buf, blen, mci, 377157001Sgshapiro PXLF_MAPFROM|PXLF_STRIP8BIT)) 378157001Sgshapiro goto writeerr; 37938032Speter if (tTd(43, 99)) 38090792Sgshapiro sm_dprintf(" ...%s", buf); 38138032Speter } 38290792Sgshapiro if (sm_io_eof(e->e_dfp)) 38338032Speter bt = MBT_FINAL; 38438032Speter if (tTd(43, 3)) 38590792Sgshapiro sm_dprintf("\t\t\tmime8to7=>%s (multipart)\n", 38638032Speter MimeBoundaryNames[bt]); 38738032Speter return bt; 38838032Speter } 38938032Speter 39038032Speter /* 39138032Speter ** Message/xxx types -- recurse exactly once. 39238032Speter ** 39338032Speter ** Class 's' is predefined to have "rfc822" only. 39438032Speter */ 39538032Speter 39690792Sgshapiro if (sm_strcasecmp(type, "message") == 0) 39738032Speter { 398159609Sgshapiro if (!wordinclass(subtype, 's') || 399159609Sgshapiro bitset(EF_TOODEEP, e->e_flags)) 40038032Speter { 40138032Speter flags |= M87F_NO8BIT; 40238032Speter } 40338032Speter else 40438032Speter { 40538032Speter auto HDR *hdr = NULL; 40638032Speter 407157001Sgshapiro if (!putline("", mci)) 408157001Sgshapiro goto writeerr; 40938032Speter 41038032Speter mci->mci_flags |= MCIF_INMIME; 411120256Sgshapiro collect(e->e_dfp, false, &hdr, e, false); 41238032Speter if (tTd(43, 101)) 41338032Speter putline("+++after collect", mci); 414157001Sgshapiro if (!putheader(mci, hdr, e, flags)) 415157001Sgshapiro goto writeerr; 41638032Speter if (tTd(43, 101)) 41738032Speter putline("+++after putheader", mci); 41898121Sgshapiro if (hvalue("MIME-Version", hdr) == NULL && 419157001Sgshapiro !bitset(M87F_NO8TO7, flags) && 420157001Sgshapiro !putline("MIME-Version: 1.0", mci)) 421157001Sgshapiro goto writeerr; 422159609Sgshapiro bt = mime8to7(mci, hdr, e, boundaries, flags, 423159609Sgshapiro level + 1); 42438032Speter mci->mci_flags &= ~MCIF_INMIME; 42538032Speter return bt; 42638032Speter } 42738032Speter } 42838032Speter 42938032Speter /* 43038032Speter ** Non-compound body type 43138032Speter ** 43238032Speter ** Compute the ratio of seven to eight bit characters; 43338032Speter ** use that as a heuristic to decide how to do the 43438032Speter ** encoding. 43538032Speter */ 43638032Speter 43738032Speter sectionsize = sectionhighbits = 0; 43842575Speter if (!bitset(M87F_NO8BIT|M87F_NO8TO7, flags)) 43938032Speter { 44038032Speter /* remember where we were */ 44190792Sgshapiro offset = sm_io_tell(e->e_dfp, SM_TIME_DEFAULT); 44238032Speter if (offset == -1) 44390792Sgshapiro syserr("mime8to7: cannot sm_io_tell on %cf%s", 44490792Sgshapiro DATAFL_LETTER, e->e_id); 44538032Speter 44638032Speter /* do a scan of this body type to count character types */ 447249729Sgshapiro while ((blen = sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, 448249729Sgshapiro sizeof(buf))) >= 0) 44938032Speter { 45038032Speter if (mimeboundary(buf, boundaries) != MBT_NOTSEP) 45138032Speter break; 452249729Sgshapiro for (i = 0; i < blen; i++) 45338032Speter { 45438032Speter /* count bytes with the high bit set */ 45538032Speter sectionsize++; 456249729Sgshapiro if (bitset(0200, buf[i])) 45738032Speter sectionhighbits++; 45838032Speter } 45938032Speter 46038032Speter /* 46138032Speter ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, 46238032Speter ** assume base64. This heuristic avoids double-reading 46338032Speter ** large graphics or video files. 46438032Speter */ 46538032Speter 46638032Speter if (sectionsize >= 4096 && 46738032Speter sectionhighbits > sectionsize / 4) 46838032Speter break; 46938032Speter } 47038032Speter 47138032Speter /* return to the original offset for processing */ 47238032Speter /* XXX use relative seeks to handle >31 bit file sizes? */ 47390792Sgshapiro if (sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, offset, SEEK_SET) < 0) 47490792Sgshapiro syserr("mime8to7: cannot sm_io_fseek on %cf%s", 47590792Sgshapiro DATAFL_LETTER, e->e_id); 47638032Speter else 47790792Sgshapiro sm_io_clearerr(e->e_dfp); 47838032Speter } 47938032Speter 48038032Speter /* 48138032Speter ** Heuristically determine encoding method. 48238032Speter ** If more than 1/8 of the total characters have the 48338032Speter ** eighth bit set, use base64; else use quoted-printable. 48438032Speter ** However, only encode binary encoded data as base64, 48538032Speter ** since otherwise the NL=>CRLF mapping will be a problem. 48638032Speter */ 48738032Speter 48838032Speter if (tTd(43, 8)) 48938032Speter { 49090792Sgshapiro sm_dprintf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n", 49138032Speter (long) sectionhighbits, (long) sectionsize, 49238032Speter cte == NULL ? "[none]" : cte, 49338032Speter type == NULL ? "[none]" : type, 49438032Speter subtype == NULL ? "[none]" : subtype); 49538032Speter } 49690792Sgshapiro if (cte != NULL && sm_strcasecmp(cte, "binary") == 0) 49738032Speter sectionsize = sectionhighbits; 49838032Speter linelen = 0; 49938032Speter bp = buf; 50038032Speter if (sectionhighbits == 0) 50138032Speter { 50238032Speter /* no encoding necessary */ 50343730Speter if (cte != NULL && 50464562Sgshapiro bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, 50564562Sgshapiro mci->mci_flags) && 50643730Speter !bitset(M87F_NO8TO7, flags)) 50738032Speter { 50842575Speter /* 50943730Speter ** Skip _unless_ in MIME mode and potentially 51043730Speter ** converting from 8 bit to 7 bit MIME. See 51143730Speter ** putheader() for the counterpart where the 51243730Speter ** CTE header is skipped in the opposite 51343730Speter ** situation. 51442575Speter */ 51542575Speter 516168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 51738032Speter "Content-Transfer-Encoding: %.200s", cte); 518157001Sgshapiro if (!putline(buf, mci)) 519157001Sgshapiro goto writeerr; 52038032Speter if (tTd(43, 36)) 52190792Sgshapiro sm_dprintf(" ...%s\n", buf); 52238032Speter } 523157001Sgshapiro if (!putline("", mci)) 524157001Sgshapiro goto writeerr; 52538032Speter mci->mci_flags &= ~MCIF_INHEADER; 526249729Sgshapiro while ((blen = sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, 527249729Sgshapiro sizeof(buf))) >= 0) 52838032Speter { 529173340Sgshapiro if (!bitset(MCIF_INLONGLINE, mci->mci_flags)) 530173340Sgshapiro { 531173340Sgshapiro bt = mimeboundary(buf, boundaries); 532173340Sgshapiro if (bt != MBT_NOTSEP) 533173340Sgshapiro break; 534173340Sgshapiro } 535249729Sgshapiro if (!putxline(buf, blen, mci, 536173340Sgshapiro PXLF_MAPFROM|PXLF_NOADDEOL)) 537157001Sgshapiro goto writeerr; 53838032Speter } 53990792Sgshapiro if (sm_io_eof(e->e_dfp)) 54038032Speter bt = MBT_FINAL; 54138032Speter } 54238032Speter else if (!MapNLtoCRLF || 54338032Speter (sectionsize / 8 < sectionhighbits && !use_qp)) 54438032Speter { 54538032Speter /* use base64 encoding */ 54638032Speter int c1, c2; 54738032Speter 54838032Speter if (tTd(43, 36)) 54990792Sgshapiro sm_dprintf(" ...Content-Transfer-Encoding: base64\n"); 550157001Sgshapiro if (!putline("Content-Transfer-Encoding: base64", mci)) 551157001Sgshapiro goto writeerr; 552168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 55338032Speter "X-MIME-Autoconverted: from 8bit to base64 by %s id %s", 55438032Speter MyHostName, e->e_id); 555157001Sgshapiro if (!putline(buf, mci) || !putline("", mci)) 556157001Sgshapiro goto writeerr; 55738032Speter mci->mci_flags &= ~MCIF_INHEADER; 55890792Sgshapiro while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != 55990792Sgshapiro SM_IO_EOF) 56038032Speter { 56138032Speter if (linelen > 71) 56238032Speter { 56338032Speter *bp = '\0'; 564157001Sgshapiro if (!putline(buf, mci)) 565157001Sgshapiro goto writeerr; 56638032Speter linelen = 0; 56738032Speter bp = buf; 56838032Speter } 56938032Speter linelen += 4; 57038032Speter *bp++ = Base64Code[(c1 >> 2)]; 57138032Speter c1 = (c1 & 0x03) << 4; 57238032Speter c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 57390792Sgshapiro if (c2 == SM_IO_EOF) 57438032Speter { 57538032Speter *bp++ = Base64Code[c1]; 57638032Speter *bp++ = '='; 57738032Speter *bp++ = '='; 57838032Speter break; 57938032Speter } 58038032Speter c1 |= (c2 >> 4) & 0x0f; 58138032Speter *bp++ = Base64Code[c1]; 58238032Speter c1 = (c2 & 0x0f) << 2; 58338032Speter c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 58490792Sgshapiro if (c2 == SM_IO_EOF) 58538032Speter { 58638032Speter *bp++ = Base64Code[c1]; 58738032Speter *bp++ = '='; 58838032Speter break; 58938032Speter } 59038032Speter c1 |= (c2 >> 6) & 0x03; 59138032Speter *bp++ = Base64Code[c1]; 59238032Speter *bp++ = Base64Code[c2 & 0x3f]; 59338032Speter } 59438032Speter *bp = '\0'; 595157001Sgshapiro if (!putline(buf, mci)) 596157001Sgshapiro goto writeerr; 59738032Speter } 59838032Speter else 59938032Speter { 60038032Speter /* use quoted-printable encoding */ 60138032Speter int c1, c2; 60238032Speter int fromstate; 60364562Sgshapiro BITMAP256 badchars; 60438032Speter 60538032Speter /* set up map of characters that must be mapped */ 60638032Speter clrbitmap(badchars); 60738032Speter for (c1 = 0x00; c1 < 0x20; c1++) 60838032Speter setbitn(c1, badchars); 60938032Speter clrbitn('\t', badchars); 61038032Speter for (c1 = 0x7f; c1 < 0x100; c1++) 61138032Speter setbitn(c1, badchars); 61238032Speter setbitn('=', badchars); 61338032Speter if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) 61438032Speter for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) 61538032Speter setbitn(*p, badchars); 61638032Speter 61738032Speter if (tTd(43, 36)) 61890792Sgshapiro sm_dprintf(" ...Content-Transfer-Encoding: quoted-printable\n"); 619157001Sgshapiro if (!putline("Content-Transfer-Encoding: quoted-printable", 620157001Sgshapiro mci)) 621157001Sgshapiro goto writeerr; 622168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 62338032Speter "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s", 62438032Speter MyHostName, e->e_id); 625157001Sgshapiro if (!putline(buf, mci) || !putline("", mci)) 626157001Sgshapiro goto writeerr; 62738032Speter mci->mci_flags &= ~MCIF_INHEADER; 62838032Speter fromstate = 0; 62938032Speter c2 = '\n'; 63090792Sgshapiro while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != 63190792Sgshapiro SM_IO_EOF) 63238032Speter { 63338032Speter if (c1 == '\n') 63438032Speter { 63538032Speter if (c2 == ' ' || c2 == '\t') 63638032Speter { 63738032Speter *bp++ = '='; 63838032Speter *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 63938032Speter *bp++ = Base16Code[c2 & 0x0f]; 64038032Speter } 64138032Speter if (buf[0] == '.' && bp == &buf[1]) 64238032Speter { 64338032Speter buf[0] = '='; 64438032Speter *bp++ = Base16Code[('.' >> 4) & 0x0f]; 64538032Speter *bp++ = Base16Code['.' & 0x0f]; 64638032Speter } 64738032Speter *bp = '\0'; 648157001Sgshapiro if (!putline(buf, mci)) 649157001Sgshapiro goto writeerr; 65038032Speter linelen = fromstate = 0; 65138032Speter bp = buf; 65238032Speter c2 = c1; 65338032Speter continue; 65438032Speter } 65538032Speter if (c2 == ' ' && linelen == 4 && fromstate == 4 && 65638032Speter bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 65738032Speter { 65838032Speter *bp++ = '='; 65938032Speter *bp++ = '2'; 66038032Speter *bp++ = '0'; 66138032Speter linelen += 3; 66238032Speter } 66338032Speter else if (c2 == ' ' || c2 == '\t') 66438032Speter { 66538032Speter *bp++ = c2; 66638032Speter linelen++; 66738032Speter } 66838032Speter if (linelen > 72 && 66938032Speter (linelen > 75 || c1 != '.' || 67038032Speter (linelen > 73 && c2 == '.'))) 67138032Speter { 67238032Speter if (linelen > 73 && c2 == '.') 67338032Speter bp--; 67438032Speter else 67538032Speter c2 = '\n'; 67638032Speter *bp++ = '='; 67738032Speter *bp = '\0'; 678157001Sgshapiro if (!putline(buf, mci)) 679157001Sgshapiro goto writeerr; 68038032Speter linelen = fromstate = 0; 68138032Speter bp = buf; 68238032Speter if (c2 == '.') 68338032Speter { 68438032Speter *bp++ = '.'; 68538032Speter linelen++; 68638032Speter } 68738032Speter } 68871345Sgshapiro if (bitnset(bitidx(c1), badchars)) 68938032Speter { 69038032Speter *bp++ = '='; 69138032Speter *bp++ = Base16Code[(c1 >> 4) & 0x0f]; 69238032Speter *bp++ = Base16Code[c1 & 0x0f]; 69338032Speter linelen += 3; 69438032Speter } 69538032Speter else if (c1 != ' ' && c1 != '\t') 69638032Speter { 69738032Speter if (linelen < 4 && c1 == "From"[linelen]) 69838032Speter fromstate++; 69938032Speter *bp++ = c1; 70038032Speter linelen++; 70138032Speter } 70238032Speter c2 = c1; 70338032Speter } 70438032Speter 70538032Speter /* output any saved character */ 70638032Speter if (c2 == ' ' || c2 == '\t') 70738032Speter { 70838032Speter *bp++ = '='; 70938032Speter *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 71038032Speter *bp++ = Base16Code[c2 & 0x0f]; 71138032Speter linelen += 3; 71238032Speter } 71338032Speter 71438032Speter if (linelen > 0 || boundaries[0] != NULL) 71538032Speter { 71638032Speter *bp = '\0'; 717157001Sgshapiro if (!putline(buf, mci)) 718157001Sgshapiro goto writeerr; 71938032Speter } 72038032Speter 72138032Speter } 72238032Speter if (tTd(43, 3)) 72390792Sgshapiro sm_dprintf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); 72438032Speter return bt; 725157001Sgshapiro 726157001Sgshapiro writeerr: 727157001Sgshapiro return SM_IO_EOF; 72838032Speter} 72990792Sgshapiro/* 73038032Speter** MIME_GETCHAR -- get a character for MIME processing 73138032Speter** 73290792Sgshapiro** Treats boundaries as SM_IO_EOF. 73338032Speter** 73438032Speter** Parameters: 73538032Speter** fp -- the input file. 73638032Speter** boundaries -- the current MIME boundaries. 73790792Sgshapiro** btp -- if the return value is SM_IO_EOF, *btp is set to 73838032Speter** the type of the boundary. 73938032Speter** 74038032Speter** Returns: 74138032Speter** The next character in the input stream. 74238032Speter*/ 74338032Speter 74464562Sgshapirostatic int 74538032Spetermime_getchar(fp, boundaries, btp) 74690792Sgshapiro register SM_FILE_T *fp; 74738032Speter char **boundaries; 74838032Speter int *btp; 74938032Speter{ 75038032Speter int c; 75190792Sgshapiro static unsigned char *bp = NULL; 75238032Speter static int buflen = 0; 75390792Sgshapiro static bool atbol = true; /* at beginning of line */ 75490792Sgshapiro static int bt = MBT_SYNTAX; /* boundary type of next SM_IO_EOF */ 75590792Sgshapiro static unsigned char buf[128]; /* need not be a full line */ 75642575Speter int start = 0; /* indicates position of - in buffer */ 75738032Speter 75842575Speter if (buflen == 1 && *bp == '\n') 75938032Speter { 76042575Speter /* last \n in buffer may be part of next MIME boundary */ 76142575Speter c = *bp; 76242575Speter } 76342575Speter else if (buflen > 0) 76442575Speter { 76538032Speter buflen--; 76638032Speter return *bp++; 76738032Speter } 76864562Sgshapiro else 76990792Sgshapiro c = sm_io_getc(fp, SM_TIME_DEFAULT); 77038032Speter bp = buf; 77138032Speter buflen = 0; 77238032Speter if (c == '\n') 77338032Speter { 77438032Speter /* might be part of a MIME boundary */ 77538032Speter *bp++ = c; 77690792Sgshapiro atbol = true; 77790792Sgshapiro c = sm_io_getc(fp, SM_TIME_DEFAULT); 77838032Speter if (c == '\n') 77938032Speter { 78090792Sgshapiro (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); 78138032Speter return c; 78238032Speter } 78342575Speter start = 1; 78438032Speter } 78590792Sgshapiro if (c != SM_IO_EOF) 78638032Speter *bp++ = c; 78738032Speter else 78838032Speter bt = MBT_FINAL; 78938032Speter if (atbol && c == '-') 79038032Speter { 79138032Speter /* check for a message boundary */ 79290792Sgshapiro c = sm_io_getc(fp, SM_TIME_DEFAULT); 79338032Speter if (c != '-') 79438032Speter { 79590792Sgshapiro if (c != SM_IO_EOF) 79638032Speter *bp++ = c; 79738032Speter else 79838032Speter bt = MBT_FINAL; 79938032Speter buflen = bp - buf - 1; 80038032Speter bp = buf; 80138032Speter return *bp++; 80238032Speter } 80338032Speter 80438032Speter /* got "--", now check for rest of separator */ 80538032Speter *bp++ = '-'; 806168515Sgshapiro while (bp < &buf[sizeof(buf) - 2] && 80790792Sgshapiro (c = sm_io_getc(fp, SM_TIME_DEFAULT)) != SM_IO_EOF && 80890792Sgshapiro c != '\n') 80938032Speter { 81038032Speter *bp++ = c; 81138032Speter } 81290792Sgshapiro *bp = '\0'; /* XXX simply cut off? */ 81342575Speter bt = mimeboundary((char *) &buf[start], boundaries); 81438032Speter switch (bt) 81538032Speter { 81638032Speter case MBT_FINAL: 81738032Speter case MBT_INTERMED: 81838032Speter /* we have a message boundary */ 81938032Speter buflen = 0; 82038032Speter *btp = bt; 82190792Sgshapiro return SM_IO_EOF; 82238032Speter } 82338032Speter 824168515Sgshapiro if (bp < &buf[sizeof(buf) - 2] && c != SM_IO_EOF) 82538032Speter *bp++ = c; 82638032Speter } 82738032Speter 828132943Sgshapiro atbol = c == '\n'; 82938032Speter buflen = bp - buf - 1; 83038032Speter if (buflen < 0) 83138032Speter { 83238032Speter *btp = bt; 83390792Sgshapiro return SM_IO_EOF; 83438032Speter } 83538032Speter bp = buf; 83638032Speter return *bp++; 83738032Speter} 83890792Sgshapiro/* 83938032Speter** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF 84038032Speter** 84138032Speter** Parameters: 84238032Speter** fp -- the input file. 84338032Speter** boundaries -- the current MIME boundaries. 84490792Sgshapiro** btp -- if the return value is SM_IO_EOF, *btp is set to 84538032Speter** the type of the boundary. 84638032Speter** 84738032Speter** Returns: 84838032Speter** The next character in the input stream. 84938032Speter*/ 85038032Speter 85164562Sgshapirostatic int 85238032Spetermime_getchar_crlf(fp, boundaries, btp) 85390792Sgshapiro register SM_FILE_T *fp; 85438032Speter char **boundaries; 85538032Speter int *btp; 85638032Speter{ 85790792Sgshapiro static bool sendlf = false; 85838032Speter int c; 85938032Speter 86038032Speter if (sendlf) 86138032Speter { 86290792Sgshapiro sendlf = false; 86338032Speter return '\n'; 86438032Speter } 86538032Speter c = mime_getchar(fp, boundaries, btp); 86638032Speter if (c == '\n' && MapNLtoCRLF) 86738032Speter { 86890792Sgshapiro sendlf = true; 86938032Speter return '\r'; 87038032Speter } 87138032Speter return c; 87238032Speter} 87390792Sgshapiro/* 87438032Speter** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type 87538032Speter** 87638032Speter** Parameters: 87738032Speter** line -- the input line. 87838032Speter** boundaries -- the set of currently pending boundaries. 87938032Speter** 88038032Speter** Returns: 88138032Speter** MBT_NOTSEP -- if this is not a separator line 88238032Speter** MBT_INTERMED -- if this is an intermediate separator 88338032Speter** MBT_FINAL -- if this is a final boundary 88438032Speter** MBT_SYNTAX -- if this is a boundary for the wrong 88538032Speter** enclosure -- i.e., a syntax error. 88638032Speter*/ 88738032Speter 88864562Sgshapirostatic int 88938032Spetermimeboundary(line, boundaries) 89038032Speter register char *line; 89138032Speter char **boundaries; 89238032Speter{ 89338032Speter int type = MBT_NOTSEP; 89438032Speter int i; 89538032Speter int savec; 89638032Speter 89738032Speter if (line[0] != '-' || line[1] != '-' || boundaries == NULL) 89838032Speter return MBT_NOTSEP; 89938032Speter i = strlen(line); 90071345Sgshapiro if (i > 0 && line[i - 1] == '\n') 90138032Speter i--; 90238032Speter 90338032Speter /* strip off trailing whitespace */ 904141858Sgshapiro while (i > 0 && (line[i - 1] == ' ' || line[i - 1] == '\t' 905141858Sgshapiro#if _FFR_MIME_CR_OK 906141858Sgshapiro || line[i - 1] == '\r' 907141858Sgshapiro#endif /* _FFR_MIME_CR_OK */ 908141858Sgshapiro )) 90938032Speter i--; 91038032Speter savec = line[i]; 91138032Speter line[i] = '\0'; 91238032Speter 91338032Speter if (tTd(43, 5)) 91490792Sgshapiro sm_dprintf("mimeboundary: line=\"%s\"... ", line); 91538032Speter 91638032Speter /* check for this as an intermediate boundary */ 91738032Speter if (isboundary(&line[2], boundaries) >= 0) 91838032Speter type = MBT_INTERMED; 91938032Speter else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) 92038032Speter { 92138032Speter /* check for a final boundary */ 92238032Speter line[i - 2] = '\0'; 92338032Speter if (isboundary(&line[2], boundaries) >= 0) 92438032Speter type = MBT_FINAL; 92538032Speter line[i - 2] = '-'; 92638032Speter } 92738032Speter 92838032Speter line[i] = savec; 92938032Speter if (tTd(43, 5)) 93090792Sgshapiro sm_dprintf("%s\n", MimeBoundaryNames[type]); 93138032Speter return type; 93238032Speter} 93390792Sgshapiro/* 93438032Speter** DEFCHARSET -- return default character set for message 93538032Speter** 93638032Speter** The first choice for character set is for the mailer 93738032Speter** corresponding to the envelope sender. If neither that 93838032Speter** nor the global configuration file has a default character 93938032Speter** set defined, return "unknown-8bit" as recommended by 94038032Speter** RFC 1428 section 3. 94138032Speter** 94238032Speter** Parameters: 94338032Speter** e -- the envelope for this message. 94438032Speter** 94538032Speter** Returns: 94638032Speter** The default character set for that mailer. 94738032Speter*/ 94838032Speter 94938032Speterchar * 95038032Speterdefcharset(e) 95138032Speter register ENVELOPE *e; 95238032Speter{ 95338032Speter if (e != NULL && e->e_from.q_mailer != NULL && 95438032Speter e->e_from.q_mailer->m_defcharset != NULL) 95538032Speter return e->e_from.q_mailer->m_defcharset; 95638032Speter if (DefaultCharSet != NULL) 95738032Speter return DefaultCharSet; 95838032Speter return "unknown-8bit"; 95938032Speter} 96090792Sgshapiro/* 96138032Speter** ISBOUNDARY -- is a given string a currently valid boundary? 96238032Speter** 96338032Speter** Parameters: 96438032Speter** line -- the current input line. 96538032Speter** boundaries -- the list of valid boundaries. 96638032Speter** 96738032Speter** Returns: 96838032Speter** The index number in boundaries if the line is found. 96938032Speter** -1 -- otherwise. 97038032Speter** 97138032Speter*/ 97238032Speter 97364562Sgshapirostatic int 97438032Speterisboundary(line, boundaries) 97538032Speter char *line; 97638032Speter char **boundaries; 97738032Speter{ 97838032Speter register int i; 97938032Speter 98071345Sgshapiro for (i = 0; i <= MAXMIMENESTING && boundaries[i] != NULL; i++) 98138032Speter { 98238032Speter if (strcmp(line, boundaries[i]) == 0) 98338032Speter return i; 98438032Speter } 98538032Speter return -1; 98638032Speter} 98738032Speter#endif /* MIME8TO7 */ 98890792Sgshapiro 98938032Speter#if MIME7TO8 99094334Sgshapirostatic int mime_fromqp __P((unsigned char *, unsigned char **, int)); 99138032Speter 99238032Speter/* 99338032Speter** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format 99438032Speter** 99538032Speter** This is a hack. Supports translating the two 7-bit body-encodings 99638032Speter** (quoted-printable and base64) to 8-bit coded bodies. 99738032Speter** 99838032Speter** There is not much point in supporting multipart here, as the UA 99938032Speter** will be able to deal with encoded MIME bodies if it can parse MIME 100038032Speter** multipart messages. 100138032Speter** 100294334Sgshapiro** Note also that we won't be called unless it is a text/plain MIME 100338032Speter** message, encoded base64 or QP and mailer flag '9' has been defined 100438032Speter** on mailer. 100538032Speter** 100638032Speter** Contributed by Marius Olaffson <marius@rhi.hi.is>. 100738032Speter** 100838032Speter** Parameters: 100938032Speter** mci -- mailer connection information. 101038032Speter** header -- the header for this body part. 101138032Speter** e -- envelope. 101238032Speter** 101338032Speter** Returns: 1014157001Sgshapiro** true iff body was written successfully 101538032Speter*/ 101638032Speter 101738032Speterstatic char index_64[128] = 101838032Speter{ 101938032Speter -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 102038032Speter -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 102138032Speter -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 102238032Speter 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, 102338032Speter -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 102438032Speter 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, 102538032Speter -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 102638032Speter 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 102738032Speter}; 102838032Speter 102964562Sgshapiro# define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) 103038032Speter 1031157001Sgshapirobool 103238032Spetermime7to8(mci, header, e) 103338032Speter register MCI *mci; 103438032Speter HDR *header; 103538032Speter register ENVELOPE *e; 103638032Speter{ 1037249729Sgshapiro int pxflags, blen; 103838032Speter register char *p; 103938032Speter char *cte; 104038032Speter char **pvp; 104190792Sgshapiro unsigned char *fbufp; 104238032Speter char buf[MAXLINE]; 104390792Sgshapiro unsigned char fbuf[MAXLINE + 1]; 104438032Speter char pvpbuf[MAXLINE]; 104590792Sgshapiro extern unsigned char MimeTokenTab[256]; 104638032Speter 104738032Speter p = hvalue("Content-Transfer-Encoding", header); 104838032Speter if (p == NULL || 1049168515Sgshapiro (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL, 1050132943Sgshapiro MimeTokenTab, false)) == NULL || 105138032Speter pvp[0] == NULL) 105238032Speter { 105338032Speter /* "can't happen" -- upper level should have caught this */ 105438032Speter syserr("mime7to8: unparsable CTE %s", p == NULL ? "<NULL>" : p); 105538032Speter 105638032Speter /* avoid bounce loops */ 105738032Speter e->e_flags |= EF_DONT_MIME; 105838032Speter 105938032Speter /* cheap failsafe algorithm -- should work on text/plain */ 106038032Speter if (p != NULL) 106138032Speter { 1062168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 106338032Speter "Content-Transfer-Encoding: %s", p); 1064157001Sgshapiro if (!putline(buf, mci)) 1065157001Sgshapiro goto writeerr; 106638032Speter } 1067157001Sgshapiro if (!putline("", mci)) 1068157001Sgshapiro goto writeerr; 106938032Speter mci->mci_flags &= ~MCIF_INHEADER; 1070249729Sgshapiro while ((blen = sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, 1071249729Sgshapiro sizeof(buf))) >= 0) 1072157001Sgshapiro { 1073249729Sgshapiro if (!putxline(buf, blen, mci, PXLF_MAPFROM)) 1074157001Sgshapiro goto writeerr; 1075157001Sgshapiro } 1076157001Sgshapiro return true; 107738032Speter } 1078168515Sgshapiro cataddr(pvp, NULL, buf, sizeof(buf), '\0', false); 107990792Sgshapiro cte = sm_rpool_strdup_x(e->e_rpool, buf); 108038032Speter 108138032Speter mci->mci_flags |= MCIF_INHEADER; 1082157001Sgshapiro if (!putline("Content-Transfer-Encoding: 8bit", mci)) 1083157001Sgshapiro goto writeerr; 1084168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 108538032Speter "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s", 108638032Speter cte, MyHostName, e->e_id); 1087157001Sgshapiro if (!putline(buf, mci) || !putline("", mci)) 1088157001Sgshapiro goto writeerr; 108938032Speter mci->mci_flags &= ~MCIF_INHEADER; 109038032Speter 109138032Speter /* 109238032Speter ** Translate body encoding to 8-bit. Supports two types of 109338032Speter ** encodings; "base64" and "quoted-printable". Assume qp if 109438032Speter ** it is not base64. 109538032Speter */ 109638032Speter 109794334Sgshapiro pxflags = PXLF_MAPFROM; 109890792Sgshapiro if (sm_strcasecmp(cte, "base64") == 0) 109938032Speter { 110038032Speter int c1, c2, c3, c4; 110138032Speter 110238032Speter fbufp = fbuf; 110390792Sgshapiro while ((c1 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != 110490792Sgshapiro SM_IO_EOF) 110538032Speter { 110638032Speter if (isascii(c1) && isspace(c1)) 110738032Speter continue; 110838032Speter 110938032Speter do 111038032Speter { 111190792Sgshapiro c2 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); 111238032Speter } while (isascii(c2) && isspace(c2)); 111390792Sgshapiro if (c2 == SM_IO_EOF) 111438032Speter break; 111538032Speter 111638032Speter do 111738032Speter { 111890792Sgshapiro c3 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); 111938032Speter } while (isascii(c3) && isspace(c3)); 112090792Sgshapiro if (c3 == SM_IO_EOF) 112138032Speter break; 112238032Speter 112338032Speter do 112438032Speter { 112590792Sgshapiro c4 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); 112638032Speter } while (isascii(c4) && isspace(c4)); 112790792Sgshapiro if (c4 == SM_IO_EOF) 112838032Speter break; 112938032Speter 113038032Speter if (c1 == '=' || c2 == '=') 113138032Speter continue; 113238032Speter c1 = CHAR64(c1); 113338032Speter c2 = CHAR64(c2); 113438032Speter 1135125820Sgshapiro#if MIME7TO8_OLD 1136125820Sgshapiro#define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \ 1137125820Sgshapiro ++fbufp; 1138125820Sgshapiro#else /* MIME7TO8_OLD */ 1139125820Sgshapiro#define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \ 1140125820Sgshapiro { \ 1141125820Sgshapiro ++fbufp; \ 1142125820Sgshapiro pxflags |= PXLF_NOADDEOL; \ 1143125820Sgshapiro } 1144125820Sgshapiro#endif /* MIME7TO8_OLD */ 1145125820Sgshapiro 1146125820Sgshapiro#define PUTLINE64 \ 1147125820Sgshapiro do \ 1148125820Sgshapiro { \ 1149125820Sgshapiro if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) \ 1150125820Sgshapiro { \ 1151125820Sgshapiro CHK_EOL; \ 1152157001Sgshapiro if (!putxline((char *) fbuf, fbufp - fbuf, mci, pxflags)) \ 1153157001Sgshapiro goto writeerr; \ 1154125820Sgshapiro pxflags &= ~PXLF_NOADDEOL; \ 1155125820Sgshapiro fbufp = fbuf; \ 1156125820Sgshapiro } \ 1157125820Sgshapiro } while (0) 1158125820Sgshapiro 115938032Speter *fbufp = (c1 << 2) | ((c2 & 0x30) >> 4); 1160125820Sgshapiro PUTLINE64; 116138032Speter if (c3 == '=') 116238032Speter continue; 116338032Speter c3 = CHAR64(c3); 116438032Speter *fbufp = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); 1165125820Sgshapiro PUTLINE64; 116638032Speter if (c4 == '=') 116738032Speter continue; 116838032Speter c4 = CHAR64(c4); 116938032Speter *fbufp = ((c3 & 0x03) << 6) | c4; 1170125820Sgshapiro PUTLINE64; 117138032Speter } 117238032Speter } 117338032Speter else 117438032Speter { 117594334Sgshapiro int off; 117694334Sgshapiro 117738032Speter /* quoted-printable */ 117894334Sgshapiro pxflags |= PXLF_NOADDEOL; 117938032Speter fbufp = fbuf; 118094334Sgshapiro while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, 1181249729Sgshapiro sizeof(buf)) >= 0) 118238032Speter { 118394334Sgshapiro off = mime_fromqp((unsigned char *) buf, &fbufp, 118494334Sgshapiro &fbuf[MAXLINE] - fbufp); 118594334Sgshapiroagain: 118694334Sgshapiro if (off < -1) 118738032Speter continue; 118838032Speter 118938032Speter if (fbufp - fbuf > 0) 1190157001Sgshapiro { 1191157001Sgshapiro if (!putxline((char *) fbuf, fbufp - fbuf - 1, 1192157001Sgshapiro mci, pxflags)) 1193157001Sgshapiro goto writeerr; 1194157001Sgshapiro } 119538032Speter fbufp = fbuf; 119694334Sgshapiro if (off >= 0 && buf[off] != '\0') 119794334Sgshapiro { 119894334Sgshapiro off = mime_fromqp((unsigned char *) (buf + off), 119994334Sgshapiro &fbufp, 120094334Sgshapiro &fbuf[MAXLINE] - fbufp); 120194334Sgshapiro goto again; 120294334Sgshapiro } 120338032Speter } 120438032Speter } 120538032Speter 120638032Speter /* force out partial last line */ 120738032Speter if (fbufp > fbuf) 120838032Speter { 120938032Speter *fbufp = '\0'; 1210157001Sgshapiro if (!putxline((char *) fbuf, fbufp - fbuf, mci, pxflags)) 1211157001Sgshapiro goto writeerr; 121238032Speter } 121394334Sgshapiro 121494334Sgshapiro /* 121594334Sgshapiro ** The decoded text may end without an EOL. Since this function 121694334Sgshapiro ** is only called for text/plain MIME messages, it is safe to 121794334Sgshapiro ** add an extra one at the end just in case. This is a hack, 121894334Sgshapiro ** but so is auto-converting MIME in the first place. 121994334Sgshapiro */ 122094334Sgshapiro 1221157001Sgshapiro if (!putline("", mci)) 1222157001Sgshapiro goto writeerr; 122394334Sgshapiro 122438032Speter if (tTd(43, 3)) 122590792Sgshapiro sm_dprintf("\t\t\tmime7to8 => %s to 8bit done\n", cte); 1226157001Sgshapiro return true; 1227157001Sgshapiro 1228157001Sgshapiro writeerr: 1229157001Sgshapiro return false; 123038032Speter} 123190792Sgshapiro/* 123238032Speter** The following is based on Borenstein's "codes.c" module, with simplifying 123338032Speter** changes as we do not deal with multipart, and to do the translation in-core, 123438032Speter** with an attempt to prevent overrun of output buffers. 123538032Speter** 123690792Sgshapiro** What is needed here are changes to defend this code better against 123738032Speter** bad encodings. Questionable to always return 0xFF for bad mappings. 123838032Speter*/ 123938032Speter 124038032Speterstatic char index_hex[128] = 124138032Speter{ 124238032Speter -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 124338032Speter -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 124438032Speter -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 124538032Speter 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, 124638032Speter -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 124738032Speter -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 124838032Speter -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 124938032Speter -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 125038032Speter}; 125138032Speter 125264562Sgshapiro# define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)]) 125338032Speter 125494334Sgshapiro/* 125594334Sgshapiro** MIME_FROMQP -- decode quoted printable string 125694334Sgshapiro** 125794334Sgshapiro** Parameters: 125894334Sgshapiro** infile -- input (encoded) string 125994334Sgshapiro** outfile -- output string 126094334Sgshapiro** maxlen -- size of output buffer 126194334Sgshapiro** 126294334Sgshapiro** Returns: 126394334Sgshapiro** -2 if decoding failure 126494334Sgshapiro** -1 if infile completely decoded into outfile 126594334Sgshapiro** >= 0 is the position in infile decoding 126694334Sgshapiro** reached before maxlen was reached 126794334Sgshapiro*/ 126894334Sgshapiro 126964562Sgshapirostatic int 127094334Sgshapiromime_fromqp(infile, outfile, maxlen) 127190792Sgshapiro unsigned char *infile; 127290792Sgshapiro unsigned char **outfile; 127338032Speter int maxlen; /* Max # of chars allowed in outfile */ 127438032Speter{ 127538032Speter int c1, c2; 127638032Speter int nchar = 0; 127794334Sgshapiro unsigned char *b; 127838032Speter 127994334Sgshapiro /* decrement by one for trailing '\0', at least one other char */ 128094334Sgshapiro if (--maxlen < 1) 128190792Sgshapiro return 0; 128290792Sgshapiro 128394334Sgshapiro b = infile; 128494334Sgshapiro while ((c1 = *infile++) != '\0' && nchar < maxlen) 128538032Speter { 128638032Speter if (c1 == '=') 128738032Speter { 128894334Sgshapiro if ((c1 = *infile++) == '\0') 128938032Speter break; 129038032Speter 129138032Speter if (c1 == '\n' || (c1 = HEXCHAR(c1)) == -1) 129238032Speter { 129394334Sgshapiro /* ignore it and the rest of the buffer */ 129494334Sgshapiro return -2; 129538032Speter } 129638032Speter else 129738032Speter { 129838032Speter do 129938032Speter { 130038032Speter if ((c2 = *infile++) == '\0') 130138032Speter { 130238032Speter c2 = -1; 130338032Speter break; 130438032Speter } 130538032Speter } while ((c2 = HEXCHAR(c2)) == -1); 130638032Speter 130794334Sgshapiro if (c2 == -1) 130838032Speter break; 130994334Sgshapiro nchar++; 131038032Speter *(*outfile)++ = c1 << 4 | c2; 131138032Speter } 131238032Speter } 131338032Speter else 131438032Speter { 131594334Sgshapiro nchar++; 131638032Speter *(*outfile)++ = c1; 131738032Speter if (c1 == '\n') 131838032Speter break; 131938032Speter } 132038032Speter } 132138032Speter *(*outfile)++ = '\0'; 132294334Sgshapiro if (nchar >= maxlen) 132394334Sgshapiro return (infile - b - 1); 132494334Sgshapiro return -1; 132538032Speter} 132638032Speter#endif /* MIME7TO8 */ 1327