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 2.3 1/10/81"; 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: ** LOG -- if set, everything is logged. 82: ** 83: ** Compilation Instructions: 84: ** cc -c -O main.c config.c deliver.c parse.c 85: ** cc -n -s *.o -lS 86: ** chown root a.out 87: ** chmod 755 a.out 88: ** mv a.out delivermail 89: ** 90: ** Deficiencies: 91: ** It ought to collect together messages that are 92: ** destined for a single host and send these 93: ** to the auxiliary mail server together. 94: ** It should take "user at host" as three separate 95: ** parameters and combine them into one address. 96: ** 97: ** Author: 98: ** Eric Allman, UCB/INGRES 99: */ 100: 101: 102: 103: 104: 105: bool ArpaFmt; /* mail is expected to be in ARPANET format */ 106: bool FromFlag; /* from person is explicitly specified */ 107: bool Debug; /* run in debug mode */ 108: bool MailBack; /* mail back response on error */ 109: bool BerkNet; /* called from BerkNet */ 110: bool WriteBack; /* write back response on error */ 111: bool HasXscrpt; /* if set, the transcript file exists */ 112: bool NoAlias; /* don't do aliasing */ 113: bool ForceMail; /* mail even if already sent a copy */ 114: bool MeToo; /* send to the sender also if in a group expansion */ 115: bool SaveFrom; /* save From lines on the front of messages */ 116: bool IgnrDot; /* if set, ignore dot when collecting mail */ 117: bool SuprErrs; /* supress errors if set */ 118: int Errors; /* count of errors */ 119: char InFileName[] = "/tmp/mailtXXXXXX"; 120: char Transcript[] = "/tmp/mailxXXXXXX"; 121: addrq From; /* the from person */ 122: char *To; /* the target person */ 123: int HopCount; /* hop count */ 124: int ExitStat; /* the exit status byte */ 125: addrq SendQ; /* queue of people to send to */ 126: addrq AliasQ; /* queue of people who turned out to be aliases */ 127: 128: 129: 130: 131: 132: 133: main(argc, argv) 134: int argc; 135: char **argv; 136: { 137: register char *p; 138: extern char *maketemp(); 139: extern char *getname(); 140: extern int finis(); 141: extern addrq *parse(); 142: register addrq *q; 143: extern char Version[]; 144: extern int errno; 145: char *from; 146: register int i; 147: typedef int (*fnptr)(); 148: char nbuf[MAXLINE]; 149: 150: if (signal(SIGINT, SIG_IGN) != SIG_IGN) 151: signal(SIGINT, finis); 152: signal(SIGTERM, finis); 153: setbuf(stdout, (char *) NULL); 154: # ifdef LOG 155: openlog("delivermail", 0); 156: # endif LOG 157: # ifdef DEBUG 158: # ifdef DEBUGFILE 159: if ((i = open(DEBUGFILE, 1)) > 0) 160: { 161: lseek(i, 0L, 2); 162: close(1); 163: dup(i); 164: close(i); 165: Debug++; 166: } 167: # endif DEBUGFILE 168: # endif 169: errno = 0; 170: from = NULL; 171: 172: /* 173: ** Crack argv. 174: */ 175: 176: while (--argc > 0 && (p = *++argv)[0] == '-') 177: { 178: switch (p[1]) 179: { 180: case 'r': /* obsolete -f flag */ 181: case 'f': /* from address */ 182: p += 2; 183: if (*p == '\0') 184: { 185: p = *++argv; 186: if (--argc <= 0 || *p == '-') 187: { 188: syserr("No \"from\" person"); 189: argc++; 190: argv--; 191: break; 192: } 193: } 194: if (from != NULL) 195: { 196: syserr("More than one \"from\" person"); 197: break; 198: } 199: from = p; 200: break; 201: 202: case 'h': /* hop count */ 203: p += 2; 204: if (*p == '\0') 205: { 206: p = *++argv; 207: if (--argc <= 0 || *p < '0' || *p > '9') 208: { 209: syserr("Bad hop count (%s)", p); 210: argc++; 211: argv--; 212: break; 213: } 214: } 215: HopCount = atoi(p); 216: break; 217: 218: case 'e': /* error message disposition */ 219: switch (p[2]) 220: { 221: case 'p': /* print errors normally */ 222: break; /* (default) */ 223: 224: case 'q': /* be silent about it */ 225: freopen("/dev/null", "w", stdout); 226: break; 227: 228: case 'm': /* mail back */ 229: MailBack++; 230: openxscrpt(); 231: break; 232: 233: case 'e': /* do berknet error processing */ 234: BerkNet++; 235: openxscrpt(); 236: break; 237: 238: case 'w': /* write back (or mail) */ 239: WriteBack++; 240: openxscrpt(); 241: break; 242: } 243: break; 244: 245: # ifdef DEBUG 246: case 'd': /* debug */ 247: Debug++; 248: printf("%s\n", Version); 249: break; 250: # endif DEBUG 251: 252: case 'n': /* don't alias */ 253: NoAlias++; 254: break; 255: 256: case 'm': /* send to me too */ 257: MeToo++; 258: break; 259: 260: case 'i': /* don't let dot stop me */ 261: IgnrDot++; 262: break; 263: 264: case 'a': /* arpanet format */ 265: ArpaFmt++; 266: break; 267: 268: case 's': /* save From lines in headers */ 269: SaveFrom++; 270: break; 271: 272: default: 273: /* at Eric Schmidt's suggestion, this will not be an error.... 274: syserr("Unknown flag %s", p); 275: ... seems that upward compatibility will be easier. */ 276: break; 277: } 278: } 279: 280: if (from != NULL && ArpaFmt) 281: syserr("-f and -a are mutually exclusive"); 282: 283: /* 284: ** Get a temp file. 285: */ 286: 287: p = maketemp(); 288: if (from == NULL) 289: from = p; 290: # ifdef DEBUG 291: if (Debug) 292: printf("Message-Id: <%s>\n", MsgId); 293: # endif DEBUG 294: 295: /* 296: ** Figure out who it's coming from. 297: ** Under certain circumstances allow the user to say who 298: ** s/he is (using -f or -r). These are: 299: ** 1. The user's uid is zero (root). 300: ** 2. The user's login name is "network" (mail from 301: ** a network server). 302: ** 3. The user's login name is "uucp" (mail from the 303: ** uucp network). 304: ** 4. The address the user is trying to claim has a 305: ** "!" character in it (since #3 doesn't do it for 306: ** us if we are dialing out). 307: ** A better check to replace #3 & #4 would be if the 308: ** effective uid is "UUCP" -- this would require me 309: ** to rewrite getpwent to "grab" uucp as it went by, 310: ** make getname more nasty, do another passwd file 311: ** scan, or compile the UID of "UUCP" into the code, 312: ** all of which are reprehensible. 313: ** 314: ** Assuming all of these fail, we figure out something 315: ** ourselves. 316: */ 317: 318: errno = 0; 319: p = getname(); 320: if (p == NULL || p[0] == '\0') 321: { 322: syserr("Who are you? (uid=%d)", getuid()); 323: finis(); 324: } 325: errno = 0; 326: if (from != NULL) 327: { 328: if (strcmp(p, "network") != 0 && strcmp(p, "uucp") != 0 && 329: index(from, '!') == NULL && getuid() != 0) 330: { 331: /* network sends -r regardless (why why why?) */ 332: /* syserr("%s, you cannot use the -f flag", p); */ 333: from = NULL; 334: } 335: } 336: if (from == NULL || from[0] == '\0') 337: from = p; 338: else 339: FromFlag++; 340: SuprErrs = TRUE; 341: if (parse(from, &From, 0) == NULL) 342: { 343: /* too many arpanet hosts generate garbage From addresses .... 344: syserr("Bad from address `%s'", from); 345: .... so we will just ignore this address */ 346: from = p; 347: FromFlag = FALSE; 348: } 349: SuprErrs = FALSE; 350: 351: # ifdef DEBUG 352: if (Debug) 353: printf("From person = \"%s\"\n", From.q_paddr); 354: # endif DEBUG 355: 356: if (argc <= 0) 357: usrerr("Usage: /etc/delivermail [flags] addr..."); 358: 359: /* 360: ** Process Hop count. 361: ** The Hop count tells us how many times this message has 362: ** been processed by delivermail. If it exceeds some 363: ** fairly large threshold, then we assume that we have 364: ** an infinite forwarding loop and die. 365: */ 366: 367: if (++HopCount > MAXHOP) 368: syserr("Infinite forwarding loop (%s->%s)", From.q_paddr, *argv); 369: 370: /* 371: ** Scan argv and deliver the message to everyone. 372: */ 373: 374: for (; argc-- > 0; argv++) 375: { 376: p = argv[1]; 377: if (argc >= 2 && p[2] == '\0' && 378: (p[0] == 'a' || p[0] == 'A') && 379: (p[1] == 't' || p[1] == 'T')) 380: { 381: if (strlen(argv[0]) + strlen(argv[2]) + 2 > sizeof nbuf) 382: { 383: usrerr("address overflow"); 384: p = argv[0]; 385: } 386: else 387: { 388: strcpy(nbuf, argv[0]); 389: strcat(nbuf, "@"); 390: strcat(nbuf, argv[2]); 391: p = nbuf; 392: argv += 2; 393: argc -= 2; 394: } 395: } 396: else 397: p = argv[0]; 398: sendto(p, 0); 399: } 400: 401: /* if we have had errors sofar, drop out now */ 402: if (Errors > 0 && ExitStat == EX_OK) 403: ExitStat = EX_USAGE; 404: if (ExitStat != EX_OK) 405: finis(); 406: 407: /* 408: ** See if we have anyone to send to at all. 409: */ 410: 411: if (nxtinq(&SendQ) == NULL && ExitStat == EX_OK) 412: { 413: syserr("Noone to send to!"); 414: ExitStat = EX_USAGE; 415: finis(); 416: } 417: 418: /* 419: ** Do aliasing. 420: ** First arrange that the person who is sending the mail 421: ** will not be expanded (unless explicitly requested). 422: */ 423: 424: if (!MeToo) 425: recipient(&From, &AliasQ); 426: To = NULL; 427: alias(); 428: if (nxtinq(&SendQ) == NULL && ExitStat == EX_OK) 429: { 430: /* 431: syserr("Vacant send queue; probably aliasing loop"); 432: ExitStat = EX_SOFTWARE; 433: finis(); 434: */ 435: recipient(&From, &SendQ); 436: } 437: 438: /* 439: ** Actually send everything. 440: */ 441: 442: for (q = &SendQ; (q = nxtinq(q)) != NULL; ) 443: deliver(q, (fnptr) NULL); 444: 445: /* 446: ** All done. 447: */ 448: 449: finis(); 450: } 451: /* 452: ** FINIS -- Clean up and exit. 453: ** 454: ** Parameters: 455: ** none 456: ** 457: ** Returns: 458: ** never 459: ** 460: ** Side Effects: 461: ** exits delivermail 462: ** 463: ** Called By: 464: ** main 465: ** via signal on interrupt. 466: ** 467: ** Deficiencies: 468: ** It may be that it should only remove the input 469: ** file if there have been no errors. 470: */ 471: 472: finis() 473: { 474: /* mail back the transcript on errors */ 475: if (ExitStat != EX_OK) 476: savemail(); 477: 478: if (HasXscrpt) 479: unlink(Transcript); 480: unlink(InFileName); 481: exit(ExitStat); 482: } 483: /* 484: ** OPENXSCRPT -- Open transcript file 485: ** 486: ** Creates a transcript file for possible eventual mailing or 487: ** sending back. 488: ** 489: ** Parameters: 490: ** none 491: ** 492: ** Returns: 493: ** none 494: ** 495: ** Side Effects: 496: ** Turns the standard output into a special file 497: ** somewhere. 498: ** 499: ** Called By: 500: ** main 501: */ 502: 503: openxscrpt() 504: { 505: mktemp(Transcript); 506: HasXscrpt++; 507: if (freopen(Transcript, "w", stdout) == NULL) 508: syserr("Can't create %s", Transcript); 509: chmod(Transcript, 0600); 510: setbuf(stdout, (char *) NULL); 511: }