1 0 stevel /* 2 2197 jbeck * Copyright (c) 1998-2003, 2006 Sendmail, Inc. and its suppliers. 3 0 stevel * All rights reserved. 4 0 stevel * Copyright (c) 1994, 1996-1997 Eric P. Allman. All rights reserved. 5 0 stevel * Copyright (c) 1994 6 0 stevel * The Regents of the University of California. All rights reserved. 7 0 stevel * 8 0 stevel * By using this file, you agree to the terms and conditions set 9 0 stevel * forth in the LICENSE file which can be found at the top level of 10 0 stevel * the sendmail distribution. 11 0 stevel * 12 0 stevel */ 13 0 stevel 14 0 stevel #pragma ident "%Z%%M% %I% %E% SMI" 15 0 stevel 16 0 stevel #include <sendmail.h> 17 0 stevel #include <string.h> 18 0 stevel 19 5402 jbeck SM_RCSID("@(#)$Id: mime.c,v 8.147 2007/09/26 23:29:11 ca Exp $") 20 0 stevel 21 0 stevel /* 22 0 stevel ** MIME support. 23 0 stevel ** 24 0 stevel ** I am indebted to John Beck of Hewlett-Packard, who contributed 25 0 stevel ** his code to me for inclusion. As it turns out, I did not use 26 0 stevel ** his code since he used a "minimum change" approach that used 27 0 stevel ** several temp files, and I wanted a "minimum impact" approach 28 0 stevel ** that would avoid copying. However, looking over his code 29 0 stevel ** helped me cement my understanding of the problem. 30 0 stevel ** 31 0 stevel ** I also looked at, but did not directly use, Nathaniel 32 0 stevel ** Borenstein's "code.c" module. Again, it functioned as 33 0 stevel ** a file-to-file translator, which did not fit within my 34 0 stevel ** design bounds, but it was a useful base for understanding 35 0 stevel ** the problem. 36 0 stevel */ 37 0 stevel 38 0 stevel /* use "old" mime 7 to 8 algorithm by default */ 39 0 stevel #ifndef MIME7TO8_OLD 40 0 stevel # define MIME7TO8_OLD 1 41 0 stevel #endif /* ! MIME7TO8_OLD */ 42 0 stevel 43 0 stevel #if MIME8TO7 44 0 stevel static int isboundary __P((char *, char **)); 45 0 stevel static int mimeboundary __P((char *, char **)); 46 0 stevel static int mime_getchar __P((SM_FILE_T *, char **, int *)); 47 0 stevel static int mime_getchar_crlf __P((SM_FILE_T *, char **, int *)); 48 0 stevel 49 0 stevel /* character set for hex and base64 encoding */ 50 0 stevel static char Base16Code[] = "0123456789ABCDEF"; 51 0 stevel static char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 52 0 stevel 53 0 stevel /* types of MIME boundaries */ 54 0 stevel # define MBT_SYNTAX 0 /* syntax error */ 55 0 stevel # define MBT_NOTSEP 1 /* not a boundary */ 56 0 stevel # define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */ 57 0 stevel # define MBT_FINAL 3 /* final boundary (trailing -- included) */ 58 0 stevel 59 0 stevel static char *MimeBoundaryNames[] = 60 0 stevel { 61 0 stevel "SYNTAX", "NOTSEP", "INTERMED", "FINAL" 62 0 stevel }; 63 0 stevel 64 0 stevel static bool MapNLtoCRLF; 65 0 stevel 66 0 stevel /* 67 0 stevel ** MIME8TO7 -- output 8 bit body in 7 bit format 68 0 stevel ** 69 0 stevel ** The header has already been output -- this has to do the 70 0 stevel ** 8 to 7 bit conversion. It would be easy if we didn't have 71 0 stevel ** to deal with nested formats (multipart/xxx and message/rfc822). 72 0 stevel ** 73 0 stevel ** We won't be called if we don't have to do a conversion, and 74 0 stevel ** appropriate MIME-Version: and Content-Type: fields have been 75 0 stevel ** output. Any Content-Transfer-Encoding: field has not been 76 0 stevel ** output, and we can add it here. 77 0 stevel ** 78 0 stevel ** Parameters: 79 0 stevel ** mci -- mailer connection information. 80 0 stevel ** header -- the header for this body part. 81 0 stevel ** e -- envelope. 82 0 stevel ** boundaries -- the currently pending message boundaries. 83 0 stevel ** NULL if we are processing the outer portion. 84 0 stevel ** flags -- to tweak processing. 85 2197 jbeck ** level -- recursion level. 86 0 stevel ** 87 0 stevel ** Returns: 88 0 stevel ** An indicator of what terminated the message part: 89 0 stevel ** MBT_FINAL -- the final boundary 90 0 stevel ** MBT_INTERMED -- an intermediate boundary 91 0 stevel ** MBT_NOTSEP -- an end of file 92 1658 jbeck ** SM_IO_EOF -- I/O error occurred 93 0 stevel */ 94 0 stevel 95 0 stevel struct args 96 0 stevel { 97 0 stevel char *a_field; /* name of field */ 98 0 stevel char *a_value; /* value of that field */ 99 0 stevel }; 100 0 stevel 101 0 stevel int 102 2197 jbeck mime8to7(mci, header, e, boundaries, flags, level) 103 0 stevel register MCI *mci; 104 0 stevel HDR *header; 105 0 stevel register ENVELOPE *e; 106 0 stevel char **boundaries; 107 0 stevel int flags; 108 2197 jbeck int level; 109 0 stevel { 110 0 stevel register char *p; 111 0 stevel int linelen; 112 0 stevel int bt; 113 0 stevel off_t offset; 114 0 stevel size_t sectionsize, sectionhighbits; 115 0 stevel int i; 116 0 stevel char *type; 117 0 stevel char *subtype; 118 0 stevel char *cte; 119 0 stevel char **pvp; 120 0 stevel int argc = 0; 121 0 stevel char *bp; 122 0 stevel bool use_qp = false; 123 0 stevel struct args argv[MAXMIMEARGS]; 124 0 stevel char bbuf[128]; 125 0 stevel char buf[MAXLINE]; 126 0 stevel char pvpbuf[MAXLINE]; 127 0 stevel extern unsigned char MimeTokenTab[256]; 128 0 stevel 129 2197 jbeck if (level > MAXMIMENESTING) 130 2197 jbeck { 131 2197 jbeck if (!bitset(EF_TOODEEP, e->e_flags)) 132 2197 jbeck { 133 2197 jbeck if (tTd(43, 4)) 134 2197 jbeck sm_dprintf("mime8to7: too deep, level=%d\n", 135 2197 jbeck level); 136 2197 jbeck usrerr("mime8to7: recursion level %d exceeded", 137 2197 jbeck level); 138 2197 jbeck e->e_flags |= EF_DONT_MIME|EF_TOODEEP; 139 2197 jbeck } 140 2197 jbeck } 141 0 stevel if (tTd(43, 1)) 142 0 stevel { 143 0 stevel sm_dprintf("mime8to7: flags = %x, boundaries =", flags); 144 0 stevel if (boundaries[0] == NULL) 145 0 stevel sm_dprintf(" <none>"); 146 0 stevel else 147 0 stevel { 148 0 stevel for (i = 0; boundaries[i] != NULL; i++) 149 0 stevel sm_dprintf(" %s", boundaries[i]); 150 0 stevel } 151 0 stevel sm_dprintf("\n"); 152 0 stevel } 153 0 stevel MapNLtoCRLF = true; 154 0 stevel p = hvalue("Content-Transfer-Encoding", header); 155 0 stevel if (p == NULL || 156 3544 jbeck (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL, 157 0 stevel MimeTokenTab, false)) == NULL || 158 0 stevel pvp[0] == NULL) 159 0 stevel { 160 0 stevel cte = NULL; 161 0 stevel } 162 0 stevel else 163 0 stevel { 164 3544 jbeck cataddr(pvp, NULL, buf, sizeof(buf), '\0', false); 165 0 stevel cte = sm_rpool_strdup_x(e->e_rpool, buf); 166 0 stevel } 167 0 stevel 168 0 stevel type = subtype = NULL; 169 0 stevel p = hvalue("Content-Type", header); 170 0 stevel if (p == NULL) 171 0 stevel { 172 0 stevel if (bitset(M87F_DIGEST, flags)) 173 0 stevel p = "message/rfc822"; 174 0 stevel else 175 0 stevel p = "text/plain"; 176 0 stevel } 177 0 stevel if (p != NULL && 178 3544 jbeck (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL, 179 0 stevel MimeTokenTab, false)) != NULL && 180 0 stevel pvp[0] != NULL) 181 0 stevel { 182 0 stevel if (tTd(43, 40)) 183 0 stevel { 184 0 stevel for (i = 0; pvp[i] != NULL; i++) 185 0 stevel sm_dprintf("pvp[%d] = \"%s\"\n", i, pvp[i]); 186 0 stevel } 187 0 stevel type = *pvp++; 188 0 stevel if (*pvp != NULL && strcmp(*pvp, "/") == 0 && 189 0 stevel *++pvp != NULL) 190 0 stevel { 191 0 stevel subtype = *pvp++; 192 0 stevel } 193 0 stevel 194 0 stevel /* break out parameters */ 195 0 stevel while (*pvp != NULL && argc < MAXMIMEARGS) 196 0 stevel { 197 0 stevel /* skip to semicolon separator */ 198 0 stevel while (*pvp != NULL && strcmp(*pvp, ";") != 0) 199 0 stevel pvp++; 200 0 stevel if (*pvp++ == NULL || *pvp == NULL) 201 0 stevel break; 202 0 stevel 203 0 stevel /* complain about empty values */ 204 0 stevel if (strcmp(*pvp, ";") == 0) 205 0 stevel { 206 0 stevel usrerr("mime8to7: Empty parameter in Content-Type header"); 207 0 stevel 208 0 stevel /* avoid bounce loops */ 209 0 stevel e->e_flags |= EF_DONT_MIME; 210 0 stevel continue; 211 0 stevel } 212 0 stevel 213 0 stevel /* extract field name */ 214 0 stevel argv[argc].a_field = *pvp++; 215 0 stevel 216 0 stevel /* see if there is a value */ 217 0 stevel if (*pvp != NULL && strcmp(*pvp, "=") == 0 && 218 0 stevel (*++pvp == NULL || strcmp(*pvp, ";") != 0)) 219 0 stevel { 220 0 stevel argv[argc].a_value = *pvp; 221 0 stevel argc++; 222 0 stevel } 223 0 stevel } 224 0 stevel } 225 0 stevel 226 0 stevel /* check for disaster cases */ 227 0 stevel if (type == NULL) 228 0 stevel type = "-none-"; 229 0 stevel if (subtype == NULL) 230 0 stevel subtype = "-none-"; 231 0 stevel 232 2197 jbeck /* don't propagate some flags more than one level into the message */ 233 0 stevel flags &= ~M87F_DIGEST; 234 0 stevel 235 0 stevel /* 236 0 stevel ** Check for cases that can not be encoded. 237 0 stevel ** 238 0 stevel ** For example, you can't encode certain kinds of types 239 0 stevel ** or already-encoded messages. If we find this case, 240 0 stevel ** just copy it through. 241 0 stevel */ 242 0 stevel 243 3544 jbeck (void) sm_snprintf(buf, sizeof(buf), "%.100s/%.100s", type, subtype); 244 0 stevel if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) 245 0 stevel flags |= M87F_NO8BIT; 246 0 stevel 247 0 stevel # ifdef USE_B_CLASS 248 0 stevel if (wordinclass(buf, 'b') || wordinclass(type, 'b')) 249 0 stevel MapNLtoCRLF = false; 250 0 stevel # endif /* USE_B_CLASS */ 251 0 stevel if (wordinclass(buf, 'q') || wordinclass(type, 'q')) 252 0 stevel use_qp = true; 253 0 stevel 254 0 stevel /* 255 0 stevel ** Multipart requires special processing. 256 0 stevel ** 257 0 stevel ** Do a recursive descent into the message. 258 0 stevel */ 259 0 stevel 260 0 stevel if (sm_strcasecmp(type, "multipart") == 0 && 261 2197 jbeck (!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags)) && 262 2197 jbeck !bitset(EF_TOODEEP, e->e_flags) 263 2197 jbeck ) 264 0 stevel { 265 0 stevel 266 0 stevel if (sm_strcasecmp(subtype, "digest") == 0) 267 0 stevel flags |= M87F_DIGEST; 268 0 stevel 269 0 stevel for (i = 0; i < argc; i++) 270 0 stevel { 271 0 stevel if (sm_strcasecmp(argv[i].a_field, "boundary") == 0) 272 0 stevel break; 273 0 stevel } 274 0 stevel if (i >= argc || argv[i].a_value == NULL) 275 0 stevel { 276 0 stevel usrerr("mime8to7: Content-Type: \"%s\": %s boundary", 277 0 stevel i >= argc ? "missing" : "bogus", p); 278 0 stevel p = "---"; 279 0 stevel 280 0 stevel /* avoid bounce loops */ 281 0 stevel e->e_flags |= EF_DONT_MIME; 282 0 stevel } 283 0 stevel else 284 0 stevel { 285 0 stevel p = argv[i].a_value; 286 0 stevel stripquotes(p); 287 0 stevel } 288 3544 jbeck if (sm_strlcpy(bbuf, p, sizeof(bbuf)) >= sizeof(bbuf)) 289 0 stevel { 290 0 stevel usrerr("mime8to7: multipart boundary \"%s\" too long", 291 0 stevel p); 292 0 stevel 293 0 stevel /* avoid bounce loops */ 294 0 stevel e->e_flags |= EF_DONT_MIME; 295 0 stevel } 296 0 stevel 297 0 stevel if (tTd(43, 1)) 298 0 stevel sm_dprintf("mime8to7: multipart boundary \"%s\"\n", 299 0 stevel bbuf); 300 0 stevel for (i = 0; i < MAXMIMENESTING; i++) 301 0 stevel { 302 0 stevel if (boundaries[i] == NULL) 303 0 stevel break; 304 0 stevel } 305 0 stevel if (i >= MAXMIMENESTING) 306 0 stevel { 307 2197 jbeck if (tTd(43, 4)) 308 2197 jbeck sm_dprintf("mime8to7: too deep, i=%d\n", i); 309 2197 jbeck if (!bitset(EF_TOODEEP, e->e_flags)) 310 2197 jbeck usrerr("mime8to7: multipart nesting boundary too deep"); 311 0 stevel 312 0 stevel /* avoid bounce loops */ 313 2197 jbeck e->e_flags |= EF_DONT_MIME|EF_TOODEEP; 314 0 stevel } 315 0 stevel else 316 0 stevel { 317 0 stevel boundaries[i] = bbuf; 318 0 stevel boundaries[i + 1] = NULL; 319 0 stevel } 320 0 stevel mci->mci_flags |= MCIF_INMIME; 321 0 stevel 322 0 stevel /* skip the early "comment" prologue */ 323 1658 jbeck if (!putline("", mci)) 324 1658 jbeck goto writeerr; 325 0 stevel mci->mci_flags &= ~MCIF_INHEADER; 326 0 stevel bt = MBT_FINAL; 327 3544 jbeck while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf)) 328 0 stevel != NULL) 329 0 stevel { 330 0 stevel bt = mimeboundary(buf, boundaries); 331 0 stevel if (bt != MBT_NOTSEP) 332 0 stevel break; 333 1658 jbeck if (!putxline(buf, strlen(buf), mci, 334 1658 jbeck PXLF_MAPFROM|PXLF_STRIP8BIT)) 335 1658 jbeck goto writeerr; 336 0 stevel if (tTd(43, 99)) 337 0 stevel sm_dprintf(" ...%s", buf); 338 0 stevel } 339 0 stevel if (sm_io_eof(e->e_dfp)) 340 0 stevel bt = MBT_FINAL; 341 0 stevel while (bt != MBT_FINAL) 342 0 stevel { 343 0 stevel auto HDR *hdr = NULL; 344 0 stevel 345 3544 jbeck (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", bbuf); 346 1658 jbeck if (!putline(buf, mci)) 347 1658 jbeck goto writeerr; 348 0 stevel if (tTd(43, 35)) 349 0 stevel sm_dprintf(" ...%s\n", buf); 350 0 stevel collect(e->e_dfp, false, &hdr, e, false); 351 0 stevel if (tTd(43, 101)) 352 0 stevel putline("+++after collect", mci); 353 1658 jbeck if (!putheader(mci, hdr, e, flags)) 354 1658 jbeck goto writeerr; 355 0 stevel if (tTd(43, 101)) 356 0 stevel putline("+++after putheader", mci); 357 2197 jbeck bt = mime8to7(mci, hdr, e, boundaries, flags, 358 2197 jbeck level + 1); 359 1658 jbeck if (bt == SM_IO_EOF) 360 1658 jbeck goto writeerr; 361 0 stevel } 362 3544 jbeck (void) sm_strlcpyn(buf, sizeof(buf), 3, "--", bbuf, "--"); 363 1658 jbeck if (!putline(buf, mci)) 364 1658 jbeck goto writeerr; 365 0 stevel if (tTd(43, 35)) 366 0 stevel sm_dprintf(" ...%s\n", buf); 367 0 stevel boundaries[i] = NULL; 368 0 stevel mci->mci_flags &= ~MCIF_INMIME; 369 0 stevel 370 0 stevel /* skip the late "comment" epilogue */ 371 3544 jbeck while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf)) 372 0 stevel != NULL) 373 0 stevel { 374 0 stevel bt = mimeboundary(buf, boundaries); 375 0 stevel if (bt != MBT_NOTSEP) 376 0 stevel break; 377 1658 jbeck if (!putxline(buf, strlen(buf), mci, 378 1658 jbeck PXLF_MAPFROM|PXLF_STRIP8BIT)) 379 1658 jbeck goto writeerr; 380 0 stevel if (tTd(43, 99)) 381 0 stevel sm_dprintf(" ...%s", buf); 382 0 stevel } 383 0 stevel if (sm_io_eof(e->e_dfp)) 384 0 stevel bt = MBT_FINAL; 385 0 stevel if (tTd(43, 3)) 386 0 stevel sm_dprintf("\t\t\tmime8to7=>%s (multipart)\n", 387 0 stevel MimeBoundaryNames[bt]); 388 0 stevel return bt; 389 0 stevel } 390 0 stevel 391 0 stevel /* 392 0 stevel ** Message/xxx types -- recurse exactly once. 393 0 stevel ** 394 0 stevel ** Class 's' is predefined to have "rfc822" only. 395 0 stevel */ 396 0 stevel 397 0 stevel if (sm_strcasecmp(type, "message") == 0) 398 0 stevel { 399 2197 jbeck if (!wordinclass(subtype, 's') || 400 2197 jbeck bitset(EF_TOODEEP, e->e_flags)) 401 0 stevel { 402 0 stevel flags |= M87F_NO8BIT; 403 0 stevel } 404 0 stevel else 405 0 stevel { 406 0 stevel auto HDR *hdr = NULL; 407 0 stevel 408 1658 jbeck if (!putline("", mci)) 409 1658 jbeck goto writeerr; 410 0 stevel 411 0 stevel mci->mci_flags |= MCIF_INMIME; 412 0 stevel collect(e->e_dfp, false, &hdr, e, false); 413 0 stevel if (tTd(43, 101)) 414 0 stevel putline("+++after collect", mci); 415 1658 jbeck if (!putheader(mci, hdr, e, flags)) 416 1658 jbeck goto writeerr; 417 0 stevel if (tTd(43, 101)) 418 0 stevel putline("+++after putheader", mci); 419 0 stevel if (hvalue("MIME-Version", hdr) == NULL && 420 1658 jbeck !bitset(M87F_NO8TO7, flags) && 421 1658 jbeck !putline("MIME-Version: 1.0", mci)) 422 1658 jbeck goto writeerr; 423 2197 jbeck bt = mime8to7(mci, hdr, e, boundaries, flags, 424 2197 jbeck level + 1); 425 0 stevel mci->mci_flags &= ~MCIF_INMIME; 426 0 stevel return bt; 427 0 stevel } 428 0 stevel } 429 0 stevel 430 0 stevel /* 431 0 stevel ** Non-compound body type 432 0 stevel ** 433 0 stevel ** Compute the ratio of seven to eight bit characters; 434 0 stevel ** use that as a heuristic to decide how to do the 435 0 stevel ** encoding. 436 0 stevel */ 437 0 stevel 438 0 stevel sectionsize = sectionhighbits = 0; 439 0 stevel if (!bitset(M87F_NO8BIT|M87F_NO8TO7, flags)) 440 0 stevel { 441 0 stevel /* remember where we were */ 442 0 stevel offset = sm_io_tell(e->e_dfp, SM_TIME_DEFAULT); 443 0 stevel if (offset == -1) 444 0 stevel syserr("mime8to7: cannot sm_io_tell on %cf%s", 445 0 stevel DATAFL_LETTER, e->e_id); 446 0 stevel 447 0 stevel /* do a scan of this body type to count character types */ 448 3544 jbeck while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf)) 449 0 stevel != NULL) 450 0 stevel { 451 0 stevel if (mimeboundary(buf, boundaries) != MBT_NOTSEP) 452 0 stevel break; 453 0 stevel for (p = buf; *p != '\0'; p++) 454 0 stevel { 455 0 stevel /* count bytes with the high bit set */ 456 0 stevel sectionsize++; 457 0 stevel if (bitset(0200, *p)) 458 0 stevel sectionhighbits++; 459 0 stevel } 460 0 stevel 461 0 stevel /* 462 0 stevel ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, 463 0 stevel ** assume base64. This heuristic avoids double-reading 464 0 stevel ** large graphics or video files. 465 0 stevel */ 466 0 stevel 467 0 stevel if (sectionsize >= 4096 && 468 0 stevel sectionhighbits > sectionsize / 4) 469 0 stevel break; 470 0 stevel } 471 0 stevel 472 0 stevel /* return to the original offset for processing */ 473 0 stevel /* XXX use relative seeks to handle >31 bit file sizes? */ 474 0 stevel if (sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, offset, SEEK_SET) < 0) 475 0 stevel syserr("mime8to7: cannot sm_io_fseek on %cf%s", 476 0 stevel DATAFL_LETTER, e->e_id); 477 0 stevel else 478 0 stevel sm_io_clearerr(e->e_dfp); 479 0 stevel } 480 0 stevel 481 0 stevel /* 482 0 stevel ** Heuristically determine encoding method. 483 0 stevel ** If more than 1/8 of the total characters have the 484 0 stevel ** eighth bit set, use base64; else use quoted-printable. 485 0 stevel ** However, only encode binary encoded data as base64, 486 0 stevel ** since otherwise the NL=>CRLF mapping will be a problem. 487 0 stevel */ 488 0 stevel 489 0 stevel if (tTd(43, 8)) 490 0 stevel { 491 0 stevel sm_dprintf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n", 492 0 stevel (long) sectionhighbits, (long) sectionsize, 493 0 stevel cte == NULL ? "[none]" : cte, 494 0 stevel type == NULL ? "[none]" : type, 495 0 stevel subtype == NULL ? "[none]" : subtype); 496 0 stevel } 497 0 stevel if (cte != NULL && sm_strcasecmp(cte, "binary") == 0) 498 0 stevel sectionsize = sectionhighbits; 499 0 stevel linelen = 0; 500 0 stevel bp = buf; 501 0 stevel if (sectionhighbits == 0) 502 0 stevel { 503 0 stevel /* no encoding necessary */ 504 0 stevel if (cte != NULL && 505 0 stevel bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, 506 0 stevel mci->mci_flags) && 507 0 stevel !bitset(M87F_NO8TO7, flags)) 508 0 stevel { 509 0 stevel /* 510 0 stevel ** Skip _unless_ in MIME mode and potentially 511 0 stevel ** converting from 8 bit to 7 bit MIME. See 512 0 stevel ** putheader() for the counterpart where the 513 0 stevel ** CTE header is skipped in the opposite 514 0 stevel ** situation. 515 0 stevel */ 516 0 stevel 517 3544 jbeck (void) sm_snprintf(buf, sizeof(buf), 518 0 stevel "Content-Transfer-Encoding: %.200s", cte); 519 1658 jbeck if (!putline(buf, mci)) 520 1658 jbeck goto writeerr; 521 0 stevel if (tTd(43, 36)) 522 0 stevel sm_dprintf(" ...%s\n", buf); 523 0 stevel } 524 1658 jbeck if (!putline("", mci)) 525 1658 jbeck goto writeerr; 526 0 stevel mci->mci_flags &= ~MCIF_INHEADER; 527 3544 jbeck while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf)) 528 0 stevel != NULL) 529 0 stevel { 530 5402 jbeck if (!bitset(MCIF_INLONGLINE, mci->mci_flags)) 531 5402 jbeck { 532 5402 jbeck bt = mimeboundary(buf, boundaries); 533 5402 jbeck if (bt != MBT_NOTSEP) 534 5402 jbeck break; 535 5402 jbeck } 536 5402 jbeck if (!putxline(buf, strlen(buf), mci, 537 5402 jbeck PXLF_MAPFROM|PXLF_NOADDEOL)) 538 1658 jbeck goto writeerr; 539 0 stevel } 540 0 stevel if (sm_io_eof(e->e_dfp)) 541 0 stevel bt = MBT_FINAL; 542 0 stevel } 543 0 stevel else if (!MapNLtoCRLF || 544 0 stevel (sectionsize / 8 < sectionhighbits && !use_qp)) 545 0 stevel { 546 0 stevel /* use base64 encoding */ 547 0 stevel int c1, c2; 548 0 stevel 549 0 stevel if (tTd(43, 36)) 550 0 stevel sm_dprintf(" ...Content-Transfer-Encoding: base64\n"); 551 1658 jbeck if (!putline("Content-Transfer-Encoding: base64", mci)) 552 1658 jbeck goto writeerr; 553 3544 jbeck (void) sm_snprintf(buf, sizeof(buf), 554 0 stevel "X-MIME-Autoconverted: from 8bit to base64 by %s id %s", 555 0 stevel MyHostName, e->e_id); 556 1658 jbeck if (!putline(buf, mci) || !putline("", mci)) 557 1658 jbeck goto writeerr; 558 0 stevel mci->mci_flags &= ~MCIF_INHEADER; 559 0 stevel while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != 560 0 stevel SM_IO_EOF) 561 0 stevel { 562 0 stevel if (linelen > 71) 563 0 stevel { 564 0 stevel *bp = '\0'; 565 1658 jbeck if (!putline(buf, mci)) 566 1658 jbeck goto writeerr; 567 0 stevel linelen = 0; 568 0 stevel bp = buf; 569 0 stevel } 570 0 stevel linelen += 4; 571 0 stevel *bp++ = Base64Code[(c1 >> 2)]; 572 0 stevel c1 = (c1 & 0x03) << 4; 573 0 stevel c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 574 0 stevel if (c2 == SM_IO_EOF) 575 0 stevel { 576 0 stevel *bp++ = Base64Code[c1]; 577 0 stevel *bp++ = '='; 578 0 stevel *bp++ = '='; 579 0 stevel break; 580 0 stevel } 581 0 stevel c1 |= (c2 >> 4) & 0x0f; 582 0 stevel *bp++ = Base64Code[c1]; 583 0 stevel c1 = (c2 & 0x0f) << 2; 584 0 stevel c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 585 0 stevel if (c2 == SM_IO_EOF) 586 0 stevel { 587 0 stevel *bp++ = Base64Code[c1]; 588 0 stevel *bp++ = '='; 589 0 stevel break; 590 0 stevel } 591 0 stevel c1 |= (c2 >> 6) & 0x03; 592 0 stevel *bp++ = Base64Code[c1]; 593 0 stevel *bp++ = Base64Code[c2 & 0x3f]; 594 0 stevel } 595 0 stevel *bp = '\0'; 596 1658 jbeck if (!putline(buf, mci)) 597 1658 jbeck goto writeerr; 598 0 stevel } 599 0 stevel else 600 0 stevel { 601 0 stevel /* use quoted-printable encoding */ 602 0 stevel int c1, c2; 603 0 stevel int fromstate; 604 0 stevel BITMAP256 badchars; 605 0 stevel 606 0 stevel /* set up map of characters that must be mapped */ 607 0 stevel clrbitmap(badchars); 608 0 stevel for (c1 = 0x00; c1 < 0x20; c1++) 609 0 stevel setbitn(c1, badchars); 610 0 stevel clrbitn('\t', badchars); 611 0 stevel for (c1 = 0x7f; c1 < 0x100; c1++) 612 0 stevel setbitn(c1, badchars); 613 0 stevel setbitn('=', badchars); 614 0 stevel if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) 615 0 stevel for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) 616 0 stevel setbitn(*p, badchars); 617 0 stevel 618 0 stevel if (tTd(43, 36)) 619 0 stevel sm_dprintf(" ...Content-Transfer-Encoding: quoted-printable\n"); 620 1658 jbeck if (!putline("Content-Transfer-Encoding: quoted-printable", 621 1658 jbeck mci)) 622 1658 jbeck goto writeerr; 623 3544 jbeck (void) sm_snprintf(buf, sizeof(buf), 624 0 stevel "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s", 625 0 stevel MyHostName, e->e_id); 626 1658 jbeck if (!putline(buf, mci) || !putline("", mci)) 627 1658 jbeck goto writeerr; 628 0 stevel mci->mci_flags &= ~MCIF_INHEADER; 629 0 stevel fromstate = 0; 630 0 stevel c2 = '\n'; 631 0 stevel while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != 632 0 stevel SM_IO_EOF) 633 0 stevel { 634 0 stevel if (c1 == '\n') 635 0 stevel { 636 0 stevel if (c2 == ' ' || c2 == '\t') 637 0 stevel { 638 0 stevel *bp++ = '='; 639 0 stevel *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 640 0 stevel *bp++ = Base16Code[c2 & 0x0f]; 641 0 stevel } 642 0 stevel if (buf[0] == '.' && bp == &buf[1]) 643 0 stevel { 644 0 stevel buf[0] = '='; 645 0 stevel *bp++ = Base16Code[('.' >> 4) & 0x0f]; 646 0 stevel *bp++ = Base16Code['.' & 0x0f]; 647 0 stevel } 648 0 stevel *bp = '\0'; 649 1658 jbeck if (!putline(buf, mci)) 650 1658 jbeck goto writeerr; 651 0 stevel linelen = fromstate = 0; 652 0 stevel bp = buf; 653 0 stevel c2 = c1; 654 0 stevel continue; 655 0 stevel } 656 0 stevel if (c2 == ' ' && linelen == 4 && fromstate == 4 && 657 0 stevel bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 658 0 stevel { 659 0 stevel *bp++ = '='; 660 0 stevel *bp++ = '2'; 661 0 stevel *bp++ = '0'; 662 0 stevel linelen += 3; 663 0 stevel } 664 0 stevel else if (c2 == ' ' || c2 == '\t') 665 0 stevel { 666 0 stevel *bp++ = c2; 667 0 stevel linelen++; 668 0 stevel } 669 0 stevel if (linelen > 72 && 670 0 stevel (linelen > 75 || c1 != '.' || 671 0 stevel (linelen > 73 && c2 == '.'))) 672 0 stevel { 673 0 stevel if (linelen > 73 && c2 == '.') 674 0 stevel bp--; 675 0 stevel else 676 0 stevel c2 = '\n'; 677 0 stevel *bp++ = '='; 678 0 stevel *bp = '\0'; 679 1658 jbeck if (!putline(buf, mci)) 680 1658 jbeck goto writeerr; 681 0 stevel linelen = fromstate = 0; 682 0 stevel bp = buf; 683 0 stevel if (c2 == '.') 684 0 stevel { 685 0 stevel *bp++ = '.'; 686 0 stevel linelen++; 687 0 stevel } 688 0 stevel } 689 0 stevel if (bitnset(bitidx(c1), badchars)) 690 0 stevel { 691 0 stevel *bp++ = '='; 692 0 stevel *bp++ = Base16Code[(c1 >> 4) & 0x0f]; 693 0 stevel *bp++ = Base16Code[c1 & 0x0f]; 694 0 stevel linelen += 3; 695 0 stevel } 696 0 stevel else if (c1 != ' ' && c1 != '\t') 697 0 stevel { 698 0 stevel if (linelen < 4 && c1 == "From"[linelen]) 699 0 stevel fromstate++; 700 0 stevel *bp++ = c1; 701 0 stevel linelen++; 702 0 stevel } 703 0 stevel c2 = c1; 704 0 stevel } 705 0 stevel 706 0 stevel /* output any saved character */ 707 0 stevel if (c2 == ' ' || c2 == '\t') 708 0 stevel { 709 0 stevel *bp++ = '='; 710 0 stevel *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 711 0 stevel *bp++ = Base16Code[c2 & 0x0f]; 712 0 stevel linelen += 3; 713 0 stevel } 714 0 stevel 715 0 stevel if (linelen > 0 || boundaries[0] != NULL) 716 0 stevel { 717 0 stevel *bp = '\0'; 718 1658 jbeck if (!putline(buf, mci)) 719 1658 jbeck goto writeerr; 720 0 stevel } 721 0 stevel 722 0 stevel } 723 0 stevel if (tTd(43, 3)) 724 0 stevel sm_dprintf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); 725 0 stevel return bt; 726 1658 jbeck 727 1658 jbeck writeerr: 728 1658 jbeck return SM_IO_EOF; 729 0 stevel } 730 0 stevel /* 731 0 stevel ** MIME_GETCHAR -- get a character for MIME processing 732 0 stevel ** 733 0 stevel ** Treats boundaries as SM_IO_EOF. 734 0 stevel ** 735 0 stevel ** Parameters: 736 0 stevel ** fp -- the input file. 737 0 stevel ** boundaries -- the current MIME boundaries. 738 0 stevel ** btp -- if the return value is SM_IO_EOF, *btp is set to 739 0 stevel ** the type of the boundary. 740 0 stevel ** 741 0 stevel ** Returns: 742 0 stevel ** The next character in the input stream. 743 0 stevel */ 744 0 stevel 745 0 stevel static int 746 0 stevel mime_getchar(fp, boundaries, btp) 747 0 stevel register SM_FILE_T *fp; 748 0 stevel char **boundaries; 749 0 stevel int *btp; 750 0 stevel { 751 0 stevel int c; 752 0 stevel static unsigned char *bp = NULL; 753 0 stevel static int buflen = 0; 754 0 stevel static bool atbol = true; /* at beginning of line */ 755 0 stevel static int bt = MBT_SYNTAX; /* boundary type of next SM_IO_EOF */ 756 0 stevel static unsigned char buf[128]; /* need not be a full line */ 757 0 stevel int start = 0; /* indicates position of - in buffer */ 758 0 stevel 759 0 stevel if (buflen == 1 && *bp == '\n') 760 0 stevel { 761 0 stevel /* last \n in buffer may be part of next MIME boundary */ 762 0 stevel c = *bp; 763 0 stevel } 764 0 stevel else if (buflen > 0) 765 0 stevel { 766 0 stevel buflen--; 767 0 stevel return *bp++; 768 0 stevel } 769 0 stevel else 770 0 stevel c = sm_io_getc(fp, SM_TIME_DEFAULT); 771 0 stevel bp = buf; 772 0 stevel buflen = 0; 773 0 stevel if (c == '\n') 774 0 stevel { 775 0 stevel /* might be part of a MIME boundary */ 776 0 stevel *bp++ = c; 777 0 stevel atbol = true; 778 0 stevel c = sm_io_getc(fp, SM_TIME_DEFAULT); 779 0 stevel if (c == '\n') 780 0 stevel { 781 0 stevel (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); 782 0 stevel return c; 783 0 stevel } 784 0 stevel start = 1; 785 0 stevel } 786 0 stevel if (c != SM_IO_EOF) 787 0 stevel *bp++ = c; 788 0 stevel else 789 0 stevel bt = MBT_FINAL; 790 0 stevel if (atbol && c == '-') 791 0 stevel { 792 0 stevel /* check for a message boundary */ 793 0 stevel c = sm_io_getc(fp, SM_TIME_DEFAULT); 794 0 stevel if (c != '-') 795 0 stevel { 796 0 stevel if (c != SM_IO_EOF) 797 0 stevel *bp++ = c; 798 0 stevel else 799 0 stevel bt = MBT_FINAL; 800 0 stevel buflen = bp - buf - 1; 801 0 stevel bp = buf; 802 0 stevel return *bp++; 803 0 stevel } 804 0 stevel 805 0 stevel /* got "--", now check for rest of separator */ 806 0 stevel *bp++ = '-'; 807 3544 jbeck while (bp < &buf[sizeof(buf) - 2] && 808 0 stevel (c = sm_io_getc(fp, SM_TIME_DEFAULT)) != SM_IO_EOF && 809 0 stevel c != '\n') 810 0 stevel { 811 0 stevel *bp++ = c; 812 0 stevel } 813 0 stevel *bp = '\0'; /* XXX simply cut off? */ 814 0 stevel bt = mimeboundary((char *) &buf[start], boundaries); 815 0 stevel switch (bt) 816 0 stevel { 817 0 stevel case MBT_FINAL: 818 0 stevel case MBT_INTERMED: 819 0 stevel /* we have a message boundary */ 820 0 stevel buflen = 0; 821 0 stevel *btp = bt; 822 0 stevel return SM_IO_EOF; 823 0 stevel } 824 0 stevel 825 3544 jbeck if (bp < &buf[sizeof(buf) - 2] && c != SM_IO_EOF) 826 0 stevel *bp++ = c; 827 0 stevel } 828 0 stevel 829 0 stevel atbol = c == '\n'; 830 0 stevel buflen = bp - buf - 1; 831 0 stevel if (buflen < 0) 832 0 stevel { 833 0 stevel *btp = bt; 834 0 stevel return SM_IO_EOF; 835 0 stevel } 836 0 stevel bp = buf; 837 0 stevel return *bp++; 838 0 stevel } 839 0 stevel /* 840 0 stevel ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF 841 0 stevel ** 842 0 stevel ** Parameters: 843 0 stevel ** fp -- the input file. 844 0 stevel ** boundaries -- the current MIME boundaries. 845 0 stevel ** btp -- if the return value is SM_IO_EOF, *btp is set to 846 0 stevel ** the type of the boundary. 847 0 stevel ** 848 0 stevel ** Returns: 849 0 stevel ** The next character in the input stream. 850 0 stevel */ 851 0 stevel 852 0 stevel static int 853 0 stevel mime_getchar_crlf(fp, boundaries, btp) 854 0 stevel register SM_FILE_T *fp; 855 0 stevel char **boundaries; 856 0 stevel int *btp; 857 0 stevel { 858 0 stevel static bool sendlf = false; 859 0 stevel int c; 860 0 stevel 861 0 stevel if (sendlf) 862 0 stevel { 863 0 stevel sendlf = false; 864 0 stevel return '\n'; 865 0 stevel } 866 0 stevel c = mime_getchar(fp, boundaries, btp); 867 0 stevel if (c == '\n' && MapNLtoCRLF) 868 0 stevel { 869 0 stevel sendlf = true; 870 0 stevel return '\r'; 871 0 stevel } 872 0 stevel return c; 873 0 stevel } 874 0 stevel /* 875 0 stevel ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type 876 0 stevel ** 877 0 stevel ** Parameters: 878 0 stevel ** line -- the input line. 879 0 stevel ** boundaries -- the set of currently pending boundaries. 880 0 stevel ** 881 0 stevel ** Returns: 882 0 stevel ** MBT_NOTSEP -- if this is not a separator line 883 0 stevel ** MBT_INTERMED -- if this is an intermediate separator 884 0 stevel ** MBT_FINAL -- if this is a final boundary 885 0 stevel ** MBT_SYNTAX -- if this is a boundary for the wrong 886 0 stevel ** enclosure -- i.e., a syntax error. 887 0 stevel */ 888 0 stevel 889 0 stevel static int 890 0 stevel mimeboundary(line, boundaries) 891 0 stevel register char *line; 892 0 stevel char **boundaries; 893 0 stevel { 894 0 stevel int type = MBT_NOTSEP; 895 0 stevel int i; 896 0 stevel int savec; 897 0 stevel 898 0 stevel if (line[0] != '-' || line[1] != '-' || boundaries == NULL) 899 0 stevel return MBT_NOTSEP; 900 0 stevel i = strlen(line); 901 0 stevel if (i > 0 && line[i - 1] == '\n') 902 0 stevel i--; 903 0 stevel 904 0 stevel /* strip off trailing whitespace */ 905 0 stevel while (i > 0 && (line[i - 1] == ' ' || line[i - 1] == '\t' 906 0 stevel #if _FFR_MIME_CR_OK 907 0 stevel || line[i - 1] == '\r' 908 0 stevel #endif /* _FFR_MIME_CR_OK */ 909 0 stevel )) 910 0 stevel i--; 911 0 stevel savec = line[i]; 912 0 stevel line[i] = '\0'; 913 0 stevel 914 0 stevel if (tTd(43, 5)) 915 0 stevel sm_dprintf("mimeboundary: line=\"%s\"... ", line); 916 0 stevel 917 0 stevel /* check for this as an intermediate boundary */ 918 0 stevel if (isboundary(&line[2], boundaries) >= 0) 919 0 stevel type = MBT_INTERMED; 920 0 stevel else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) 921 0 stevel { 922 0 stevel /* check for a final boundary */ 923 0 stevel line[i - 2] = '\0'; 924 0 stevel if (isboundary(&line[2], boundaries) >= 0) 925 0 stevel type = MBT_FINAL; 926 0 stevel line[i - 2] = '-'; 927 0 stevel } 928 0 stevel 929 0 stevel line[i] = savec; 930 0 stevel if (tTd(43, 5)) 931 0 stevel sm_dprintf("%s\n", MimeBoundaryNames[type]); 932 0 stevel return type; 933 0 stevel } 934 0 stevel /* 935 0 stevel ** DEFCHARSET -- return default character set for message 936 0 stevel ** 937 0 stevel ** The first choice for character set is for the mailer 938 0 stevel ** corresponding to the envelope sender. If neither that 939 0 stevel ** nor the global configuration file has a default character 940 0 stevel ** set defined, return "unknown-8bit" as recommended by 941 0 stevel ** RFC 1428 section 3. 942 0 stevel ** 943 0 stevel ** Parameters: 944 0 stevel ** e -- the envelope for this message. 945 0 stevel ** 946 0 stevel ** Returns: 947 0 stevel ** The default character set for that mailer. 948 0 stevel */ 949 0 stevel 950 0 stevel char * 951 0 stevel defcharset(e) 952 0 stevel register ENVELOPE *e; 953 0 stevel { 954 0 stevel if (e != NULL && e->e_from.q_mailer != NULL && 955 0 stevel e->e_from.q_mailer->m_defcharset != NULL) 956 0 stevel return e->e_from.q_mailer->m_defcharset; 957 0 stevel if (DefaultCharSet != NULL) 958 0 stevel return DefaultCharSet; 959 0 stevel return "unknown-8bit"; 960 0 stevel } 961 0 stevel /* 962 0 stevel ** ISBOUNDARY -- is a given string a currently valid boundary? 963 0 stevel ** 964 0 stevel ** Parameters: 965 0 stevel ** line -- the current input line. 966 0 stevel ** boundaries -- the list of valid boundaries. 967 0 stevel ** 968 0 stevel ** Returns: 969 0 stevel ** The index number in boundaries if the line is found. 970 0 stevel ** -1 -- otherwise. 971 0 stevel ** 972 0 stevel */ 973 0 stevel 974 0 stevel static int 975 0 stevel isboundary(line, boundaries) 976 0 stevel char *line; 977 0 stevel char **boundaries; 978 0 stevel { 979 0 stevel register int i; 980 0 stevel 981 0 stevel for (i = 0; i <= MAXMIMENESTING && boundaries[i] != NULL; i++) 982 0 stevel { 983 0 stevel if (strcmp(line, boundaries[i]) == 0) 984 0 stevel return i; 985 0 stevel } 986 0 stevel return -1; 987 0 stevel } 988 0 stevel #endif /* MIME8TO7 */ 989 0 stevel 990 0 stevel #if MIME7TO8 991 0 stevel static int mime_fromqp __P((unsigned char *, unsigned char **, int)); 992 0 stevel 993 0 stevel /* 994 0 stevel ** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format 995 0 stevel ** 996 0 stevel ** This is a hack. Supports translating the two 7-bit body-encodings 997 0 stevel ** (quoted-printable and base64) to 8-bit coded bodies. 998 0 stevel ** 999 0 stevel ** There is not much point in supporting multipart here, as the UA 1000 0 stevel ** will be able to deal with encoded MIME bodies if it can parse MIME 1001 0 stevel ** multipart messages. 1002 0 stevel ** 1003 0 stevel ** Note also that we won't be called unless it is a text/plain MIME 1004 0 stevel ** message, encoded base64 or QP and mailer flag '9' has been defined 1005 0 stevel ** on mailer. 1006 0 stevel ** 1007 0 stevel ** Contributed by Marius Olaffson <marius (at) rhi.hi.is>. 1008 0 stevel ** 1009 0 stevel ** Parameters: 1010 0 stevel ** mci -- mailer connection information. 1011 0 stevel ** header -- the header for this body part. 1012 0 stevel ** e -- envelope. 1013 0 stevel ** 1014 0 stevel ** Returns: 1015 1658 jbeck ** true iff body was written successfully 1016 0 stevel */ 1017 0 stevel 1018 0 stevel static char index_64[128] = 1019 0 stevel { 1020 0 stevel -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1021 0 stevel -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1022 0 stevel -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 1023 0 stevel 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, 1024 0 stevel -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 1025 0 stevel 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, 1026 0 stevel -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 1027 0 stevel 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 1028 0 stevel }; 1029 0 stevel 1030 0 stevel # define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) 1031 0 stevel 1032 1658 jbeck bool 1033 0 stevel mime7to8(mci, header, e) 1034 0 stevel register MCI *mci; 1035 0 stevel HDR *header; 1036 0 stevel register ENVELOPE *e; 1037 0 stevel { 1038 0 stevel int pxflags; 1039 0 stevel register char *p; 1040 0 stevel char *cte; 1041 0 stevel char **pvp; 1042 0 stevel unsigned char *fbufp; 1043 0 stevel char buf[MAXLINE]; 1044 0 stevel unsigned char fbuf[MAXLINE + 1]; 1045 0 stevel char pvpbuf[MAXLINE]; 1046 0 stevel extern unsigned char MimeTokenTab[256]; 1047 0 stevel 1048 0 stevel p = hvalue("Content-Transfer-Encoding", header); 1049 0 stevel if (p == NULL || 1050 3544 jbeck (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL, 1051 0 stevel MimeTokenTab, false)) == NULL || 1052 0 stevel pvp[0] == NULL) 1053 0 stevel { 1054 0 stevel /* "can't happen" -- upper level should have caught this */ 1055 0 stevel syserr("mime7to8: unparsable CTE %s", p == NULL ? "<NULL>" : p); 1056 0 stevel 1057 0 stevel /* avoid bounce loops */ 1058 0 stevel e->e_flags |= EF_DONT_MIME; 1059 0 stevel 1060 0 stevel /* cheap failsafe algorithm -- should work on text/plain */ 1061 0 stevel if (p != NULL) 1062 0 stevel { 1063 3544 jbeck (void) sm_snprintf(buf, sizeof(buf), 1064 0 stevel "Content-Transfer-Encoding: %s", p); 1065 1658 jbeck if (!putline(buf, mci)) 1066 1658 jbeck goto writeerr; 1067 0 stevel } 1068 1658 jbeck if (!putline("", mci)) 1069 1658 jbeck goto writeerr; 1070 0 stevel mci->mci_flags &= ~MCIF_INHEADER; 1071 3544 jbeck while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf)) 1072 0 stevel != NULL) 1073 1658 jbeck { 1074 1658 jbeck if (!putline(buf, mci)) 1075 1658 jbeck goto writeerr; 1076 1658 jbeck } 1077 1658 jbeck return true; 1078 0 stevel } 1079 3544 jbeck cataddr(pvp, NULL, buf, sizeof(buf), '\0', false); 1080 0 stevel cte = sm_rpool_strdup_x(e->e_rpool, buf); 1081 0 stevel 1082 0 stevel mci->mci_flags |= MCIF_INHEADER; 1083 1658 jbeck if (!putline("Content-Transfer-Encoding: 8bit", mci)) 1084 1658 jbeck goto writeerr; 1085 3544 jbeck (void) sm_snprintf(buf, sizeof(buf), 1086 0 stevel "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s", 1087 0 stevel cte, MyHostName, e->e_id); 1088 1658 jbeck if (!putline(buf, mci) || !putline("", mci)) 1089 1658 jbeck goto writeerr; 1090 0 stevel mci->mci_flags &= ~MCIF_INHEADER; 1091 0 stevel 1092 0 stevel /* 1093 0 stevel ** Translate body encoding to 8-bit. Supports two types of 1094 0 stevel ** encodings; "base64" and "quoted-printable". Assume qp if 1095 0 stevel ** it is not base64. 1096 0 stevel */ 1097 0 stevel 1098 0 stevel pxflags = PXLF_MAPFROM; 1099 0 stevel if (sm_strcasecmp(cte, "base64") == 0) 1100 0 stevel { 1101 0 stevel int c1, c2, c3, c4; 1102 0 stevel 1103 0 stevel fbufp = fbuf; 1104 0 stevel while ((c1 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != 1105 0 stevel SM_IO_EOF) 1106 0 stevel { 1107 0 stevel if (isascii(c1) && isspace(c1)) 1108 0 stevel continue; 1109 0 stevel 1110 0 stevel do 1111 0 stevel { 1112 0 stevel c2 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); 1113 0 stevel } while (isascii(c2) && isspace(c2)); 1114 0 stevel if (c2 == SM_IO_EOF) 1115 0 stevel break; 1116 0 stevel 1117 0 stevel do 1118 0 stevel { 1119 0 stevel c3 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); 1120 0 stevel } while (isascii(c3) && isspace(c3)); 1121 0 stevel if (c3 == SM_IO_EOF) 1122 0 stevel break; 1123 0 stevel 1124 0 stevel do 1125 0 stevel { 1126 0 stevel c4 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); 1127 0 stevel } while (isascii(c4) && isspace(c4)); 1128 0 stevel if (c4 == SM_IO_EOF) 1129 0 stevel break; 1130 0 stevel 1131 0 stevel if (c1 == '=' || c2 == '=') 1132 0 stevel continue; 1133 0 stevel c1 = CHAR64(c1); 1134 0 stevel c2 = CHAR64(c2); 1135 0 stevel 1136 0 stevel #if MIME7TO8_OLD 1137 0 stevel #define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \ 1138 0 stevel ++fbufp; 1139 0 stevel #else /* MIME7TO8_OLD */ 1140 0 stevel #define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \ 1141 0 stevel { \ 1142 0 stevel ++fbufp; \ 1143 0 stevel pxflags |= PXLF_NOADDEOL; \ 1144 0 stevel } 1145 0 stevel #endif /* MIME7TO8_OLD */ 1146 0 stevel 1147 0 stevel #define PUTLINE64 \ 1148 0 stevel do \ 1149 0 stevel { \ 1150 0 stevel if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) \ 1151 0 stevel { \ 1152 0 stevel CHK_EOL; \ 1153 1658 jbeck if (!putxline((char *) fbuf, fbufp - fbuf, mci, pxflags)) \ 1154 1658 jbeck goto writeerr; \ 1155 0 stevel pxflags &= ~PXLF_NOADDEOL; \ 1156 0 stevel fbufp = fbuf; \ 1157 0 stevel } \ 1158 0 stevel } while (0) 1159 0 stevel 1160 0 stevel *fbufp = (c1 << 2) | ((c2 & 0x30) >> 4); 1161 0 stevel PUTLINE64; 1162 0 stevel if (c3 == '=') 1163 0 stevel continue; 1164 0 stevel c3 = CHAR64(c3); 1165 0 stevel *fbufp = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); 1166 0 stevel PUTLINE64; 1167 0 stevel if (c4 == '=') 1168 0 stevel continue; 1169 0 stevel c4 = CHAR64(c4); 1170 0 stevel *fbufp = ((c3 & 0x03) << 6) | c4; 1171 0 stevel PUTLINE64; 1172 0 stevel } 1173 0 stevel } 1174 0 stevel else 1175 0 stevel { 1176 0 stevel int off; 1177 0 stevel 1178 0 stevel /* quoted-printable */ 1179 0 stevel pxflags |= PXLF_NOADDEOL; 1180 0 stevel fbufp = fbuf; 1181 0 stevel while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, 1182 3544 jbeck sizeof(buf)) != NULL) 1183 0 stevel { 1184 0 stevel off = mime_fromqp((unsigned char *) buf, &fbufp, 1185 0 stevel &fbuf[MAXLINE] - fbufp); 1186 0 stevel again: 1187 0 stevel if (off < -1) 1188 0 stevel continue; 1189 0 stevel 1190 0 stevel if (fbufp - fbuf > 0) 1191 1658 jbeck { 1192 1658 jbeck if (!putxline((char *) fbuf, fbufp - fbuf - 1, 1193 1658 jbeck mci, pxflags)) 1194 1658 jbeck goto writeerr; 1195 1658 jbeck } 1196 0 stevel fbufp = fbuf; 1197 0 stevel if (off >= 0 && buf[off] != '\0') 1198 0 stevel { 1199 0 stevel off = mime_fromqp((unsigned char *) (buf + off), 1200 0 stevel &fbufp, 1201 0 stevel &fbuf[MAXLINE] - fbufp); 1202 0 stevel goto again; 1203 0 stevel } 1204 0 stevel } 1205 0 stevel } 1206 0 stevel 1207 0 stevel /* force out partial last line */ 1208 0 stevel if (fbufp > fbuf) 1209 0 stevel { 1210 0 stevel *fbufp = '\0'; 1211 1658 jbeck if (!putxline((char *) fbuf, fbufp - fbuf, mci, pxflags)) 1212 1658 jbeck goto writeerr; 1213 0 stevel } 1214 0 stevel 1215 0 stevel /* 1216 0 stevel ** The decoded text may end without an EOL. Since this function 1217 0 stevel ** is only called for text/plain MIME messages, it is safe to 1218 0 stevel ** add an extra one at the end just in case. This is a hack, 1219 0 stevel ** but so is auto-converting MIME in the first place. 1220 0 stevel */ 1221 0 stevel 1222 1658 jbeck if (!putline("", mci)) 1223 1658 jbeck goto writeerr; 1224 0 stevel 1225 0 stevel if (tTd(43, 3)) 1226 0 stevel sm_dprintf("\t\t\tmime7to8 => %s to 8bit done\n", cte); 1227 1658 jbeck return true; 1228 1658 jbeck 1229 1658 jbeck writeerr: 1230 1658 jbeck return false; 1231 0 stevel } 1232 0 stevel /* 1233 0 stevel ** The following is based on Borenstein's "codes.c" module, with simplifying 1234 0 stevel ** changes as we do not deal with multipart, and to do the translation in-core, 1235 0 stevel ** with an attempt to prevent overrun of output buffers. 1236 0 stevel ** 1237 0 stevel ** What is needed here are changes to defend this code better against 1238 0 stevel ** bad encodings. Questionable to always return 0xFF for bad mappings. 1239 0 stevel */ 1240 0 stevel 1241 0 stevel static char index_hex[128] = 1242 0 stevel { 1243 0 stevel -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1244 0 stevel -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1245 0 stevel -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1246 0 stevel 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, 1247 0 stevel -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1248 0 stevel -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1249 0 stevel -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1250 0 stevel -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 1251 0 stevel }; 1252 0 stevel 1253 0 stevel # define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)]) 1254 0 stevel 1255 0 stevel /* 1256 0 stevel ** MIME_FROMQP -- decode quoted printable string 1257 0 stevel ** 1258 0 stevel ** Parameters: 1259 0 stevel ** infile -- input (encoded) string 1260 0 stevel ** outfile -- output string 1261 0 stevel ** maxlen -- size of output buffer 1262 0 stevel ** 1263 0 stevel ** Returns: 1264 0 stevel ** -2 if decoding failure 1265 0 stevel ** -1 if infile completely decoded into outfile 1266 0 stevel ** >= 0 is the position in infile decoding 1267 0 stevel ** reached before maxlen was reached 1268 0 stevel */ 1269 0 stevel 1270 0 stevel static int 1271 0 stevel mime_fromqp(infile, outfile, maxlen) 1272 0 stevel unsigned char *infile; 1273 0 stevel unsigned char **outfile; 1274 0 stevel int maxlen; /* Max # of chars allowed in outfile */ 1275 0 stevel { 1276 0 stevel int c1, c2; 1277 0 stevel int nchar = 0; 1278 0 stevel unsigned char *b; 1279 0 stevel 1280 0 stevel /* decrement by one for trailing '\0', at least one other char */ 1281 0 stevel if (--maxlen < 1) 1282 0 stevel return 0; 1283 0 stevel 1284 0 stevel b = infile; 1285 0 stevel while ((c1 = *infile++) != '\0' && nchar < maxlen) 1286 0 stevel { 1287 0 stevel if (c1 == '=') 1288 0 stevel { 1289 0 stevel if ((c1 = *infile++) == '\0') 1290 0 stevel break; 1291 0 stevel 1292 0 stevel if (c1 == '\n' || (c1 = HEXCHAR(c1)) == -1) 1293 0 stevel { 1294 0 stevel /* ignore it and the rest of the buffer */ 1295 0 stevel return -2; 1296 0 stevel } 1297 0 stevel else 1298 0 stevel { 1299 0 stevel do 1300 0 stevel { 1301 0 stevel if ((c2 = *infile++) == '\0') 1302 0 stevel { 1303 0 stevel c2 = -1; 1304 0 stevel break; 1305 0 stevel } 1306 0 stevel } while ((c2 = HEXCHAR(c2)) == -1); 1307 0 stevel 1308 0 stevel if (c2 == -1) 1309 0 stevel break; 1310 0 stevel nchar++; 1311 0 stevel *(*outfile)++ = c1 << 4 | c2; 1312 0 stevel } 1313 0 stevel } 1314 0 stevel else 1315 0 stevel { 1316 0 stevel nchar++; 1317 0 stevel *(*outfile)++ = c1; 1318 0 stevel if (c1 == '\n') 1319 0 stevel break; 1320 0 stevel } 1321 0 stevel } 1322 0 stevel *(*outfile)++ = '\0'; 1323 0 stevel if (nchar >= maxlen) 1324 0 stevel return (infile - b - 1); 1325 0 stevel return -1; 1326 0 stevel } 1327 0 stevel #endif /* MIME7TO8 */ 1328