|
|
1.1 ! root 1: /* ! 2: ** Deliver.c ! 3: ** ! 4: ** Routines to effect delivery of mail for rmail/smail. ! 5: ** ! 6: */ ! 7: ! 8: #ifndef lint ! 9: static char *sccsid="@(#)deliver.c 2.5 (smail) 9/15/87"; ! 10: #endif ! 11: ! 12: # include <stdio.h> ! 13: # include <sys/types.h> ! 14: # include <sys/stat.h> ! 15: /* # include <sys/utsname.h> */ ! 16: # include <ctype.h> ! 17: # include <signal.h> ! 18: # include "defs.h" ! 19: ! 20: extern int errno; /* Error number set by certain system routines. */ ! 21: extern int exitstat; /* set if a forked mailer fails */ ! 22: extern enum edebug debug; /* how verbose we are */ ! 23: extern char hostname[]; /* our uucp hostname */ ! 24: extern char hostdomain[]; /* our host's domain */ ! 25: extern enum ehandle handle; /* what we handle */ ! 26: extern enum erouting routing; /* how we're routing addresses */ ! 27: extern char *uuxargs; /* arguments given to uux */ ! 28: extern int queuecost; /* threshold for queueing mail */ ! 29: extern int maxnoqueue; /* max number of uucico's */ ! 30: extern char *spoolfile; /* file name of spooled message */ ! 31: extern FILE *spoolfp; /* file ptr to spooled message */ ! 32: extern int spoolmaster; /* set if creator of spoolfile */ ! 33: extern char nows[]; /* local time in ctime(3) format*/ ! 34: extern char arpanows[]; /* local time in arpadate format*/ ! 35: extern char _version[]; /* Version string from copyright.c */ ! 36: char stderrfile[20]; /* error file for stderr traping*/ ! 37: char err_msg[2*MAXCLEN]; /* handy place to build error messages */ ! 38: ! 39: char userlist[2*MAXCLEN]; /* used to build a list of users mail will ! 40: * sent to. ! 41: */ ! 42: /* ! 43: ** ! 44: ** deliver(): hand the letter to the proper mail programs. ! 45: ** ! 46: ** Issues one command for each different host of <hostv>, ! 47: ** constructing the proper command for LOCAL or UUCP mail. ! 48: ** Note that LOCAL mail has blank host names. ! 49: ** ! 50: ** The <userv> names for each host are arguments to the command. ! 51: ** ! 52: ** Prepends a "From" line to the letter just before going ! 53: ** out, with a "remote from <hostname>" if it is a UUCP letter. ! 54: ** ! 55: */ ! 56: ! 57: deliver(argc, hostv, userv, formv, costv) ! 58: int argc; /* number of addresses */ ! 59: char *hostv[]; /* host names */ ! 60: char *userv[]; /* user names */ ! 61: enum eform formv[]; /* form for each address */ ! 62: int costv[]; /* cost vector */ ! 63: { ! 64: FILE *out; /* pipe to mailer */ ! 65: FILE *popen(); /* to fork a mailer */ ! 66: #ifdef RECORD ! 67: void record(); /* record all transactions */ ! 68: #endif ! 69: #ifdef LOG ! 70: void log(); ! 71: #endif ! 72: char *mktemp(); ! 73: char *sform(); ! 74: char from[SMLBUF]; /* accumulated from argument */ ! 75: char lcommand[SMLBUF]; /* local command issued */ ! 76: char rcommand[SMLBUF]; /* remote command issued */ ! 77: char scommand[SMLBUF]; /* retry command issued */ ! 78: char *command; /* actual command */ ! 79: char buf[SMLBUF]; /* copying rest of the letter */ ! 80: enum eform form; /* holds form[i] for speed */ ! 81: long size; /* number of bytes of message */ ! 82: char *flags; /* flags for uux */ ! 83: char *sflag; /* flag for smail */ ! 84: int i, j, status, retrying; ! 85: char *c, *postmaster(); ! 86: int failcount = 0; ! 87: int noqcnt = 0; /* number of uucico's started */ ! 88: char *uux_noqueue = UUX_NOQUEUE;/* uucico starts immediately */ ! 89: char *uux_queue = UUX_QUEUE; /* uucico job gets queued */ ! 90: off_t message; ! 91: struct stat st; ! 92: ! 93: /* ! 94: ** rewind the spool file and read the collapsed From_ line ! 95: */ ! 96: (void) fseek(spoolfp, 0L, 0); ! 97: (void) fgets(from, sizeof(from), spoolfp); ! 98: if((c = index(from, '\n')) != 0) *c = '\0'; ! 99: message = ftell(spoolfp); ! 100: ! 101: /* ! 102: ** We pass through the list of addresses. ! 103: */ ! 104: userlist[0] = '\0'; /* initialize userlist */ ! 105: stderrfile[0] = '\0'; ! 106: for(i = 0; i < argc; i++) { ! 107: char *lend = lcommand; ! 108: char *rend = rcommand; ! 109: char *send = scommand; ! 110: ! 111: /* ! 112: ** Prevent errors from previous passes. ! 113: */ ! 114: exitstat = 0; ! 115: ! 116: ! 117: /* ! 118: ** If we don't have sendmail, arrange to trap standard error ! 119: ** for inclusion in the message that is returned with failed mail. ! 120: */ ! 121: (void) unlink(stderrfile); ! 122: (void) strcpy(stderrfile, "/tmp/stderrXXXXXX"); ! 123: (void) mktemp(stderrfile); ! 124: (void) freopen(stderrfile, "w", stderr); ! 125: if(debug != YES) { ! 126: (void) freopen(stderrfile, "w", stdout); ! 127: } ! 128: ! 129: *lend = *rend = *send = '\0'; ! 130: ! 131: /* ! 132: ** If form == ERROR, the address was bad ! 133: ** If form == SENT, it has been sent on a previous pass. ! 134: */ ! 135: form = formv[i]; ! 136: if (form == SENT) { ! 137: continue; ! 138: } ! 139: /* ! 140: ** Build the command based on whether this is local mail or uucp mail. ! 141: ** By default, don't allow more than 'maxnoqueue' uucico commands to ! 142: ** be started by a single invocation of 'smail'. ! 143: */ ! 144: if(uuxargs == NULL) { /* flags not set on command line */ ! 145: if(noqcnt < maxnoqueue && costv[i] <= queuecost) { ! 146: flags = uux_noqueue; ! 147: } else { ! 148: flags = uux_queue; ! 149: } ! 150: } else { ! 151: flags = uuxargs; ! 152: } ! 153: ! 154: retrying = 0; ! 155: if(routing == JUSTDOMAIN) { ! 156: sflag = "-r"; ! 157: } else if(routing == ALWAYS) { ! 158: sflag = "-R"; ! 159: } else { ! 160: sflag = ""; ! 161: } ! 162: ! 163: (void) sprintf(lcommand, LMAIL(from, hostv[i])); ! 164: (void) sprintf(rcommand, RMAIL(flags, from, hostv[i])); ! 165: ! 166: /* ! 167: ** For each address with the same host name and form, append the user ! 168: ** name to the command line, and set form = ERROR so we skip this address ! 169: ** on later passes. ! 170: */ ! 171: /* we initialized lend (rend) to point at the ! 172: * beginning of its buffer, so that at ! 173: * least one address will be used regardless ! 174: * of the length of lcommand (rcommand). ! 175: */ ! 176: for (j = i; j < argc; j++) { ! 177: if ((formv[j] != form) ! 178: || (strcmpic(hostv[i], hostv[j]) != 0) ! 179: || ((lend - lcommand) > MAXCLEN) ! 180: || ((rend - rcommand) > MAXCLEN)) { ! 181: continue; ! 182: } ! 183: ! 184: /* ! 185: ** seek to the end of scommand ! 186: ** and add on a 'smail' command ! 187: ** multiple commands are separated by ';' ! 188: */ ! 189: ! 190: send += strlen(send); ! 191: if(send != scommand) { ! 192: *send++ = ';' ; ! 193: } ! 194: ! 195: (void) sprintf(send, RETRY(sflag)); ! 196: send += strlen(send); ! 197: ! 198: lend += strlen(lend); ! 199: rend += strlen(rend); ! 200: ! 201: if (form == LOCAL) { ! 202: (void) sprintf(lend, LARG(userv[j])); ! 203: (void) sprintf(send, LARG(userv[j])); ! 204: strcat(userlist,userv[j]); ! 205: } else { ! 206: (void) sprintf(lend, RLARG(hostv[i], userv[j])); ! 207: (void) sprintf(send, RLARG(hostv[i], userv[j])); ! 208: } ! 209: ! 210: (void) sprintf(rend, RARG(userv[j])); ! 211: formv[j] = SENT; ! 212: } ! 213: retry: ! 214: /* ! 215: ** rewind the spool file and read the collapsed From_ line ! 216: */ ! 217: (void) fseek(spoolfp, message, 0); ! 218: ! 219: /* if the address was in a bogus form (usually DOMAIN), ! 220: ** then don't bother trying the uux. ! 221: ** ! 222: ** Rather, go straight to the next smail routing level. ! 223: */ ! 224: if(form == ERROR) { ! 225: static char errbuf[SMLBUF]; ! 226: (void) sprintf(errbuf, ! 227: "address resolution ('%s' @ '%s') failed", ! 228: userv[i], hostv[i]); ! 229: command = errbuf; ! 230: size = 0; ! 231: goto form_error; ! 232: } ! 233: ! 234: if (retrying) { ! 235: command = scommand; ! 236: } else if (form == LOCAL) { ! 237: command = lcommand; ! 238: } else { ! 239: command = rcommand; ! 240: if(flags == uux_noqueue) { ! 241: noqcnt++; ! 242: } ! 243: } ! 244: ADVISE("COMMAND: %s\n", command); ! 245: ! 246: /* ! 247: ** Fork the mailer and set it up for writing so we can send the mail to it, ! 248: ** or for debugging divert the output to stdout. ! 249: */ ! 250: ! 251: /* ! 252: ** We may try to write on a broken pipe, if the uux'd host ! 253: ** is unknown to us. Ignore this signal, since we can use the ! 254: ** return value of the pclose() as our indication of failure. ! 255: */ ! 256: (void) signal(SIGPIPE, SIG_IGN); ! 257: ! 258: if (debug == YES) { ! 259: out = stdout; ! 260: } else { ! 261: failcount = 0; ! 262: do { ! 263: out = popen(command, "w"); ! 264: if (out != NULL) break; ! 265: /* ! 266: * Fork failed. System probably overloaded. ! 267: * Wait awhile and try again 10 times. ! 268: * If it keeps failing, probably some ! 269: * other problem, like no uux or smail. ! 270: */ ! 271: (void) sleep(60); ! 272: if (failcount == 1) { ! 273: sprintf(err_msg, ! 274: "Deliver can not popen %s. errno = %d", ! 275: command, errno); ! 276: error_log(err_msg); ! 277: } /* if failed exactly once */ ! 278: } while (++failcount < 10); ! 279: if (failcount == 10) { ! 280: sprintf(err_msg, ! 281: "Deliver failed 10 times. errno = %d", ! 282: command, errno); ! 283: error_log(err_msg); ! 284: } /* if failed exactly once */ ! 285: } ! 286: if(out == NULL) { ! 287: exitstat = EX_UNAVAILABLE; ! 288: (void) printf("couldn't execute %s.\n", command); ! 289: continue; ! 290: } ! 291: ! 292: size = 0; ! 293: if(fstat(fileno(spoolfp), &st) >= 0) { ! 294: size = st.st_size - message; ! 295: } ! 296: /* ! 297: ** Output our From_ line. ! 298: */ ! 299: if (form == LOCAL) { ! 300: #ifdef SENDMAIL ! 301: (void) sprintf(buf, LFROM(from, nows, hostname)); ! 302: size += strlen(buf); ! 303: (void) fputs(buf, out); ! 304: fflush(out); ! 305: #else ! 306: char *p; ! 307: if((p=index(from, '!')) != NULL) { ! 308: *p = NULL; ! 309: (void) sprintf(buf, RFROM(p+1, nows, from)); ! 310: size += strlen(buf); ! 311: (void) fputs(buf, out); ! 312: fflush(out); ! 313: *p = '!'; ! 314: } ! 315: #endif ! 316: } else { ! 317: (void) sprintf(buf, RFROM(from, nows, hostname)); ! 318: size += strlen(buf); ! 319: (void) fputs(buf, out); ! 320: fflush(out); ! 321: } ! 322: ! 323: /* END OF THIS LOOKS LIKE THE PLACE */ ! 324: #ifdef SENDMAIL ! 325: /* ! 326: ** If using sendmail, insert a Received: line only for mail ! 327: ** that is being passed to uux. If not using sendmail, always ! 328: ** insert the received line, since sendmail isn't there to do it. ! 329: */ ! 330: if(command == rcommand && handle != ALL) ! 331: #endif ! 332: { ! 333: #ifdef GATEWAY_NAME ! 334: (void) sprintf(buf, ! 335: "Received: by %s (%s - %s) id AA%05d; %s\n", ! 336: hostdomain, VERSION,GATEWAY_NAME, ! 337: getpid(), arpanows); ! 338: #else ! 339: (void) sprintf(buf, ! 340: "Received: by %s (%s) id AA%05d; %s\n", ! 341: hostdomain, VERSION, ! 342: getpid(), arpanows); ! 343: #endif ! 344: size += strlen(buf); ! 345: (void) fputs(buf, out); ! 346: fflush(out); ! 347: } ! 348: ! 349: /* ! 350: ** Copy input. ! 351: */ ! 352: while(fgets(buf, sizeof(buf), spoolfp) != NULL) { ! 353: (void) fputs(buf, out); ! 354: fflush(out); ! 355: } ! 356: /* ! 357: ** Get exit status and if non-zero, set global exitstat so when we exit ! 358: ** we can indicate an error. ! 359: */ ! 360: form_error: ! 361: if (debug != YES) { ! 362: if(form == ERROR) { ! 363: exitstat = EX_NOHOST; ! 364: } else { ! 365: status = pclose(out); ! 366: exitstat = status >> 8; ! 367: } ! 368: /* ! 369: * The 'retrying' check prevents a smail loop. ! 370: */ ! 371: if(exitstat != 0) { ! 372: /* ! 373: ** the mail failed, probably because the host ! 374: ** being uux'ed isn't in L.sys or local user ! 375: ** is unknown. ! 376: */ ! 377: ! 378: if((retrying == 0) /* first pass */ ! 379: && (routing != REROUTE) /* have higher level */ ! 380: && (form != LOCAL)) { /* can't route local */ ! 381: /* ! 382: ** Try again using a higher ! 383: ** level of routing. ! 384: */ ! 385: ADVISE("%s failed (%d)\ntrying %s\n", ! 386: command, exitstat, scommand); ! 387: exitstat = 0; ! 388: retrying = 1; ! 389: form = SENT; ! 390: goto retry; ! 391: } ! 392: ! 393: /* ! 394: ** if we have no other routing possibilities ! 395: ** see that the mail is returned to sender. ! 396: */ ! 397: ! 398: /* Important NOTE: A very nasty race condition exists here. If ! 399: * multiple child processes are trying to send mail to the same ! 400: * user simultaneously, then 1 process will get the mailbox and ! 401: * lock it, causing the other processes to fail. If a user invokes ! 402: * several processes to send mail to himself locally, then each ! 403: * process that fails once a mailbox is locked will force smail to ! 404: * return the message back to the original sender, which will fail, ! 405: * because the mailbox is already locked. This will force another ! 406: * smail process to fire up to return the returned message, and we ! 407: * will enter a recursive spawning of smail processes that will ! 408: * eventually choke the kernel. ! 409: * ! 410: * I have added code to check the following: ! 411: * if a local mail send fails, we check to see if the person ! 412: * who sent the message exists in the local send-to-user ! 413: * list (userlist). If this is true, we do NOT return the ! 414: * the mail, but write a message to the log file to indicate ! 415: * that this occured. ! 416: * ! 417: * Bob H. 10/20/92 ! 418: */ ! 419: if((routing == REROUTE) ! 420: || ((form == LOCAL) && (!strstr(userlist,from)))) { ! 421: ! 422: /* ! 423: ** if this was our last chance, ! 424: ** return the mail to the sender. ! 425: */ ! 426: ! 427: ADVISE("%s failed (%d)\n", ! 428: command, exitstat); ! 429: ! 430: (void) fseek(spoolfp, message, 0); ! 431: #ifdef SENDMAIL ! 432: /* if we have sendmail, then it ! 433: ** was handed the mail, which failed. ! 434: ** sendmail returns the failed mail ! 435: ** for us, so we need not do it again. ! 436: */ ! 437: if(form != LOCAL) ! 438: #endif ! 439: { ! 440: return_mail(from, command, form); ! 441: } ! 442: exitstat = 0; ! 443: } ! 444: if((form == LOCAL) && (strstr(userlist,from))){ ! 445: sprintf(err_msg,"exit stat was %d", exitstat); ! 446: error_log(err_msg); ! 447: sprintf(err_msg,"Local mail to user %s failed. Because", from); ! 448: error_log(err_msg); ! 449: sprintf(err_msg,"this was the same person to send the message,"); ! 450: error_log(err_msg); ! 451: sprintf(err_msg,"it will NOT be returned to avoid a race condition."); ! 452: error_log(err_msg); ! 453: ! 454: } ! 455: } ! 456: # ifdef LOG ! 457: else { ! 458: if(retrying == 0) log(command, from, size); ! 459: } ! 460: # endif ! 461: } ! 462: } ! 463: /* ! 464: ** Update logs and records. ! 465: */ ! 466: # ifdef RECORD ! 467: (void) fseek(spoolfp, message, 0); ! 468: record(command, from, size); ! 469: # endif ! 470: ! 471: /* ! 472: ** close spool file pointer. ! 473: ** if we created it, then unlink file. ! 474: */ ! 475: (void) fclose(spoolfp); ! 476: if(spoolmaster) { ! 477: (void) unlink(spoolfile); ! 478: } ! 479: (void) unlink(stderrfile); ! 480: } ! 481: ! 482: /* ! 483: ** return mail to sender, as determined by From_ line. ! 484: */ ! 485: return_mail(from, fcommand, form) ! 486: char *from, *fcommand; ! 487: enum eform form; ! 488: { ! 489: char buf[SMLBUF]; ! 490: char domain[SMLBUF], user[SMLBUF]; ! 491: char *r; ! 492: FILE *fp, *out, *popen(); ! 493: int i = 0; ! 494: ! 495: r = buf; ! 496: ! 497: (void) sprintf(r, "%s %s", SMAIL, VFLAG); ! 498: r += strlen(r); ! 499: ! 500: if(islocal(from, domain, user)) { ! 501: (void) sprintf(r, LARG(user)); ! 502: } else { ! 503: (void) sprintf(r, RLARG(domain, user)); ! 504: } ! 505: ! 506: i = 0; ! 507: ! 508: do { ! 509: out = popen(buf, "w"); ! 510: if (out) break; ! 511: /* ! 512: * Fork failed. System probably overloaded. ! 513: * Wait awhile and try again 10 times. ! 514: * If it keeps failing, probably some ! 515: * other problem, like no uux or smail. ! 516: */ ! 517: (void) sleep(60); ! 518: } while (++i < 10); ! 519: ! 520: if(out == NULL) { ! 521: (void) printf("couldn't execute %s.\n", buf); ! 522: return; ! 523: } ! 524: ! 525: (void) fprintf(out, "Date: %s\n", arpanows); ! 526: (void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain); ! 527: (void) fprintf(out, "Subject: failed mail\n"); ! 528: (void) fprintf(out, "To: %s\n", from); ! 529: (void) fprintf(out, "\n"); ! 530: (void) fprintf(out, "Form: %s\n", sform(form)); ! 531: (void) fprintf(out, "Exit status: %d\n", exitstat); ! 532: (void) fprintf(out, "======= command failed =======\n\n"); ! 533: (void) fprintf(out, " COMMAND: %s\n\n", fcommand); ! 534: ! 535: (void) fprintf(out, "======= standard error follows =======\n"); ! 536: (void) fflush(stderr); ! 537: if((fp = fopen(stderrfile, "r")) != NULL) { ! 538: while(fgets(buf, sizeof(buf), fp) != NULL) { ! 539: (void) fputs(buf, out); ! 540: } ! 541: } ! 542: (void) fclose(fp); ! 543: (void) fprintf(out, "======= text of message follows =======\n"); ! 544: /* ! 545: ** Copy input. ! 546: */ ! 547: (void) fprintf(out, "From %s\n", from); ! 548: while(fgets(buf, sizeof(buf), spoolfp) != NULL) { ! 549: (void) fputs(buf, out); ! 550: } ! 551: (void) pclose(out); ! 552: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.