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