|
|
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) 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: #else ! 305: char *p; ! 306: if((p=index(from, '!')) != NULL) { ! 307: *p = NULL; ! 308: (void) sprintf(buf, RFROM(p+1, nows, from)); ! 309: size += strlen(buf); ! 310: (void) fputs(buf, out); ! 311: *p = '!'; ! 312: } ! 313: #endif ! 314: } else { ! 315: (void) sprintf(buf, RFROM(from, nows, hostname)); ! 316: size += strlen(buf); ! 317: (void) fputs(buf, out); ! 318: } ! 319: ! 320: /* END OF THIS LOOKS LIKE THE PLACE */ ! 321: #ifdef SENDMAIL ! 322: /* ! 323: ** If using sendmail, insert a Received: line only for mail ! 324: ** that is being passed to uux. If not using sendmail, always ! 325: ** insert the received line, since sendmail isn't there to do it. ! 326: */ ! 327: if(command == rcommand && handle != ALL) ! 328: #endif ! 329: { ! 330: #ifdef GATEWAY_NAME ! 331: (void) sprintf(buf, ! 332: "Received: by %s (%s - %s) id AA%05d; %s\n", ! 333: hostdomain, VERSION,GATEWAY_NAME, ! 334: getpid(), arpanows); ! 335: #else ! 336: (void) sprintf(buf, ! 337: "Received: by %s (%s) id AA%05d; %s\n", ! 338: hostdomain, VERSION, ! 339: getpid(), arpanows); ! 340: #endif ! 341: size += strlen(buf); ! 342: (void) fputs(buf, out); ! 343: } ! 344: ! 345: /* ! 346: ** Copy input. ! 347: */ ! 348: while(fgets(buf, sizeof(buf), spoolfp) != NULL) { ! 349: (void) fputs(buf, out); ! 350: } ! 351: /* ! 352: ** Get exit status and if non-zero, set global exitstat so when we exit ! 353: ** we can indicate an error. ! 354: */ ! 355: form_error: ! 356: if (debug != YES) { ! 357: if(form == ERROR) { ! 358: exitstat = EX_NOHOST; ! 359: } else { ! 360: status = pclose(out); ! 361: exitstat = status >> 8; ! 362: } ! 363: /* ! 364: * The 'retrying' check prevents a smail loop. ! 365: */ ! 366: if(exitstat != 0) { ! 367: /* ! 368: ** the mail failed, probably because the host ! 369: ** being uux'ed isn't in L.sys or local user ! 370: ** is unknown. ! 371: */ ! 372: ! 373: if((retrying == 0) /* first pass */ ! 374: && (routing != REROUTE) /* have higher level */ ! 375: && (form != LOCAL)) { /* can't route local */ ! 376: /* ! 377: ** Try again using a higher ! 378: ** level of routing. ! 379: */ ! 380: ADVISE("%s failed (%d)\ntrying %s\n", ! 381: command, exitstat, scommand); ! 382: exitstat = 0; ! 383: retrying = 1; ! 384: form = SENT; ! 385: goto retry; ! 386: } ! 387: ! 388: /* ! 389: ** if we have no other routing possibilities ! 390: ** see that the mail is returned to sender. ! 391: */ ! 392: ! 393: /* Important NOTE: A very nasty race condition exists here. If ! 394: * multiple child processes are trying to send mail to the same ! 395: * user simultaneously, then 1 process will get the mailbox and ! 396: * lock it, causing the other processes to fail. If a user invokes ! 397: * several processes to send mail to himself locally, then each ! 398: * process that fails once a mailbox is locked will force smail to ! 399: * return the message back to the original sender, which will fail, ! 400: * because the mailbox is already locked. This will force another ! 401: * smail process to fire up to return the returned message, and we ! 402: * will enter a recursive spawning of smail processes that will ! 403: * eventually choke the kernel. ! 404: * ! 405: * I have added code to check the following: ! 406: * if a local mail send fails, we check to see if the person ! 407: * who sent the message exists in the local send-to-user ! 408: * list (userlist). If this is true, we do NOT return the ! 409: * the mail, but write a message to the log file to indicate ! 410: * that this occured. ! 411: * ! 412: * Bob H. 10/20/92 ! 413: */ ! 414: if((routing == REROUTE) ! 415: || ((form == LOCAL) && (!strstr(userlist,from)))) { ! 416: ! 417: /* ! 418: ** if this was our last chance, ! 419: ** return the mail to the sender. ! 420: */ ! 421: ! 422: ADVISE("%s failed (%d)\n", ! 423: command, exitstat); ! 424: ! 425: (void) fseek(spoolfp, message, 0); ! 426: #ifdef SENDMAIL ! 427: /* if we have sendmail, then it ! 428: ** was handed the mail, which failed. ! 429: ** sendmail returns the failed mail ! 430: ** for us, so we need not do it again. ! 431: */ ! 432: if(form != LOCAL) ! 433: #endif ! 434: { ! 435: return_mail(from, command, form); ! 436: } ! 437: exitstat = 0; ! 438: } ! 439: if((form == LOCAL) && (strstr(userlist,from))){ ! 440: sprintf(err_msg,"Local mail to user %s failed. Because", from); ! 441: error_log(err_msg); ! 442: sprintf(err_msg,"this was the same person to send the message,"); ! 443: error_log(err_msg); ! 444: sprintf(err_msg,"it will NOT be returned to avoid a race condition."); ! 445: error_log(err_msg); ! 446: } ! 447: } ! 448: # ifdef LOG ! 449: else { ! 450: if(retrying == 0) log(command, from, size); ! 451: } ! 452: # endif ! 453: } ! 454: } ! 455: /* ! 456: ** Update logs and records. ! 457: */ ! 458: # ifdef RECORD ! 459: (void) fseek(spoolfp, message, 0); ! 460: record(command, from, size); ! 461: # endif ! 462: ! 463: /* ! 464: ** close spool file pointer. ! 465: ** if we created it, then unlink file. ! 466: */ ! 467: (void) fclose(spoolfp); ! 468: if(spoolmaster) { ! 469: (void) unlink(spoolfile); ! 470: } ! 471: (void) unlink(stderrfile); ! 472: } ! 473: ! 474: /* ! 475: ** return mail to sender, as determined by From_ line. ! 476: */ ! 477: return_mail(from, fcommand, form) ! 478: char *from, *fcommand; ! 479: enum eform form; ! 480: { ! 481: char buf[SMLBUF]; ! 482: char domain[SMLBUF], user[SMLBUF]; ! 483: char *r; ! 484: FILE *fp, *out, *popen(); ! 485: int i = 0; ! 486: ! 487: r = buf; ! 488: ! 489: (void) sprintf(r, "%s %s", SMAIL, VFLAG); ! 490: r += strlen(r); ! 491: ! 492: if(islocal(from, domain, user)) { ! 493: (void) sprintf(r, LARG(user)); ! 494: } else { ! 495: (void) sprintf(r, RLARG(domain, user)); ! 496: } ! 497: ! 498: i = 0; ! 499: ! 500: do { ! 501: out = popen(buf, "w"); ! 502: if (out) break; ! 503: /* ! 504: * Fork failed. System probably overloaded. ! 505: * Wait awhile and try again 10 times. ! 506: * If it keeps failing, probably some ! 507: * other problem, like no uux or smail. ! 508: */ ! 509: (void) sleep(60); ! 510: } while (++i < 10); ! 511: ! 512: if(out == NULL) { ! 513: (void) printf("couldn't execute %s.\n", buf); ! 514: return; ! 515: } ! 516: ! 517: (void) fprintf(out, "Date: %s\n", arpanows); ! 518: (void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain); ! 519: (void) fprintf(out, "Subject: failed mail\n"); ! 520: (void) fprintf(out, "To: %s\n", from); ! 521: (void) fprintf(out, "\n"); ! 522: (void) fprintf(out, "Form: %s\n", sform(form)); ! 523: (void) fprintf(out, "Exit status: %d\n", exitstat); ! 524: (void) fprintf(out, "======= command failed =======\n\n"); ! 525: (void) fprintf(out, " COMMAND: %s\n\n", fcommand); ! 526: ! 527: (void) fprintf(out, "======= standard error follows =======\n"); ! 528: (void) fflush(stderr); ! 529: if((fp = fopen(stderrfile, "r")) != NULL) { ! 530: while(fgets(buf, sizeof(buf), fp) != NULL) { ! 531: (void) fputs(buf, out); ! 532: } ! 533: } ! 534: (void) fclose(fp); ! 535: (void) fprintf(out, "======= text of message follows =======\n"); ! 536: /* ! 537: ** Copy input. ! 538: */ ! 539: (void) fprintf(out, "From %s\n", from); ! 540: while(fgets(buf, sizeof(buf), spoolfp) != NULL) { ! 541: (void) fputs(buf, out); ! 542: } ! 543: (void) pclose(out); ! 544: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.