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