1 # include <stdio.h> 2 # include <signal.h> 3 # include <ctype.h> 4 # include "dlvrmail.h" 5 # ifdef LOG 6 # include <log.h> 7 # endif LOG 8 9 /* 10 ** DELIVERMAIL -- Deliver mail to a set of destinations 11 ** 12 ** This is the basic mail router. All user mail programs should 13 ** call this routine to actually deliver mail. Delivermail in 14 ** turn calls a bunch of mail servers that do the real work of 15 ** delivering the mail. 16 ** 17 ** Delivermail is driven by tables defined in config.c. This 18 ** file will be different from system to system, but the rest 19 ** of the code will be the same. This table could be read in, 20 ** but it seemed nicer to have it compiled in, since deliver- 21 ** mail will potentially be exercised a lot. 22 ** 23 ** Usage: 24 ** /etc/delivermail [-f name] [-a] [-q] [-v] [-n] [-m] addr ... 25 ** 26 ** Positional Parameters: 27 ** addr -- the address to deliver the mail to. There 28 ** can be several. 29 ** 30 ** Flags: 31 ** -f name The mail is from "name" -- used for 32 ** the header in local mail, and to 33 ** deliver reports of failures to. 34 ** -r name Same as -f; however, this flag is 35 ** reserved to indicate special processing 36 ** for remote mail delivery as needed 37 ** in the future. So, network servers 38 ** should use -r. 39 ** -a This mail should be in ARPANET std 40 ** format (not used). 41 ** -n Don't do aliasing. This might be used 42 ** when delivering responses, for 43 ** instance. 44 ** -d Run in debug mode. 45 ** -em Mail back a response if there was an 46 ** error in processing. This should be 47 ** used when the origin of this message 48 ** is another machine. 49 ** -ew Write back a response if the user is 50 ** still logged in, otherwise, act like 51 ** -em. 52 ** -eq Don't print any error message (just 53 ** return exit status). 54 ** -ep (default) Print error messages 55 ** normally. 56 ** -m In group expansion, send to the 57 ** sender also (stands for the Mail metoo 58 ** option. 59 ** -i Do not terminate mail on a line 60 ** containing just dot. 61 ** -s Save UNIX-like "From" lines on the 62 ** front of messages. 63 ** 64 ** Return Codes: 65 ** As defined in <sysexits.h>. 66 ** 67 ** These codes are actually returned from the auxiliary 68 ** mailers; it is their responsibility to make them 69 ** correct. 70 ** 71 ** Defined Constants: 72 ** none 73 ** 74 ** Compilation Flags: 75 ** BADMAIL -- the mailer used for local mail doesn't 76 ** return the standard set of exit codes. This 77 ** causes the name to be looked up before mail 78 ** is ever sent. 79 ** LOG -- if set, everything is logged. 80 ** MESSAGEID -- if set, the Message-Id field is added 81 ** to the message header if one does not already 82 ** exist. This can be used to delete duplicate 83 ** messages. 84 ** 85 ** Compilation Instructions: 86 ** cc -c -O main.c config.c deliver.c parse.c 87 ** cc -n -s *.o -lS 88 ** chown root a.out 89 ** chmod 755 a.out 90 ** mv a.out delivermail 91 ** 92 ** Requires: 93 ** signal (sys) 94 ** setbuf (sys) 95 ** initlog (libX) 96 ** open (sys) 97 ** lseek (sys) 98 ** close (sys) 99 ** dup (sys) 100 ** printf (sys) 101 ** syserr 102 ** atoi (sys) 103 ** freopen (sys) 104 ** openxscript 105 ** maketemp 106 ** getname 107 ** strcmp (sys) 108 ** getuid (sys) 109 ** parse 110 ** usrerr 111 ** finis 112 ** sendto 113 ** alias 114 ** recipient 115 ** nxtinq 116 ** deliver 117 ** 118 ** Deficiencies: 119 ** It ought to collect together messages that are 120 ** destined for a single host and send these 121 ** to the auxiliary mail server together. 122 ** It should take "user at host" as three separate 123 ** parameters and combine them into one address. 124 ** 125 ** Author: 126 ** Eric Allman, UCB/INGRES 127 ** 128 ** History: 129 ** 12/26/79 -- first written. 130 */ 131 132 133 134 135 136 char ArpaFmt; /* mail is expected to be in ARPANET format */ 137 char FromFlag; /* from person is explicitly specified */ 138 char Debug; /* run in debug mode */ 139 char MailBack; /* mail back response on error */ 140 char EchoBack; /* echo the message on error */ 141 char WriteBack; /* write back response on error */ 142 char HasXscrpt; /* if set, the transcript file exists */ 143 char NoAlias; /* don't do aliasing */ 144 char ForceMail; /* mail even if already sent a copy */ 145 char MeToo; /* send to the sender also if in a group expansion */ 146 char SaveFrom; /* save From lines on the front of messages */ 147 char IgnrDot; /* if set, ignore dot when collecting mail */ 148 char Error; /* set if errors */ 149 char SuprErrs; /* supress errors if set */ 150 char InFileName[] = "/tmp/mailtXXXXXX"; 151 char Transcript[] = "/tmp/mailxXXXXXX"; 152 addrq From; /* the from person */ 153 char *To; /* the target person */ 154 char MsgId[MAXNAME]; /* the message-id for this letter */ 155 int HopCount; /* hop count */ 156 int ExitStat; /* the exit status byte */ 157 addrq SendQ; /* queue of people to send to */ 158 addrq AliasQ; /* queue of people who turned out to be aliases */ 159 160 161 162 163 164 165 main(argc, argv) 166 int argc; 167 char **argv; 168 { 169 register char *p; 170 extern char *maketemp(); 171 extern char *getname(); 172 extern int finis(); 173 extern addrq *parse(); 174 register addrq *q; 175 extern char Version[]; 176 extern int errno; 177 char *from; 178 register int i; 179 typedef int (*fnptr)(); 180 181 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 182 signal(SIGINT, finis); 183 signal(SIGTERM, finis); 184 setbuf(stdout, (char *) NULL); 185 # ifdef LOG 186 initlog("delivermail", 0, LOG_INDEP); 187 # endif LOG 188 # ifdef DEBUG 189 # ifdef DEBUGFILE 190 if ((i = open(DEBUGFILE, 1)) > 0) 191 { 192 lseek(i, 0L, 2); 193 close(1); 194 dup(i); 195 close(i); 196 Debug++; 197 } 198 # endif DEBUGFILE 199 if (Debug) 200 printf("%s\n", Version); 201 # endif 202 errno = 0; 203 from = NULL; 204 205 /* 206 ** Crack argv. 207 */ 208 209 while (--argc > 0 && (p = *++argv)[0] == '-') 210 { 211 switch (p[1]) 212 { 213 case 'r': /* obsolete -f flag */ 214 case 'f': /* from address */ 215 p += 2; 216 if (*p == '\0') 217 { 218 p = *++argv; 219 if (--argc <= 0 || *p == '-') 220 { 221 syserr("No \"from\" person"); 222 argc++; 223 argv--; 224 break; 225 } 226 } 227 if (from != NULL) 228 { 229 syserr("More than one \"from\" person"); 230 break; 231 } 232 from = p; 233 break; 234 235 case 'h': /* hop count */ 236 p += 2; 237 if (*p == '\0') 238 { 239 p = *++argv; 240 if (--argc <= 0 || *p < '0' || *p > '9') 241 { 242 syserr("Bad hop count (%s)", p); 243 argc++; 244 argv--; 245 break; 246 } 247 } 248 HopCount = atoi(p); 249 break; 250 251 case 'e': /* error message disposition */ 252 switch (p[2]) 253 { 254 case 'p': /* print errors normally */ 255 break; /* (default) */ 256 257 case 'q': /* be silent about it */ 258 freopen("/dev/null", "w", stdout); 259 break; 260 261 case 'm': /* mail back */ 262 MailBack++; 263 openxscrpt(); 264 break; 265 266 case 'e': /* echo back */ 267 EchoBack++; 268 openxscrpt(); 269 break; 270 271 case 'w': /* write back (or mail) */ 272 WriteBack++; 273 openxscrpt(); 274 break; 275 } 276 break; 277 278 # ifdef DEBUG 279 case 'd': /* debug */ 280 Debug++; 281 break; 282 # endif DEBUG 283 284 case 'n': /* don't alias */ 285 NoAlias++; 286 break; 287 288 case 'm': /* send to me too */ 289 MeToo++; 290 break; 291 292 case 'i': /* don't let dot stop me */ 293 IgnrDot++; 294 break; 295 296 case 'a': /* arpanet format */ 297 ArpaFmt++; 298 break; 299 300 case 's': /* save From lines in headers */ 301 SaveFrom++; 302 break; 303 304 default: 305 /* at Eric Schmidt's suggestion, this will not be an error.... 306 syserr("Unknown flag %s", p); 307 ... seems that upward compatibility will be easier. */ 308 break; 309 } 310 } 311 312 if (from != NULL && ArpaFmt) 313 syserr("-f and -a are mutually exclusive"); 314 315 /* 316 ** Get a temp file. 317 */ 318 319 p = maketemp(); 320 if (from == NULL) 321 from = p; 322 # ifdef DEBUG 323 if (Debug) 324 printf("Message-Id: <%s>\n", MsgId); 325 # endif DEBUG 326 327 /* 328 ** Figure out who it's coming from. 329 ** If we are root or "network", then allow -f. Otherwise, 330 ** insist that we figure it out ourselves. 331 */ 332 333 errno = 0; 334 p = getname(); 335 if (p == NULL || p[0] == '\0') 336 { 337 syserr("Who are you? (uid=%d)", getuid()); 338 finis(); 339 } 340 errno = 0; 341 if (from != NULL) 342 { 343 if (strcmp(p, "network") != 0 && getuid() != 0 /* && strcmp(p, From) != 0 */ ) 344 { 345 /* network sends -r regardless (why why why?) */ 346 /* syserr("%s, you cannot use the -f flag", p); */ 347 from = NULL; 348 } 349 } 350 if (from == NULL || from[0] == '\0') 351 from = p; 352 else 353 FromFlag++; 354 SuprErrs = TRUE; 355 if (parse(from, &From, 0) == NULL) 356 { 357 /* too many arpanet hosts generate garbage From addresses .... 358 syserr("Bad from address `%s'", from); 359 .... so we will just ignore this address */ 360 from = p; 361 FromFlag = FALSE; 362 } 363 SuprErrs = FALSE; 364 365 # ifdef DEBUG 366 if (Debug) 367 printf("From person = \"%s\"\n", From.q_paddr); 368 # endif DEBUG 369 370 if (argc <= 0) 371 usrerr("Usage: /etc/delivermail [flags] addr..."); 372 373 /* 374 ** Process Hop count. 375 ** The Hop count tells us how many times this message has 376 ** been processed by delivermail. If it exceeds some 377 ** fairly large threshold, then we assume that we have 378 ** an infinite forwarding loop and die. 379 */ 380 381 if (++HopCount > MAXHOP) 382 syserr("Infinite forwarding loop (%s->%s)", From.q_paddr, *argv); 383 384 /* if we have had errors sofar, drop out now */ 385 if (Error && ExitStat == EX_OK) 386 ExitStat = EX_USAGE; 387 if (ExitStat != EX_OK) 388 finis(); 389 390 /* 391 ** Scan argv and deliver the message to everyone. 392 */ 393 394 for (; argc-- > 0; argv++) 395 { 396 sendto(*argv, 0); 397 } 398 399 /* 400 ** See if we have anyone to send to at all. 401 */ 402 403 if (nxtinq(&SendQ) == NULL && ExitStat == EX_OK) 404 { 405 syserr("Noone to send to!"); 406 ExitStat = EX_USAGE; 407 finis(); 408 } 409 410 /* 411 ** Do aliasing. 412 ** First arrange that the person who is sending the mail 413 ** will not be expanded (unless explicitly requested). 414 */ 415 416 if (!MeToo) 417 recipient(&From, &AliasQ); 418 To = NULL; 419 alias(); 420 if (nxtinq(&SendQ) == NULL && ExitStat == EX_OK) 421 { 422 /* 423 syserr("Vacant send queue; probably aliasing loop"); 424 ExitStat = EX_SOFTWARE; 425 finis(); 426 */ 427 recipient(&From, &SendQ); 428 } 429 430 /* 431 ** Actually send everything. 432 */ 433 434 for (q = &SendQ; (q = nxtinq(q)) != NULL; ) 435 deliver(q, (fnptr) NULL); 436 437 /* 438 ** All done. 439 */ 440 441 finis(); 442 } 443 /* 444 ** FINIS -- Clean up and exit. 445 ** 446 ** Algorithm: 447 ** if we should remove the input 448 ** remove the input 449 ** exit 450 ** 451 ** Parameters: 452 ** none 453 ** 454 ** Returns: 455 ** never 456 ** 457 ** Side Effects: 458 ** exits delivermail 459 ** 460 ** Requires: 461 ** unlink (sys) 462 ** exit (sys) 463 ** savemail 464 ** InFileName -- the file to remove 465 ** ExitStat -- the status to exit with 466 ** 467 ** Called By: 468 ** main 469 ** via signal on interrupt. 470 ** 471 ** Deficiencies: 472 ** It may be that it should only remove the input 473 ** file if there have been no errors. 474 ** 475 ** History: 476 ** 12/26/79 -- written. 477 */ 478 479 finis() 480 { 481 /* mail back the transcript on errors */ 482 if (ExitStat != EX_OK) 483 savemail(); 484 485 if (HasXscrpt) 486 unlink(Transcript); 487 unlink(InFileName); 488 exit(ExitStat); 489 } 490 /* 491 ** MAKETEMP -- Make temporary file 492 ** 493 ** Creates a temporary file name and copies the standard 494 ** input to that file. While it is doing it, it looks for 495 ** "From:" and "Sender:" fields to use as the from-person 496 ** (but only if the -a flag is specified). It prefers to 497 ** to use the "Sender:" field -- the protocol says that 498 ** "Sender:" must come after "From:", so this works easily. 499 ** MIT seems to like to produce "Sent-By:" fields instead 500 ** of "Sender:" fields. We used to catch this, but it turns 501 ** out that the "Sent-By:" field doesn't always correspond 502 ** to someone real, as required by the protocol. So we limp 503 ** by..... 504 ** 505 ** Parameters: 506 ** none 507 ** 508 ** Returns: 509 ** Name of temp file. 510 ** 511 ** Side Effects: 512 ** Temp file is created and filled. 513 ** 514 ** Requires: 515 ** creat (sys) 516 ** close (sys) 517 ** syserr 518 ** mktemp (sys) 519 ** fopen (sys) 520 ** fgets (sys) 521 ** makemsgid 522 ** fprintf (sys) 523 ** fputs (sys) 524 ** isspace (sys) 525 ** matchhdr 526 ** prescan 527 ** ferror (sys) 528 ** clearerr (sys) 529 ** freopen (sys) 530 ** 531 ** Called By: 532 ** main 533 ** 534 ** Notes: 535 ** This is broken off from main largely so that the 536 ** temp buffer can be deallocated. 537 ** 538 ** Deficiencies: 539 ** It assumes that the From: field will preceed the 540 ** Sender: field. This violates the Arpanet NIC 733 541 ** protocol, but seems reasonable in practice. In 542 ** any case, the only problem is that error responses 543 ** may be sent to the wrong person. 544 ** 545 ** History: 546 ** 12/26/79 -- written. 547 */ 548 549 char * 550 maketemp() 551 { 552 register FILE *tf; 553 char buf[MAXLINE+1]; 554 static char fbuf[sizeof buf]; 555 extern char *prescan(); 556 extern char *matchhdr(); 557 register char *p; 558 bool inheader; 559 bool firstline; 560 561 /* 562 ** Create the temp file name and create the file. 563 */ 564 565 mktemp(InFileName); 566 close(creat(InFileName, 0600)); 567 if ((tf = fopen(InFileName, "w")) == NULL) 568 { 569 syserr("Cannot create %s", InFileName); 570 return (NULL); 571 } 572 573 /* 574 ** Copy stdin to temp file & do message editting. 575 ** From person gets copied into fbuf. At the end of 576 ** this loop, if fbuf[0] == '\0' then there was no 577 ** recognized from person in the message. We also 578 ** save the message id in MsgId. The 579 ** flag 'inheader' keeps track of whether we are 580 ** in the header or in the body of the message. 581 ** The flag 'firstline' is only true on the first 582 ** line of a message. 583 ** To keep certain mailers from getting confused, 584 ** and to keep the output clean, lines that look 585 ** like UNIX "From" lines are deleted in the header, 586 ** and prepended with ">" in the body. 587 */ 588 589 inheader = TRUE; 590 firstline = TRUE; 591 fbuf[0] = '\0'; 592 while (fgets(buf, sizeof buf, stdin) != NULL) 593 { 594 if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 595 break; 596 597 /* are we still in the header? */ 598 if ((buf[0] == '\n' || buf[0] == '\0') && inheader) 599 { 600 inheader = FALSE; 601 if (MsgId[0] == '\0') 602 { 603 makemsgid(); 604 # ifdef MESSAGEID 605 fprintf(tf, "Message-Id: <%s>\n", MsgId); 606 # endif MESSAGEID 607 } 608 } 609 610 /* Hide UNIX-like From lines */ 611 if (buf[0] == 'F' && buf[1] == 'r' && buf[2] == 'o' && 612 buf[3] == 'm' && buf[4] == ' ') 613 { 614 if (firstline && !SaveFrom) 615 continue; 616 fputs(">", tf); 617 } 618 619 if (inheader && !isspace(buf[0])) 620 { 621 /* find out if this is really a header */ 622 for (p = buf; *p != ':' && *p != '\0' && !isspace(*p); p++) 623 continue; 624 while (*p != ':' && isspace(*p)) 625 p++; 626 if (*p != ':') 627 inheader = FALSE; 628 } 629 630 if (inheader) 631 { 632 /* find the sender */ 633 p = matchhdr(buf, "from"); 634 if (p == NULL) 635 p = matchhdr(buf, "sender"); 636 if (p != NULL) 637 prescan(p, fbuf, &fbuf[sizeof fbuf - 1], '\0'); 638 639 /* find the message id */ 640 p = matchhdr(buf, "message-id"); 641 if (p != NULL && MsgId[0] == '\0') 642 prescan(p, MsgId, &MsgId[sizeof MsgId - 1], '\0'); 643 } 644 fputs(buf, tf); 645 firstline = FALSE; 646 if (ferror(tf)) 647 { 648 syserr("Cannot write %s", InFileName); 649 clearerr(tf); 650 break; 651 } 652 } 653 fclose(tf); 654 if (MsgId[0] == '\0') 655 makemsgid(); 656 if (freopen(InFileName, "r", stdin) == NULL) 657 syserr("Cannot reopen %s", InFileName); 658 return (ArpaFmt && fbuf[0] != '\0' ? fbuf : NULL); 659 } 660 /* 661 ** MAKEMSGID -- Compute a message id for this process. 662 ** 663 ** This routine creates a message id for a message if 664 ** it did not have one already. If the MESSAGEID compile 665 ** flag is set, the messageid will be added to any message 666 ** that does not already have one. Currently it is more 667 ** of an artifact, but I suggest that if you are hacking, 668 ** you leave it in -- I may want to use it someday if 669 ** duplicate messages turn out to be a problem. 670 ** 671 ** Parameters: 672 ** none. 673 ** 674 ** Returns: 675 ** none. 676 ** 677 ** Side Effects: 678 ** Stores a message-id into MsgId. 679 ** 680 ** Requires: 681 ** sprintf (sys) 682 ** getpid (sys) 683 ** time (sys) 684 ** 685 ** Called By: 686 ** maketemp 687 ** 688 ** History: 689 ** 2/3/80 -- written. 690 */ 691 692 makemsgid() 693 { 694 auto long t; 695 extern char *MyLocName; 696 697 time(&t); 698 sprintf(MsgId, "%ld.%d.%s@Berkeley", t, getpid(), MyLocName); 699 } 700 /* 701 ** OPENXSCRPT -- Open transcript file 702 ** 703 ** Creates a transcript file for possible eventual mailing or 704 ** sending back. 705 ** 706 ** Parameters: 707 ** none 708 ** 709 ** Returns: 710 ** none 711 ** 712 ** Side Effects: 713 ** Turns the standard output into a special file 714 ** somewhere. 715 ** 716 ** Requires: 717 ** mktemp (sys) 718 ** chmod (sys) 719 ** freopen (sys) 720 ** syserr 721 ** setbuf (sys) 722 ** 723 ** Called By: 724 ** main 725 ** 726 ** History: 727 ** 1/11/80 -- written. 728 */ 729 730 openxscrpt() 731 { 732 mktemp(Transcript); 733 HasXscrpt++; 734 if (freopen(Transcript, "w", stdout) == NULL) 735 syserr("Can't create %s", Transcript); 736 chmod(Transcript, 0600); 737 setbuf(stdout, (char *) NULL); 738 } 739