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