1 /* 2 * Copyright (c) 1994 Eric P. Allman 3 * Copyright (c) 1994 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 # include "sendmail.h" 10 # include <string.h> 11 12 #ifndef lint 13 static char sccsid[] = "@(#)mime.c 8.21 (Berkeley) 05/15/95"; 14 #endif /* not lint */ 15 16 /* 17 ** MIME support. 18 ** 19 ** I am indebted to John Beck of Hewlett-Packard, who contributed 20 ** his code to me for inclusion. As it turns out, I did not use 21 ** his code since he used a "minimum change" approach that used 22 ** several temp files, and I wanted a "minimum impact" approach 23 ** that would avoid copying. However, looking over his code 24 ** helped me cement my understanding of the problem. 25 ** 26 ** I also looked at, but did not directly use, Nathaniel 27 ** Borenstein's "code.c" module. Again, it functioned as 28 ** a file-to-file translator, which did not fit within my 29 ** design bounds, but it was a useful base for understanding 30 ** the problem. 31 */ 32 33 34 /* character set for hex and base64 encoding */ 35 char Base16Code[] = "0123456789ABCDEF"; 36 char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 37 38 /* types of MIME boundaries */ 39 #define MBT_SYNTAX 0 /* syntax error */ 40 #define MBT_NOTSEP 1 /* not a boundary */ 41 #define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */ 42 #define MBT_FINAL 3 /* final boundary (trailing -- included) */ 43 44 static char *MimeBoundaryNames[] = 45 { 46 "SYNTAX", "NOTSEP", "INTERMED", "FINAL" 47 }; 48 /* 49 ** MIME8TO7 -- output 8 bit body in 7 bit format 50 ** 51 ** The header has already been output -- this has to do the 52 ** 8 to 7 bit conversion. It would be easy if we didn't have 53 ** to deal with nested formats (multipart/xxx and message/rfc822). 54 ** 55 ** We won't be called if we don't have to do a conversion, and 56 ** appropriate MIME-Version: and Content-Type: fields have been 57 ** output. Any Content-Transfer-Encoding: field has not been 58 ** output, and we can add it here. 59 ** 60 ** Parameters: 61 ** mci -- mailer connection information. 62 ** header -- the header for this body part. 63 ** e -- envelope. 64 ** boundaries -- the currently pending message boundaries. 65 ** NULL if we are processing the outer portion. 66 ** flags -- to tweak processing. 67 ** 68 ** Returns: 69 ** An indicator of what terminated the message part: 70 ** MBT_FINAL -- the final boundary 71 ** MBT_INTERMED -- an intermediate boundary 72 ** MBT_NOTSEP -- an end of file 73 */ 74 75 struct args 76 { 77 char *field; /* name of field */ 78 char *value; /* value of that field */ 79 }; 80 81 int 82 mime8to7(mci, header, e, boundaries, flags) 83 register MCI *mci; 84 HDR *header; 85 register ENVELOPE *e; 86 char **boundaries; 87 int flags; 88 { 89 register char *p; 90 int linelen; 91 int bt; 92 off_t offset; 93 size_t sectionsize, sectionhighbits; 94 int i; 95 char *type; 96 char *subtype; 97 char *cte; 98 char **pvp; 99 int argc = 0; 100 char *bp; 101 struct args argv[MAXMIMEARGS]; 102 char bbuf[128]; 103 char buf[MAXLINE]; 104 char pvpbuf[MAXLINE]; 105 extern char MimeTokenTab[256]; 106 107 if (tTd(43, 1)) 108 { 109 printf("mime8to7: flags = %x, boundaries =", flags); 110 if (boundaries[0] == NULL) 111 printf(" <none>"); 112 else 113 { 114 for (i = 0; boundaries[i] != NULL; i++) 115 printf(" %s", boundaries[i]); 116 } 117 printf("\n"); 118 } 119 p = hvalue("Content-Transfer-Encoding", header); 120 if (p == NULL || 121 (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, 122 MimeTokenTab)) == NULL || 123 pvp[0] == NULL) 124 { 125 cte = NULL; 126 } 127 else 128 { 129 cataddr(pvp, NULL, buf, sizeof buf, '\0'); 130 cte = newstr(buf); 131 } 132 133 type = subtype = NULL; 134 p = hvalue("Content-Type", header); 135 if (p == NULL) 136 { 137 if (bitset(M87F_DIGEST, flags)) 138 p = "message/rfc822"; 139 else 140 p = "text/plain"; 141 } 142 if (p != NULL && 143 (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, 144 MimeTokenTab)) != NULL && 145 pvp[0] != NULL) 146 { 147 if (tTd(43, 40)) 148 { 149 for (i = 0; pvp[i] != NULL; i++) 150 printf("pvp[%d] = \"%s\"\n", i, pvp[i]); 151 } 152 type = *pvp++; 153 if (*pvp != NULL && strcmp(*pvp, "/") == 0 && 154 *++pvp != NULL) 155 { 156 subtype = *pvp++; 157 } 158 159 /* break out parameters */ 160 while (*pvp != NULL && argc < MAXMIMEARGS) 161 { 162 /* skip to semicolon separator */ 163 while (*pvp != NULL && strcmp(*pvp, ";") != 0) 164 pvp++; 165 if (*pvp++ == NULL || *pvp == NULL) 166 break; 167 168 /* extract field name */ 169 argv[argc].field = *pvp++; 170 171 /* see if there is a value */ 172 if (*pvp != NULL && strcmp(*pvp, "=") == 0 && 173 (*++pvp == NULL || strcmp(*pvp, ";") != 0)) 174 { 175 argv[argc].value = *pvp; 176 argc++; 177 } 178 } 179 } 180 181 /* check for disaster cases */ 182 if (type == NULL) 183 type = "-none-"; 184 if (subtype == NULL) 185 subtype = "-none-"; 186 187 /* don't propogate some flags more than one level into the message */ 188 flags &= ~M87F_DIGEST; 189 190 /* 191 ** Check for cases that can not be encoded. 192 ** 193 ** For example, you can't encode certain kinds of types 194 ** or already-encoded messages. If we find this case, 195 ** just copy it through. 196 */ 197 198 sprintf(buf, "%s/%s", type, subtype); 199 if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) 200 flags |= M87F_NO8BIT; 201 202 /* 203 ** Multipart requires special processing. 204 ** 205 ** Do a recursive descent into the message. 206 */ 207 208 if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags)) 209 { 210 int blen; 211 212 if (strcasecmp(subtype, "digest") == 0) 213 flags |= M87F_DIGEST; 214 215 for (i = 0; i < argc; i++) 216 { 217 if (strcasecmp(argv[i].field, "boundary") == 0) 218 break; 219 } 220 if (i >= argc) 221 { 222 syserr("mime8to7: Content-Type: %s missing boundary", p); 223 p = "---"; 224 } 225 else 226 { 227 p = argv[i].value; 228 stripquotes(p); 229 } 230 blen = strlen(p); 231 if (blen > sizeof bbuf - 1) 232 { 233 syserr("mime8to7: multipart boundary \"%s\" too long", 234 p); 235 blen = sizeof bbuf - 1; 236 } 237 strncpy(bbuf, p, blen); 238 bbuf[blen] = '\0'; 239 if (tTd(43, 1)) 240 printf("mime8to7: multipart boundary \"%s\"\n", bbuf); 241 for (i = 0; i < MAXMIMENESTING; i++) 242 if (boundaries[i] == NULL) 243 break; 244 if (i >= MAXMIMENESTING) 245 syserr("mime8to7: multipart nesting boundary too deep"); 246 else 247 { 248 boundaries[i] = bbuf; 249 boundaries[i + 1] = NULL; 250 } 251 mci->mci_flags |= MCIF_INMIME; 252 253 /* skip the early "comment" prologue */ 254 putline("", mci); 255 while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 256 { 257 bt = mimeboundary(buf, boundaries); 258 if (bt != MBT_NOTSEP) 259 break; 260 putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); 261 if (tTd(43, 99)) 262 printf(" ...%s", buf); 263 } 264 if (feof(e->e_dfp)) 265 bt = MBT_FINAL; 266 while (bt != MBT_FINAL) 267 { 268 auto HDR *hdr = NULL; 269 270 sprintf(buf, "--%s", bbuf); 271 putline(buf, mci); 272 if (tTd(43, 35)) 273 printf(" ...%s\n", buf); 274 collect(e->e_dfp, FALSE, FALSE, &hdr, e); 275 if (tTd(43, 101)) 276 putline("+++after collect", mci); 277 putheader(mci, hdr, e); 278 if (tTd(43, 101)) 279 putline("+++after putheader", mci); 280 bt = mime8to7(mci, hdr, e, boundaries, flags); 281 } 282 sprintf(buf, "--%s--", bbuf); 283 putline(buf, mci); 284 if (tTd(43, 35)) 285 printf(" ...%s\n", buf); 286 boundaries[i] = NULL; 287 mci->mci_flags &= ~MCIF_INMIME; 288 289 /* skip the late "comment" epilogue */ 290 while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 291 { 292 bt = mimeboundary(buf, boundaries); 293 if (bt != MBT_NOTSEP) 294 break; 295 putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); 296 if (tTd(43, 99)) 297 printf(" ...%s", buf); 298 } 299 if (feof(e->e_dfp)) 300 bt = MBT_FINAL; 301 if (tTd(43, 3)) 302 printf("\t\t\tmime8to7=>%s (multipart)\n", 303 MimeBoundaryNames[bt]); 304 return bt; 305 } 306 307 /* 308 ** Message/* types -- recurse exactly once. 309 */ 310 311 if (strcasecmp(type, "message") == 0) 312 { 313 if (strcasecmp(subtype, "rfc822") != 0) 314 { 315 flags |= M87F_NO8BIT; 316 } 317 else 318 { 319 register char *q; 320 auto HDR *hdr = NULL; 321 322 putline("", mci); 323 324 mci->mci_flags |= MCIF_INMIME; 325 collect(e->e_dfp, FALSE, FALSE, &hdr, e); 326 if (tTd(43, 101)) 327 putline("+++after collect", mci); 328 putheader(mci, hdr, e); 329 if (tTd(43, 101)) 330 putline("+++after putheader", mci); 331 bt = mime8to7(mci, hdr, e, boundaries, flags); 332 mci->mci_flags &= ~MCIF_INMIME; 333 return bt; 334 } 335 } 336 337 /* 338 ** Non-compound body type 339 ** 340 ** Compute the ratio of seven to eight bit characters; 341 ** use that as a heuristic to decide how to do the 342 ** encoding. 343 */ 344 345 sectionsize = sectionhighbits = 0; 346 if (!bitset(M87F_NO8BIT, flags)) 347 { 348 /* remember where we were */ 349 offset = ftell(e->e_dfp); 350 if (offset == -1) 351 syserr("mime8to7: cannot ftell on df%s", e->e_id); 352 353 /* do a scan of this body type to count character types */ 354 while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 355 { 356 if (mimeboundary(buf, boundaries) != MBT_NOTSEP) 357 break; 358 for (p = buf; *p != '\0'; p++) 359 { 360 /* count bytes with the high bit set */ 361 sectionsize++; 362 if (bitset(0200, *p)) 363 sectionhighbits++; 364 } 365 366 /* 367 ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, 368 ** assume base64. This heuristic avoids double-reading 369 ** large graphics or video files. 370 */ 371 372 if (sectionsize >= 4096 && 373 sectionhighbits > sectionsize / 4) 374 break; 375 } 376 377 /* return to the original offset for processing */ 378 /* XXX use relative seeks to handle >31 bit file sizes? */ 379 if (fseek(e->e_dfp, offset, SEEK_SET) < 0) 380 syserr("mime8to7: cannot fseek on df%s", e->e_id); 381 else 382 clearerr(e->e_dfp); 383 } 384 385 /* 386 ** Heuristically determine encoding method. 387 ** If more than 1/8 of the total characters have the 388 ** eighth bit set, use base64; else use quoted-printable. 389 ** However, only encode binary encoded data as base64, 390 ** since otherwise the NL=>CRLF mapping will be a problem. 391 */ 392 393 if (tTd(43, 8)) 394 { 395 printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s\n", 396 sectionhighbits, sectionsize, 397 cte == NULL ? "[none]" : cte); 398 } 399 if (cte != NULL && strcasecmp(cte, "binary") == 0) 400 sectionsize = sectionhighbits; 401 linelen = 0; 402 bp = buf; 403 if (sectionhighbits == 0) 404 { 405 /* no encoding necessary */ 406 if (cte != NULL) 407 { 408 sprintf(buf, "Content-Transfer-Encoding: %s", cte); 409 putline(buf, mci); 410 if (tTd(43, 36)) 411 printf(" ...%s\n", buf); 412 } 413 putline("", mci); 414 mci->mci_flags &= ~MCIF_INHEADER; 415 while (fgets(buf, sizeof buf, e->e_dfp) != NULL) 416 { 417 bt = mimeboundary(buf, boundaries); 418 if (bt != MBT_NOTSEP) 419 break; 420 putline(buf, mci); 421 } 422 if (feof(e->e_dfp)) 423 bt = MBT_FINAL; 424 } 425 else if (sectionsize / 8 < sectionhighbits) 426 { 427 /* use base64 encoding */ 428 int c1, c2; 429 430 putline("Content-Transfer-Encoding: base64", mci); 431 if (tTd(43, 36)) 432 printf(" ...Content-Transfer-Encoding: base64\n"); 433 putline("", mci); 434 mci->mci_flags &= ~MCIF_INHEADER; 435 while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF) 436 { 437 if (linelen > 71) 438 { 439 *bp = '\0'; 440 putline(buf, mci); 441 linelen = 0; 442 bp = buf; 443 } 444 linelen += 4; 445 *bp++ = Base64Code[(c1 >> 2)]; 446 c1 = (c1 & 0x03) << 4; 447 c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 448 if (c2 == EOF) 449 { 450 *bp++ = Base64Code[c1]; 451 *bp++ = '='; 452 *bp++ = '='; 453 break; 454 } 455 c1 |= (c2 >> 4) & 0x0f; 456 *bp++ = Base64Code[c1]; 457 c1 = (c2 & 0x0f) << 2; 458 c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); 459 if (c2 == EOF) 460 { 461 *bp++ = Base64Code[c1]; 462 *bp++ = '='; 463 break; 464 } 465 c1 |= (c2 >> 6) & 0x03; 466 *bp++ = Base64Code[c1]; 467 *bp++ = Base64Code[c2 & 0x3f]; 468 } 469 } 470 else 471 { 472 /* use quoted-printable encoding */ 473 int c1, c2; 474 int fromstate; 475 BITMAP badchars; 476 477 /* set up map of characters that must be mapped */ 478 clrbitmap(badchars); 479 for (c1 = 0x00; c1 < 0x20; c1++) 480 setbitn(c1, badchars); 481 clrbitn('\t', badchars); 482 for (c1 = 0x7f; c1 < 0x100; c1++) 483 setbitn(c1, badchars); 484 setbitn('=', badchars); 485 if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) 486 for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) 487 setbitn(*p, badchars); 488 489 putline("Content-Transfer-Encoding: quoted-printable", mci); 490 if (tTd(43, 36)) 491 printf(" ...Content-Transfer-Encoding: quoted-printable\n"); 492 putline("", mci); 493 mci->mci_flags &= ~MCIF_INHEADER; 494 fromstate = 0; 495 c2 = '\n'; 496 while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF) 497 { 498 if (c1 == '\n') 499 { 500 if (c2 == ' ' || c2 == '\t') 501 { 502 *bp++ = '='; 503 *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 504 *bp++ = Base16Code[c2 & 0x0f]; 505 *bp = '\0'; 506 putline(buf, mci); 507 bp = buf; 508 } 509 *bp = '\0'; 510 putline(buf, mci); 511 linelen = fromstate = 0; 512 bp = buf; 513 c2 = c1; 514 continue; 515 } 516 if (c2 == ' ' && linelen == 4 && fromstate == 4 && 517 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 518 { 519 *bp++ = '='; 520 *bp++ = '2'; 521 *bp++ = '0'; 522 linelen += 3; 523 } 524 else if (c2 == ' ' || c2 == '\t') 525 { 526 *bp++ = c2; 527 linelen++; 528 } 529 if (linelen > 72) 530 { 531 *bp++ = '='; 532 *bp = '\0'; 533 putline(buf, mci); 534 linelen = fromstate = 0; 535 bp = buf; 536 c2 = '\n'; 537 } 538 if (bitnset(c1 & 0xff, badchars)) 539 { 540 *bp++ = '='; 541 *bp++ = Base16Code[(c1 >> 4) & 0x0f]; 542 *bp++ = Base16Code[c1 & 0x0f]; 543 linelen += 3; 544 } 545 else if (c1 != ' ' && c1 != '\t') 546 { 547 if (linelen < 4 && c1 == "From"[linelen]) 548 fromstate++; 549 *bp++ = c1; 550 linelen++; 551 } 552 c2 = c1; 553 } 554 555 /* output any saved character */ 556 if (c2 == ' ' || c2 == '\t') 557 { 558 *bp++ = '='; 559 *bp++ = Base16Code[(c2 >> 4) & 0x0f]; 560 *bp++ = Base16Code[c2 & 0x0f]; 561 linelen += 3; 562 } 563 } 564 if (linelen > 0) 565 { 566 *bp = '\0'; 567 putline(buf, mci); 568 } 569 if (tTd(43, 3)) 570 printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); 571 return bt; 572 } 573 /* 574 ** MIME_GETCHAR -- get a character for MIME processing 575 ** 576 ** Treats boundaries as EOF. 577 ** 578 ** Parameters: 579 ** fp -- the input file. 580 ** boundaries -- the current MIME boundaries. 581 ** btp -- if the return value is EOF, *btp is set to 582 ** the type of the boundary. 583 ** 584 ** Returns: 585 ** The next character in the input stream. 586 */ 587 588 int 589 mime_getchar(fp, boundaries, btp) 590 register FILE *fp; 591 char **boundaries; 592 int *btp; 593 { 594 int c; 595 static u_char *bp = NULL; 596 static int buflen = 0; 597 static bool atbol = TRUE; /* at beginning of line */ 598 static int bt = MBT_SYNTAX; /* boundary type of next EOF */ 599 static u_char buf[128]; /* need not be a full line */ 600 601 if (buflen > 0) 602 { 603 buflen--; 604 return *bp++; 605 } 606 bp = buf; 607 buflen = 0; 608 c = getc(fp); 609 if (c == '\n') 610 { 611 /* might be part of a MIME boundary */ 612 *bp++ = c; 613 atbol = TRUE; 614 c = getc(fp); 615 if (c == '\n') 616 { 617 ungetc(c, fp); 618 return c; 619 } 620 } 621 if (c != EOF) 622 *bp++ = c; 623 else 624 bt = MBT_FINAL; 625 if (atbol && c == '-') 626 { 627 /* check for a message boundary */ 628 c = getc(fp); 629 if (c != '-') 630 { 631 if (c != EOF) 632 *bp++ = c; 633 else 634 bt = MBT_FINAL; 635 buflen = bp - buf - 1; 636 bp = buf; 637 return *bp++; 638 } 639 640 /* got "--", now check for rest of separator */ 641 *bp++ = '-'; 642 while (bp < &buf[sizeof buf - 2] && 643 (c = getc(fp)) != EOF && c != '\n') 644 { 645 *bp++ = c; 646 } 647 *bp = '\0'; 648 bt = mimeboundary(&buf[1], boundaries); 649 switch (bt) 650 { 651 case MBT_FINAL: 652 case MBT_INTERMED: 653 /* we have a message boundary */ 654 buflen = 0; 655 *btp = bt; 656 return EOF; 657 } 658 659 atbol = c == '\n'; 660 if (c != EOF) 661 *bp++ = c; 662 } 663 664 buflen = bp - buf - 1; 665 if (buflen < 0) 666 { 667 *btp = bt; 668 return EOF; 669 } 670 bp = buf; 671 return *bp++; 672 } 673 /* 674 ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF 675 ** 676 ** Parameters: 677 ** fp -- the input file. 678 ** boundaries -- the current MIME boundaries. 679 ** btp -- if the return value is EOF, *btp is set to 680 ** the type of the boundary. 681 ** 682 ** Returns: 683 ** The next character in the input stream. 684 */ 685 686 int 687 mime_getchar_crlf(fp, boundaries, btp) 688 register FILE *fp; 689 char **boundaries; 690 int *btp; 691 { 692 static bool sendlf = FALSE; 693 int c; 694 695 if (sendlf) 696 { 697 sendlf = FALSE; 698 return '\n'; 699 } 700 c = mime_getchar(fp, boundaries, btp); 701 if (c == '\n') 702 { 703 sendlf = TRUE; 704 return '\r'; 705 } 706 return c; 707 } 708 /* 709 ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type 710 ** 711 ** Parameters: 712 ** line -- the input line. 713 ** boundaries -- the set of currently pending boundaries. 714 ** 715 ** Returns: 716 ** MBT_NOTSEP -- if this is not a separator line 717 ** MBT_INTERMED -- if this is an intermediate separator 718 ** MBT_FINAL -- if this is a final boundary 719 ** MBT_SYNTAX -- if this is a boundary for the wrong 720 ** enclosure -- i.e., a syntax error. 721 */ 722 723 int 724 mimeboundary(line, boundaries) 725 register char *line; 726 char **boundaries; 727 { 728 int type = MBT_NOTSEP; 729 int i; 730 int savec; 731 732 if (line[0] != '-' || line[1] != '-' || boundaries == NULL) 733 return MBT_NOTSEP; 734 i = strlen(line); 735 if (line[i - 1] == '\n') 736 i--; 737 738 /* strip off trailing whitespace */ 739 while (line[i - 1] == ' ' || line[i - 1] == '\t') 740 i--; 741 savec = line[i]; 742 line[i] = '\0'; 743 744 if (tTd(43, 5)) 745 printf("mimeboundary: line=\"%s\"... ", line); 746 747 /* check for this as an intermediate boundary */ 748 if (isboundary(&line[2], boundaries) >= 0) 749 type = MBT_INTERMED; 750 else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) 751 { 752 /* check for a final boundary */ 753 line[i - 2] = '\0'; 754 if (isboundary(&line[2], boundaries) >= 0) 755 type = MBT_FINAL; 756 line[i - 2] = '-'; 757 } 758 759 line[i] = savec; 760 if (tTd(43, 5)) 761 printf("%s\n", MimeBoundaryNames[type]); 762 return type; 763 } 764 /* 765 ** DEFCHARSET -- return default character set for message 766 ** 767 ** The first choice for character set is for the mailer 768 ** corresponding to the envelope sender. If neither that 769 ** nor the global configuration file has a default character 770 ** set defined, return "unknown-8bit" as recommended by 771 ** RFC 1428 section 3. 772 ** 773 ** Parameters: 774 ** e -- the envelope for this message. 775 ** 776 ** Returns: 777 ** The default character set for that mailer. 778 */ 779 780 char * 781 defcharset(e) 782 register ENVELOPE *e; 783 { 784 if (e != NULL && e->e_from.q_mailer != NULL && 785 e->e_from.q_mailer->m_defcharset != NULL) 786 return e->e_from.q_mailer->m_defcharset; 787 if (DefaultCharSet != NULL) 788 return DefaultCharSet; 789 return "unknown-8bit"; 790 } 791 /* 792 ** ISBOUNDARY -- is a given string a currently valid boundary? 793 ** 794 ** Parameters: 795 ** line -- the current input line. 796 ** boundaries -- the list of valid boundaries. 797 ** 798 ** Returns: 799 ** The index number in boundaries if the line is found. 800 ** -1 -- otherwise. 801 ** 802 */ 803 804 int 805 isboundary(line, boundaries) 806 char *line; 807 char **boundaries; 808 { 809 register int i; 810 811 for (i = 0; boundaries[i] != NULL; i++) 812 { 813 if (strcmp(line, boundaries[i]) == 0) 814 return i; 815 } 816 return -1; 817 } 818