home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Deliver.c
- **
- ** Routines to effect delivery of mail for rmail/smail.
- **
- */
-
- /*
- ** Patched for MS-DOS compatibility by Stephen Trier
- ** March, 1990 - Version 0.1 alpha
- ** Mid-April, 1990 - Version 0.1 beta
- ** May, 1990 - Version 1.0 beta
- */
-
- #ifndef lint
- static char *sccsid="@(#)deliver.c 2.5 (smail) 9/15/87";
- #endif
-
- # include <stdio.h>
- # include <sys/types.h>
- # include <sys/stat.h>
- # include <ctype.h>
- # include <signal.h>
- # include "defs.h"
- #ifdef MSDOS
- # include <process.h>
- # include <fcntl.h>
- # include <setjmp.h>
- #endif
-
- extern int exitstat; /* set if a forked mailer fails */
- extern enum edebug debug; /* how verbose we are */
- extern char hostname[]; /* our uucp hostname */
- extern char hostdomain[]; /* our host's domain */
- extern enum ehandle handle; /* what we handle */
- extern enum erouting routing; /* how we're routing addresses */
- extern char *uuxargs; /* arguments given to uux */
- extern int queuecost; /* threshold for queueing mail */
- extern int maxnoqueue; /* max number of uucico's */
- extern char *spoolfile; /* file name of spooled message */
- extern FILE *spoolfp; /* file ptr to spooled message */
- extern int spoolmaster; /* set if creator of spoolfile */
- extern char nows[]; /* local time in ctime(3) format*/
- extern char arpanows[]; /* local time in arpadate format*/
- #ifndef MSDOS
- char stderrfile[20]; /* error file for stderr traping*/
- #else
- char stderrfile[80]; /* error file for stderr traping*/
- #endif
-
- /*
- **
- ** deliver(): hand the letter to the proper mail programs.
- **
- ** Issues one command for each different host of <hostv>,
- ** constructing the proper command for LOCAL or UUCP mail.
- ** Note that LOCAL mail has blank host names.
- **
- ** The <userv> names for each host are arguments to the command.
- **
- ** Prepends a "From" line to the letter just before going
- ** out, with a "remote from <hostname>" if it is a UUCP letter.
- **
- */
-
- deliver(argc, hostv, userv, formv, costv)
- int argc; /* number of addresses */
- char *hostv[]; /* host names */
- char *userv[]; /* user names */
- enum eform formv[]; /* form for each address */
- int costv[]; /* cost vector */
- {
- FILE *out; /* pipe to mailer */
- FILE *popen(); /* to fork a mailer */
- #ifdef RECORD
- void record(); /* record all transactions */
- #endif
- #ifdef LOG
- void log();
- #endif
- char *mktemp();
- char from[SMLBUF]; /* accumulated from argument */
- char lcommand[SMLBUF]; /* local command issued */
- char rcommand[SMLBUF]; /* remote command issued */
- char scommand[SMLBUF]; /* retry command issued */
- char *command; /* actual command */
- char buf[SMLBUF]; /* copying rest of the letter */
- enum eform form; /* holds form[i] for speed */
- long size; /* number of bytes of message */
- char *flags; /* flags for uux */
- char *sflag; /* flag for smail */
- int i, j, status, retrying;
- char *c, *postmaster();
- #ifndef MSDOS
- int failcount = 0;
- #else
- int k, stderrfd;
- #endif
- int noqcnt = 0; /* number of uucico's started */
- char *uux_noqueue = UUX_NOQUEUE;/* uucico starts immediately */
- char *uux_queue = UUX_QUEUE; /* uucico job gets queued */
- off_t message;
- struct stat st;
-
- /*
- ** rewind the spool file and read the collapsed From_ line
- */
- (void) fseek(spoolfp, 0L, 0);
- (void) fgets(from, sizeof(from), spoolfp);
- if((c = index(from, '\n')) != 0) *c = '\0';
- message = ftell(spoolfp);
-
- /*
- ** We pass through the list of addresses.
- */
- stderrfile[0] = '\0';
- for(i = 0; i < argc; i++) {
- char *lend = lcommand;
- char *rend = rcommand;
- char *send = scommand;
-
- /*
- ** If we don't have sendmail, arrange to trap standard error
- ** for inclusion in the message that is returned with failed mail.
- */
- (void) unlink(stderrfile);
- #ifndef MSDOS
- (void) strcpy(stderrfile, "/tmp/stderrXXXXXX");
- (void) mktemp(stderrfile);
- (void) freopen(stderrfile, "w", stderr);
- if(debug != YES) {
- (void) freopen(stderrfile, "w", stdout);
- }
- #else /* MSDOS */
- (void) sprintf(stderrfile, "%s/stderrXXXXXX", ms_tmpdir);
- (void) mktemp(stderrfile);
- stderrfd = open(stderrfile, O_CREAT|O_APPEND, S_IREAD|S_IWRITE);
- dup2(stderrfd, 2);
- if (debug != NO)
- dup2(stderrfd, 2);
- #endif /* !MSDOS */
-
- *lend = *rend = *send = '\0';
-
- /*
- ** If form == ERROR, the address was bad
- ** If form == SENT, it has been sent on a previous pass.
- */
- form = formv[i];
- if (form == SENT) {
- continue;
- }
- /*
- ** Build the command based on whether this is local mail or uucp mail.
- ** By default, don't allow more than 'maxnoqueue' uucico commands to
- ** be started by a single invocation of 'smail'.
- */
- if(uuxargs == NULL) { /* flags not set on command line */
- if(noqcnt < maxnoqueue && costv[i] <= queuecost) {
- flags = uux_noqueue;
- } else {
- flags = uux_queue;
- }
- } else {
- flags = uuxargs;
- }
-
- retrying = 0;
- if(routing == JUSTDOMAIN) {
- sflag = "-r";
- } else if(routing == ALWAYS) {
- sflag = "-R";
- } else {
- sflag = "";
- }
-
- (void) sprintf(lcommand, LMAIL(from, hostv[i]));
- (void) sprintf(rcommand, RMAIL(flags, from, hostv[i]));
-
- /*
- ** For each address with the same host name and form, append the user
- ** name to the command line, and set form = ERROR so we skip this address
- ** on later passes.
- */
- /* we initialized lend (rend) to point at the
- * beginning of its buffer, so that at
- * least one address will be used regardless
- * of the length of lcommand (rcommand).
- */
- for (j = i; j < argc; j++) {
- if ((formv[j] != form)
- || (strcmpic(hostv[i], hostv[j]) != 0)
- || ((lend - lcommand) > MAXCLEN)
- || ((rend - rcommand) > MAXCLEN)) {
- continue;
- }
-
- /*
- ** seek to the end of scommand
- ** and add on a 'smail' command
- ** multiple commands are separated by ';'
- */
-
- send += strlen(send);
- if(send != scommand) {
- *send++ = ';' ;
- }
-
- (void) sprintf(send, RETRY(sflag));
- send += strlen(send);
-
- lend += strlen(lend);
- rend += strlen(rend);
-
- if (form == LOCAL) {
- (void) sprintf(lend, LARG(userv[j]));
- (void) sprintf(send, LARG(userv[j]));
- } else {
- (void) sprintf(lend, RLARG(hostv[i], userv[j]));
- (void) sprintf(send, RLARG(hostv[i], userv[j]));
- }
-
- (void) sprintf(rend, RARG(userv[j]));
- formv[j] = SENT;
- }
- retry:
- /*
- ** rewind the spool file and read the collapsed From_ line
- */
- (void) fseek(spoolfp, message, 0);
-
- /* if the address was in a bogus form (usually DOMAIN),
- ** then don't bother trying the uux.
- **
- ** Rather, go straight to the next smail routing level.
- */
- if(form == ERROR) {
- static char errbuf[SMLBUF];
- (void) sprintf(errbuf,
- "address resolution ('%s' @ '%s') failed",
- userv[i], hostv[i]);
- command = errbuf;
- size = 0;
- goto form_error;
- }
-
- if (retrying) {
- command = scommand;
- } else if (form == LOCAL) {
- command = lcommand;
- } else {
- command = rcommand;
- if(flags == uux_noqueue) {
- noqcnt++;
- }
- }
- ADVISE("COMMAND: %s\n", command);
-
- /*
- ** Fork the mailer and set it up for writing so we can send the mail to it,
- ** or for debugging divert the output to stdout.
- */
-
- /*
- ** We may try to write on a broken pipe, if the uux'd host
- ** is unknown to us. Ignore this signal, since we can use the
- ** return value of the pclose() as our indication of failure.
- */
- #ifndef MSDOS
- (void) signal(SIGPIPE, SIG_IGN);
- #endif
-
- if (debug == YES) {
- out = stdout;
- } else {
- #ifndef MSDOS
- failcount = 0;
- do {
- out = popen(command, "w");
- if (out) break;
- /*
- * Fork failed. System probably overloaded.
- * Wait awhile and try again 10 times.
- * If it keeps failing, probably some
- * other problem, like no uux or smail.
- */
- (void) sleep(60);
- } while (++failcount < 10);
- #else /* MSDOS */
- out = popen(command, "w");
- /*
- * A failed fork under MS-DOS is just that. Don't
- * bother retrying it; it won't get any better.
- */
- #endif /* !MSDOS */
- }
- if(out == NULL) {
- exitstat = EX_UNAVAILABLE;
- (void) printf("couldn't execute %s.\n", command);
- continue;
- }
-
- size = 0;
- if(fstat(fileno(spoolfp), &st) >= 0) {
- size = st.st_size - message;
- }
- /*
- ** Output our From_ line.
- */
- if (form == LOCAL) {
- #ifdef SENDMAIL
- (void) sprintf(buf, LFROM(from, nows, hostname));
- size += strlen(buf);
- (void) fputs(buf, out);
- #else
- char *p;
- if((p=index(from, '!')) == NULL) {
- (void) sprintf(buf, LFROM(from, nows, hostname));
- size += strlen(buf);
- (void) fputs(buf, out);
- } else {
- *p = NULL;
- (void) sprintf(buf, RFROM(p+1, nows, from));
- size += strlen(buf);
- (void) fputs(buf, out);
- *p = '!';
- }
- #endif
- } else {
- (void) sprintf(buf, RFROM(from, nows, hostname));
- size += strlen(buf);
- (void) fputs(buf, out);
- }
-
- #ifdef SENDMAIL
- /*
- ** If using sendmail, insert a Received: line only for mail
- ** that is being passed to uux. If not using sendmail, always
- ** insert the received line, since sendmail isn't there to do it.
- */
- if(command == rcommand && handle != ALL)
- #endif
- {
- (void) sprintf(buf,
- "Received: by %s (%s)\n\tid AA%05d; %s\n",
- hostdomain, VERSION,
- getpid(), arpanows);
- size += strlen(buf);
- (void) fputs(buf, out);
- }
-
- /*
- ** Copy input.
- */
- while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
- (void) fputs(buf, out);
- }
- /*
- ** Get exit status and if non-zero, set global exitstat so when we exit
- ** we can indicate an error.
- */
- form_error:
- if (debug != YES) {
- if(form == ERROR) {
- exitstat = EX_NOHOST;
- } else if (status = pclose(out)) {
- exitstat = status >> 8;
- }
- /*
- * The 'retrying' check prevents a smail loop.
- */
- if(exitstat != 0) {
- /*
- ** the mail failed, probably because the host
- ** being uux'ed isn't in L.sys or local user
- ** is unknown.
- */
-
- if((retrying == 0) /* first pass */
- && (routing != REROUTE) /* have higher level */
- && (form != LOCAL)) { /* can't route local */
- /*
- ** Try again using a higher
- ** level of routing.
- */
- ADVISE("%s failed (%d)\ntrying %s\n",
- command, exitstat, scommand);
- exitstat = 0;
- retrying = 1;
- form = SENT;
- #ifndef MSDOS
- goto retry;
- #else
- for (k = i; k < argc; k++) {
- if ((formv[k] != form)
- || (strcmpic(hostv[i], hostv[k]) != 0)
- || ((lend - lcommand) > MAXCLEN)
- || ((rend - rcommand) > MAXCLEN)) {
- continue;
- }
- formv[k] = ROUTE; /* Try again */
- }
- #endif
- }
-
- /*
- ** if we have no other routing possibilities
- ** see that the mail is returned to sender.
- */
-
- if((routing == REROUTE)
- || (form == LOCAL)) {
-
- /*
- ** if this was our last chance,
- ** return the mail to the sender.
- */
-
- ADVISE("%s failed (%d)\n",
- command, exitstat);
-
- (void) fseek(spoolfp, message, 0);
- #ifdef SENDMAIL
- /* if we have sendmail, then it
- ** was handed the mail, which failed.
- ** sendmail returns the failed mail
- ** for us, so we need not do it again.
- */
- if(form != LOCAL)
- #endif
- {
- return_mail(from, command);
- }
- exitstat = 0;
- }
- }
- # ifdef LOG
- else {
- if(retrying == 0) log(command, from, size); /* */
- }
- # endif
- }
- }
- /*
- ** Update logs and records.
- */
- # ifdef RECORD
- (void) fseek(spoolfp, message, 0);
- record(command, from, size);
- # endif
-
- /*
- ** close spool file pointer.
- ** if we created it, then unlink file.
- */
- #ifndef MSDOS
- (void) fclose(spoolfp);
- if(spoolmaster) {
- (void) unlink(spoolfile);
- }
- #else /* MSDOS */
- /*
- * Close and unlink the spool file in the main program; we might need
- * it again.
- */
- close(stderrfd);
- fclose(stderr);
- #endif /* !MSDOS */
- (void) unlink(stderrfile);
- }
-
- /*
- ** return mail to sender, as determined by From_ line.
- */
- return_mail(from, fcommand)
- char *from, *fcommand;
- {
- char buf[SMLBUF];
- char domain[SMLBUF], user[SMLBUF];
- char *r;
- FILE *fp, *out, *popen();
- #ifndef MSDOS
- int i = 0;
- #endif
-
- r = buf;
-
- (void) sprintf(r, "%s %s", SMAIL, VFLAG);
- r += strlen(r);
-
- if(islocal(from, domain, user)) {
- (void) sprintf(r, LARG(user));
- } else {
- (void) sprintf(r, RLARG(domain, user));
- }
-
- #ifndef MSDOS
- i = 0;
- do {
- out = popen(buf, "w");
- if (out) break;
- /*
- * Fork failed. System probably overloaded.
- * Wait awhile and try again 10 times.
- * If it keeps failing, probably some
- * other problem, like no uux or smail.
- */
- (void) sleep(60);
- } while (++i < 10);
- #else /* !MSDOS */
- out = popen(buf, "w");
- /*
- * Don't bother retrying it if the fork failed. After
- * all, it's awfully hard for an MS-DOS machine to be
- * running too many processes at once... :-)
- */
- #endif
-
- if(out == NULL) {
- (void) printf("couldn't execute %s.\n", buf);
- return;
- }
-
- (void) fprintf(out, "Date: %s\n", arpanows);
- (void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain);
- (void) fprintf(out, "Subject: failed mail\n");
- (void) fprintf(out, "To: %s\n", from);
- (void) fprintf(out, "\n");
- (void) fprintf(out, "======= command failed =======\n\n");
- (void) fprintf(out, " COMMAND: %s\n\n", fcommand);
-
- (void) fprintf(out, "======= standard error follows =======\n");
- (void) fflush(stderr);
- if((fp = fopen(stderrfile, "r")) != NULL) {
- while(fgets(buf, sizeof(buf), fp) != NULL) {
- (void) fputs(buf, out);
- }
- }
- (void) fclose(fp);
- (void) fprintf(out, "\n");
- (void) fprintf(out, "======= text of message follows =======\n");
- /*
- ** Copy input.
- */
- (void) fprintf(out, "From %s\n", from);
- while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
- (void) fputs(buf, out);
- }
- (void) pclose(out);
- }