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