1 # include <stdio.h> 2 # include <pwd.h> 3 # include <signal.h> 4 # include <ctype.h> 5 # include "dlvrmail.h" 6 # ifdef LOG 7 # include <syslog.h> 8 # endif LOG 9 10 static char SccsId[] = "@(#)deliver.c 3.1 03/04/81"; 11 12 /* 13 ** DELIVER -- Deliver a message to a particular address. 14 ** 15 ** Algorithm: 16 ** Compute receiving network (i.e., mailer), host, & user. 17 ** If local, see if this is really a program name. 18 ** Build argument for the mailer. 19 ** Create pipe through edit fcn if appropriate. 20 ** Fork. 21 ** Child: call mailer 22 ** Parent: call editfcn if specified. 23 ** Wait for mailer to finish. 24 ** Interpret exit status. 25 ** 26 ** Parameters: 27 ** to -- the address to deliver the message to. 28 ** editfcn -- if non-NULL, we want to call this function 29 ** to output the letter (instead of just out- 30 ** putting it raw). 31 ** 32 ** Returns: 33 ** zero -- successfully delivered. 34 ** else -- some failure, see ExitStat for more info. 35 ** 36 ** Side Effects: 37 ** The standard input is passed off to someone. 38 ** 39 ** WARNING: 40 ** The standard input is shared amongst all children, 41 ** including the file pointer. It is critical that the 42 ** parent waits for the child to finish before forking 43 ** another child. 44 ** 45 ** Called By: 46 ** main 47 ** savemail 48 ** 49 ** Files: 50 ** standard input -- must be opened to the message to 51 ** deliver. 52 */ 53 54 deliver(to, editfcn) 55 addrq *to; 56 int (*editfcn)(); 57 { 58 register struct mailer *m; 59 char *host; 60 char *user; 61 extern struct passwd *getpwnam(); 62 char **pvp; 63 extern char **buildargv(); 64 auto int st; 65 register int i; 66 register char *p; 67 int pid; 68 int pvect[2]; 69 extern FILE *fdopen(); 70 extern int errno; 71 FILE *mfile; 72 extern putmessage(); 73 extern pipesig(); 74 extern char *index(); 75 76 /* 77 ** Compute receiving mailer, host, and to addreses. 78 ** Do some initialization first. To is the to address 79 ** for error messages. 80 */ 81 82 To = to->q_paddr; 83 m = to->q_mailer; 84 user = to->q_user; 85 host = to->q_host; 86 Errors = 0; 87 errno = 0; 88 # ifdef DEBUG 89 if (Debug) 90 printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user); 91 # endif DEBUG 92 93 /* 94 ** Remove quote bits from user/host. 95 */ 96 97 for (p = user; (*p++ &= 0177) != '\0'; ) 98 continue; 99 if (host != NULL) 100 for (p = host; (*p++ &= 0177) != '\0'; ) 101 continue; 102 103 /* 104 ** Strip quote bits from names if the mailer wants it. 105 */ 106 107 if (flagset(M_STRIPQ, m->m_flags)) 108 { 109 stripquotes(user); 110 stripquotes(host); 111 } 112 113 /* 114 ** See if this user name is "special". 115 ** If the user is a program, diddle with the mailer spec. 116 ** If the user name has a slash in it, assume that this 117 ** is a file -- send it off without further ado. 118 ** Note that this means that editfcn's will not 119 ** be applied to the message. 120 */ 121 122 if (m == &Mailer[0]) 123 { 124 if (*user == '|') 125 { 126 user++; 127 m = &Mailer[1]; 128 } 129 else 130 { 131 if (index(user, '/') != NULL) 132 { 133 i = mailfile(user); 134 giveresponse(i, TRUE, m); 135 return (i); 136 } 137 } 138 } 139 140 /* 141 ** See if the user exists. 142 ** Strictly, this is only needed to print a pretty 143 ** error message. 144 ** 145 ** >>>>>>>>>> This clause assumes that the local mailer 146 ** >> NOTE >> cannot do any further aliasing; that 147 ** >>>>>>>>>> function is subsumed by delivermail. 148 */ 149 150 if (m == &Mailer[0]) 151 { 152 if (getpwnam(user) == NULL) 153 { 154 giveresponse(EX_NOUSER, TRUE, m); 155 return (EX_NOUSER); 156 } 157 } 158 159 /* 160 ** Call the mailer. 161 ** The argument vector gets built, pipes 162 ** are created as necessary, and we fork & exec as 163 ** appropriate. 164 */ 165 166 pvp = buildargv(m, host, user, From.q_paddr); 167 if (pvp == NULL) 168 { 169 usrerr("name too long"); 170 return (-1); 171 } 172 rewind(stdin); 173 174 /* create a pipe to shove the mail through */ 175 if (pipe(pvect) < 0) 176 { 177 syserr("pipe"); 178 return (-1); 179 } 180 # ifdef VFORK 181 pid = vfork(); 182 # else 183 pid = fork(); 184 # endif 185 if (pid < 0) 186 { 187 syserr("Cannot fork"); 188 close(pvect[0]); 189 close(pvect[1]); 190 return (-1); 191 } 192 else if (pid == 0) 193 { 194 /* child -- set up input & exec mailer */ 195 /* make diagnostic output be standard output */ 196 close(2); 197 dup(1); 198 signal(SIGINT, SIG_IGN); 199 close(0); 200 if (dup(pvect[0]) < 0) 201 { 202 syserr("Cannot dup to zero!"); 203 _exit(EX_OSERR); 204 } 205 close(pvect[0]); 206 close(pvect[1]); 207 if (!flagset(M_RESTR, m->m_flags)) 208 setuid(getuid()); 209 # ifndef VFORK 210 /* 211 ** We have to be careful with vfork - we can't mung up the 212 ** memory but we don't want the mailer to inherit any extra 213 ** open files. Chances are the mailer won't 214 ** care about an extra file, but then again you never know. 215 ** Actually, we would like to close(fileno(pwf)), but it's 216 ** declared static so we can't. But if we fclose(pwf), which 217 ** is what endpwent does, it closes it in the parent too and 218 ** the next getpwnam will be slower. If you have a weird 219 ** mailer that chokes on the extra file you should do the 220 ** endpwent(). 221 ** 222 ** Similar comments apply to log. However, openlog is 223 ** clever enough to set the FIOCLEX mode on the file, 224 ** so it will be closed automatically on the exec. 225 */ 226 227 endpwent(); 228 # ifdef LOG 229 closelog(); 230 # endif LOG 231 # endif VFORK 232 execv(m->m_mailer, pvp); 233 /* syserr fails because log is closed */ 234 /* syserr("Cannot exec %s", m->m_mailer); */ 235 printf("Cannot exec %s\n", m->m_mailer); 236 fflush(stdout); 237 _exit(EX_UNAVAILABLE); 238 } 239 240 /* write out message to mailer */ 241 close(pvect[0]); 242 signal(SIGPIPE, pipesig); 243 mfile = fdopen(pvect[1], "w"); 244 if (editfcn == NULL) 245 editfcn = putmessage; 246 (*editfcn)(mfile, m); 247 fclose(mfile); 248 249 /* 250 ** Wait for child to die and report status. 251 ** We should never get fatal errors (e.g., segmentation 252 ** violation), so we report those specially. For other 253 ** errors, we choose a status message (into statmsg), 254 ** and if it represents an error, we print it. 255 */ 256 257 while ((i = wait(&st)) > 0 && i != pid) 258 continue; 259 if (i < 0) 260 { 261 syserr("wait"); 262 return (-1); 263 } 264 if ((st & 0377) != 0) 265 { 266 syserr("%s: stat %o", pvp[0], st); 267 ExitStat = EX_UNAVAILABLE; 268 return (-1); 269 } 270 i = (st >> 8) & 0377; 271 giveresponse(i, TRUE, m); 272 return (i); 273 } 274 /* 275 ** GIVERESPONSE -- Interpret an error response from a mailer 276 ** 277 ** Parameters: 278 ** stat -- the status code from the mailer (high byte 279 ** only; core dumps must have been taken care of 280 ** already). 281 ** force -- if set, force an error message output, even 282 ** if the mailer seems to like to print its own 283 ** messages. 284 ** m -- the mailer descriptor for this mailer. 285 ** 286 ** Returns: 287 ** none. 288 ** 289 ** Side Effects: 290 ** Errors may be incremented. 291 ** ExitStat may be set. 292 ** 293 ** Called By: 294 ** deliver 295 */ 296 297 giveresponse(stat, force, m) 298 int stat; 299 int force; 300 register struct mailer *m; 301 { 302 register char *statmsg; 303 extern char *SysExMsg[]; 304 register int i; 305 extern int N_SysEx; 306 extern long MsgSize; 307 char buf[30]; 308 309 i = stat - EX__BASE; 310 if (i < 0 || i > N_SysEx) 311 statmsg = NULL; 312 else 313 statmsg = SysExMsg[i]; 314 if (stat == 0) 315 statmsg = "ok"; 316 else 317 { 318 Errors++; 319 if (statmsg == NULL && m->m_badstat != 0) 320 { 321 stat = m->m_badstat; 322 i = stat - EX__BASE; 323 # ifdef DEBUG 324 if (i < 0 || i >= N_SysEx) 325 syserr("Bad m_badstat %d", stat); 326 else 327 # endif DEBUG 328 statmsg = SysExMsg[i]; 329 } 330 if (statmsg == NULL) 331 usrerr("unknown mailer response %d", stat); 332 else if (force || !flagset(M_QUIET, m->m_flags)) 333 usrerr("%s", statmsg); 334 } 335 336 /* 337 ** Final cleanup. 338 ** Log a record of the transaction. Compute the new 339 ** ExitStat -- if we already had an error, stick with 340 ** that. 341 */ 342 343 if (statmsg == NULL) 344 { 345 sprintf(buf, "error %d", stat); 346 statmsg = buf; 347 } 348 349 # ifdef LOG 350 syslog(LOG_INFO, "%s->%s: %ld: %s", From.q_paddr, To, MsgSize, statmsg); 351 # endif LOG 352 setstat(stat); 353 return (stat); 354 } 355 /* 356 ** PUTMESSAGE -- output a message to the final mailer. 357 ** 358 ** This routine takes care of recreating the header from the 359 ** in-core copy, etc. 360 ** 361 ** Parameters: 362 ** fp -- file to output onto. 363 ** m -- a mailer descriptor. 364 ** 365 ** Returns: 366 ** none. 367 ** 368 ** Side Effects: 369 ** The message is written onto fp. 370 */ 371 372 putmessage(fp, m) 373 FILE *fp; 374 struct mailer *m; 375 { 376 char buf[BUFSIZ]; 377 register int i; 378 HDR *h; 379 register char *p; 380 extern char *arpadate(); 381 extern char *hvalue(); 382 bool anyheader = FALSE; 383 extern char *translate(); 384 385 /* clear all "used" bits */ 386 for (h = Header; h != NULL; h = h->h_link) 387 h->h_flags &= ~H_USED; 388 389 /* output date if needed by mailer */ 390 p = hvalue("date"); 391 if (flagset(M_NEEDDATE, m->m_flags) && p == NULL) 392 p = arpadate(Date); 393 if (p != NULL) 394 { 395 fprintf(fp, "Date: %s\n", p); 396 anyheader = TRUE; 397 } 398 399 /* output from line if needed by mailer */ 400 p = hvalue("from"); 401 if (flagset(M_NEEDFROM, m->m_flags) && p == NULL) 402 { 403 char frombuf[MAXLINE]; 404 extern char *FullName; 405 406 p = translate("$f", From.q_mailer, From.q_paddr, NULL, NULL); 407 if (FullName != NULL) 408 fprintf(fp, "From: %s <%s>\n", FullName, p); 409 else 410 fprintf(fp, "From: %s\n", p); 411 free(p); 412 anyheader = TRUE; 413 } 414 else if (p != NULL) 415 { 416 fprintf(fp, "From: %s\n", p); 417 anyheader = TRUE; 418 } 419 420 /* output message-id field if needed */ 421 p = hvalue("message-id"); 422 if (flagset(M_MSGID, m->m_flags) && p == NULL) 423 p = MsgId; 424 if (p != NULL) 425 { 426 fprintf(fp, "Message-Id: %s\n", p); 427 anyheader = TRUE; 428 } 429 430 /* output any other header lines */ 431 for (h = Header; h != NULL; h = h->h_link) 432 { 433 if (flagset(H_USED, h->h_flags)) 434 continue; 435 fprintf(fp, "%s: %s\n", capitalize(h->h_field), h->h_value); 436 h->h_flags |= H_USED; 437 anyheader = TRUE; 438 } 439 440 if (anyheader) 441 fprintf(fp, "\n"); 442 443 /* output the body of the message */ 444 while (!ferror(fp) && (i = read(0, buf, BUFSIZ)) > 0) 445 fwrite(buf, 1, i, fp); 446 447 if (ferror(fp)) 448 { 449 syserr("putmessage: write error"); 450 setstat(EX_IOERR); 451 } 452 } 453 /* 454 ** PIPESIG -- Handle broken pipe signals 455 ** 456 ** This just logs an error. 457 ** 458 ** Parameters: 459 ** none 460 ** 461 ** Returns: 462 ** none 463 ** 464 ** Side Effects: 465 ** logs an error message. 466 */ 467 468 pipesig() 469 { 470 syserr("Broken pipe"); 471 signal(SIGPIPE, SIG_IGN); 472 } 473 /* 474 ** SENDTO -- Designate a send list. 475 ** 476 ** The parameter is a comma-separated list of people to send to. 477 ** This routine arranges to send to all of them. 478 ** 479 ** Parameters: 480 ** list -- the send list. 481 ** copyf -- the copy flag; passed to parse. 482 ** 483 ** Returns: 484 ** none 485 ** 486 ** Side Effects: 487 ** none. 488 ** 489 ** Called By: 490 ** main 491 ** alias 492 */ 493 494 sendto(list, copyf) 495 char *list; 496 int copyf; 497 { 498 register char *p; 499 register char *q; 500 register char c; 501 addrq *a; 502 extern addrq *parse(); 503 bool more; 504 505 /* more keeps track of what the previous delimiter was */ 506 more = TRUE; 507 for (p = list; more; ) 508 { 509 /* find the end of this address */ 510 q = p; 511 while ((c = *p++) != '\0' && c != ',' && c != '\n') 512 continue; 513 more = c != '\0'; 514 *--p = '\0'; 515 if (more) 516 p++; 517 518 /* parse the address */ 519 if ((a = parse(q, (addrq *) NULL, copyf)) == NULL) 520 continue; 521 522 /* arrange to send to this person */ 523 recipient(a, &SendQ); 524 } 525 To = NULL; 526 } 527 /* 528 ** RECIPIENT -- Designate a message recipient 529 ** 530 ** Saves the named person for future mailing. 531 ** 532 ** Designates a person as a recipient. This routine 533 ** does the initial parsing, and checks to see if 534 ** this person has already received the mail. 535 ** It also supresses local network names and turns them into 536 ** local names. 537 ** 538 ** Parameters: 539 ** a -- the (preparsed) address header for the recipient. 540 ** targetq -- the queue to add the name to. 541 ** 542 ** Returns: 543 ** none. 544 ** 545 ** Side Effects: 546 ** none. 547 ** 548 ** Called By: 549 ** sendto 550 ** main 551 */ 552 553 recipient(a, targetq) 554 register addrq *a; 555 addrq *targetq; 556 { 557 register addrq *q; 558 register struct mailer *m; 559 register char **pvp; 560 extern char *xalloc(); 561 extern bool forward(); 562 extern int errno; 563 extern bool sameaddr(); 564 565 To = a->q_paddr; 566 m = a->q_mailer; 567 errno = 0; 568 # ifdef DEBUG 569 if (Debug) 570 printf("recipient(%s)\n", To); 571 # endif DEBUG 572 573 /* 574 ** Look up this person in the recipient list. If they 575 ** are there already, return, otherwise continue. 576 */ 577 578 if (!ForceMail) 579 { 580 for (q = &SendQ; (q = nxtinq(q)) != NULL; ) 581 if (sameaddr(q, a, FALSE)) 582 { 583 # ifdef DEBUG 584 if (Debug) 585 printf("(%s in SendQ)\n", a->q_paddr); 586 # endif DEBUG 587 return; 588 } 589 for (q = &AliasQ; (q = nxtinq(q)) != NULL; ) 590 if (sameaddr(q, a, FALSE)) 591 { 592 # ifdef DEBUG 593 if (Debug) 594 printf("(%s in AliasQ)\n", a->q_paddr); 595 # endif DEBUG 596 return; 597 } 598 } 599 600 /* 601 ** See if the user wants hir mail forwarded. 602 ** `Forward' must do the forwarding recursively. 603 */ 604 605 if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a)) 606 return; 607 608 /* 609 ** Put the user onto the target queue. 610 */ 611 612 if (targetq != NULL) 613 { 614 putonq(a, targetq); 615 } 616 617 return; 618 } 619 /* 620 ** BUILDARGV -- Build an argument vector for a mail server. 621 ** 622 ** Using a template defined in config.c, an argv is built. 623 ** The format of the template is already a vector. The 624 ** items of this vector are copied, unless a dollar sign 625 ** is encountered. In this case, the next character 626 ** specifies something else to copy in. These can be 627 ** $f The from address. 628 ** $h The host. 629 ** $u The user. 630 ** $c The hop count. 631 ** The vector is built in a local buffer. A pointer to 632 ** the static argv is returned. 633 ** 634 ** Parameters: 635 ** m -- a pointer to the mailer descriptor. 636 ** host -- the host name to send to. 637 ** user -- the user name to send to. 638 ** from -- the person this mail is from. 639 ** 640 ** Returns: 641 ** A pointer to an argv. 642 ** 643 ** Side Effects: 644 ** none 645 ** 646 ** WARNING: 647 ** Since the argv is staticly allocated, any subsequent 648 ** calls will clobber the old argv. 649 ** 650 ** Called By: 651 ** deliver 652 */ 653 654 char ** 655 buildargv(m, host, user, from) 656 struct mailer *m; 657 char *host; 658 char *user; 659 char *from; 660 { 661 register char *p; 662 register char *q; 663 static char *pv[MAXPV+1]; 664 char **pvp; 665 char **mvp; 666 static char buf[512]; 667 register char *bp; 668 extern char *translate(); 669 670 /* 671 ** Do initial argv setup. 672 ** Insert the mailer name. Notice that $x expansion is 673 ** NOT done on the mailer name. Then, if the mailer has 674 ** a picky -f flag, we insert it as appropriate. This 675 ** code does not check for 'pv' overflow; this places a 676 ** manifest lower limit of 4 for MAXPV. 677 */ 678 679 pvp = pv; 680 bp = buf; 681 682 *pvp++ = m->m_argv[0]; 683 684 /* insert -f or -r flag as appropriate */ 685 if (flagset(M_FOPT|M_ROPT, m->m_flags) && FromFlag) 686 { 687 if (flagset(M_FOPT, m->m_flags)) 688 *pvp++ = "-f"; 689 else 690 *pvp++ = "-r"; 691 *pvp++ = translate(from, m, from, user, host); 692 } 693 694 /* 695 ** Build the rest of argv. 696 ** For each prototype parameter, the prototype is 697 ** scanned character at a time. If a dollar-sign is 698 ** found, 'q' is set to the appropriate expansion, 699 ** otherwise it is null. Then either the string 700 ** pointed to by q, or the original character, is 701 ** interpolated into the buffer. Buffer overflow is 702 ** checked. 703 */ 704 705 for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 706 { 707 if (pvp >= &pv[MAXPV]) 708 { 709 syserr("Too many parameters to %s", pv[0]); 710 return (NULL); 711 } 712 *pvp++ = translate(p, m, from, user, host); 713 } 714 *pvp = NULL; 715 716 # ifdef DEBUG 717 if (Debug) 718 { 719 printf("Interpolated argv is:\n"); 720 for (mvp = pv; *mvp != NULL; mvp++) 721 printf("\t%s\n", *mvp); 722 } 723 # endif DEBUG 724 725 return (pv); 726 } 727 /* 728 ** TRANSLATE -- translate a string using $x escapes. 729 ** 730 ** Parameters: 731 ** s -- string to translate. 732 ** m -- pointer to mailer descriptor. 733 ** 734 ** Returns: 735 ** pointer to translated string. 736 ** 737 ** Side Effects: 738 ** none. 739 */ 740 741 char * 742 translate(s, m, from, user, host) 743 register char *s; 744 struct mailer *m; 745 char *from; 746 char *user; 747 char *host; 748 { 749 register char *q; 750 char buf[MAXNAME]; 751 register char *bp; 752 char *stack = NULL; 753 char pbuf[10]; 754 extern char *newstr(); 755 extern char *Macro[]; 756 757 bp = buf; 758 restart: 759 760 # ifdef DEBUG 761 if (Debug) 762 printf("translate(%s)\n", s); 763 # endif DEBUG 764 for (; *s != '\0'; s++) 765 { 766 /* q will be the interpolated quantity */ 767 q = NULL; 768 if (*s == '$') 769 { 770 if (isupper(*++s)) 771 q = Macro[*s - 'A']; 772 else 773 { 774 switch (*s) 775 { 776 case 'f': /* from person */ 777 if (stack == NULL && m != NULL) 778 { 779 stack = s; 780 s = m->m_from; 781 goto restart; 782 } 783 q = from; 784 break; 785 786 case 'u': /* user */ 787 q = user; 788 break; 789 790 case 'h': /* host */ 791 q = host; 792 break; 793 794 case 'c': /* hop count */ 795 sprintf(pbuf, "%d", HopCount); 796 q = pbuf; 797 break; 798 } 799 } 800 } 801 802 /* 803 ** Interpolate q or output one character 804 ** Strip quote bits as we proceed..... 805 */ 806 807 if (q != NULL) 808 { 809 while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0') 810 continue; 811 bp--; 812 } 813 else if (bp < &buf[sizeof buf - 1]) 814 *bp++ = *s; 815 } 816 if (stack != NULL) 817 { 818 s = stack; 819 s++; 820 stack = NULL; 821 goto restart; 822 } 823 *bp++ = '\0'; 824 if (bp >= &buf[sizeof buf - 1]) 825 return (NULL); 826 # ifdef DEBUG 827 if (Debug) 828 printf("translate ==> '%s'\n", buf); 829 # endif DEBUG 830 return (newstr(buf)); 831 } 832 /* 833 ** MAILFILE -- Send a message to a file. 834 ** 835 ** Parameters: 836 ** filename -- the name of the file to send to. 837 ** 838 ** Returns: 839 ** The exit code associated with the operation. 840 ** 841 ** Side Effects: 842 ** none. 843 ** 844 ** Called By: 845 ** deliver 846 */ 847 848 mailfile(filename) 849 char *filename; 850 { 851 char buf[MAXLINE]; 852 register FILE *f; 853 auto long tim; 854 extern char *ctime(); 855 856 f = fopen(filename, "a"); 857 if (f == NULL) 858 return (EX_CANTCREAT); 859 860 /* output the timestamp */ 861 time(&tim); 862 fprintf(f, "From %s %s", From.q_paddr, ctime(&tim)); 863 rewind(stdin); 864 while (fgets(buf, sizeof buf, stdin) != NULL) 865 { 866 fputs(buf, f); 867 if (ferror(f)) 868 { 869 fclose(f); 870 return (EX_IOERR); 871 } 872 } 873 fputs("\n", f); 874 fclose(f); 875 return (EX_OK); 876 } 877