+** SENDALL -- actually send all the messages.
+**
+** Parameters:
+** e -- the envelope to send.
+** mode -- the delivery mode to use. If SM_DEFAULT, use
+** the current e->e_sendmode.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Scans the send lists and sends everything it finds.
+** Delivers any appropriate error messages.
+** If we are running in a non-interactive mode, takes the
+** appropriate action.
+*/
+
+sendall(e, mode)
+ ENVELOPE *e;
+ char mode;
+{
+ register ADDRESS *q;
+ char *owner;
+ int otherowners;
+ register ENVELOPE *ee;
+ ENVELOPE *splitenv = NULL;
+ bool announcequeueup;
+
+ if (bitset(EF_FATALERRS, e->e_flags))
+ {
+ /* this will get a return message -- so don't send it */
+ e->e_flags |= EF_CLRQUEUE;
+ return;
+ }
+
+ /* determine actual delivery mode */
+ if (mode == SM_DEFAULT)
+ {
+ mode = e->e_sendmode;
+ if (mode != SM_VERIFY &&
+ shouldqueue(e->e_msgpriority, e->e_ctime))
+ mode = SM_QUEUE;
+ announcequeueup = mode == SM_QUEUE;
+ }
+ else
+ announcequeueup = FALSE;
+
+ if (tTd(13, 1))
+ {
+ printf("\nSENDALL: mode %c, e_from ", mode);
+ printaddr(&e->e_from, FALSE);
+ printf("sendqueue:\n");
+ printaddr(e->e_sendqueue, TRUE);
+ }
+
+ /*
+ ** Do any preprocessing necessary for the mode we are running.
+ ** Check to make sure the hop count is reasonable.
+ ** Delete sends to the sender in mailing lists.
+ */
+
+ CurEnv = e;
+
+ if (e->e_hopcount > MaxHopCount)
+ {
+ errno = 0;
+ syserr("554 too many hops %d (%d max): from %s, to %s",
+ e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
+ e->e_sendqueue->q_paddr);
+ return;
+ }
+
+ /*
+ ** Do sender deletion.
+ **
+ ** If the sender has the QQUEUEUP flag set, skip this.
+ ** This can happen if the name server is hosed when you
+ ** are trying to send mail. The result is that the sender
+ ** is instantiated in the queue as a recipient.
+ */
+
+ if (!MeToo && !bitset(QQUEUEUP, e->e_from.q_flags))
+ {
+ if (tTd(13, 5))
+ {
+ printf("sendall: QDONTSEND ");
+ printaddr(&e->e_from, FALSE);
+ }
+ e->e_from.q_flags |= QDONTSEND;
+ (void) recipient(&e->e_from, &e->e_sendqueue, e);
+ }
+
+ /*
+ ** Handle alias owners.
+ **
+ ** We scan up the q_alias chain looking for owners.
+ ** We discard owners that are the same as the return path.
+ */
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ register struct address *a;
+
+ for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
+ continue;
+ if (a != NULL)
+ q->q_owner = a->q_owner;
+
+ if (q->q_owner != NULL &&
+ !bitset(QDONTSEND, q->q_flags) &&
+ strcmp(q->q_owner, e->e_from.q_paddr) == 0)
+ q->q_owner = NULL;
+ }
+
+ owner = "";
+ otherowners = 1;
+ while (owner != NULL && otherowners > 0)
+ {
+ owner = NULL;
+ otherowners = 0;
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (bitset(QDONTSEND, q->q_flags))
+ continue;
+
+ if (q->q_owner != NULL)
+ {
+ if (owner == NULL)
+ owner = q->q_owner;
+ else if (owner != q->q_owner)
+ {
+ if (strcmp(owner, q->q_owner) == 0)
+ {
+ /* make future comparisons cheap */
+ q->q_owner = owner;
+ }
+ else
+ {
+ otherowners++;
+ }
+ owner = q->q_owner;
+ }
+ }
+ else
+ {
+ otherowners++;
+ }
+ }
+
+ if (owner != NULL && otherowners > 0)
+ {
+ extern HDR *copyheader();
+ extern ADDRESS *copyqueue();
+
+ /*
+ ** Split this envelope into two.
+ */
+
+ ee = (ENVELOPE *) xalloc(sizeof(ENVELOPE));
+ *ee = *e;
+ ee->e_id = NULL;
+ (void) queuename(ee, '\0');
+
+ if (tTd(13, 1))
+ printf("sendall: split %s into %s\n",
+ e->e_id, ee->e_id);
+
+ ee->e_header = copyheader(e->e_header);
+ ee->e_sendqueue = copyqueue(e->e_sendqueue);
+ ee->e_errorqueue = copyqueue(e->e_errorqueue);
+ ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS);
+ setsender(owner, ee, NULL, TRUE);
+ if (tTd(13, 5))
+ {
+ printf("sendall(split): QDONTSEND ");
+ printaddr(&ee->e_from, FALSE);
+ }
+ ee->e_from.q_flags |= QDONTSEND;
+ ee->e_dfp = NULL;
+ ee->e_xfp = NULL;
+ ee->e_lockfp = NULL;
+ ee->e_df = NULL;
+ ee->e_errormode = EM_MAIL;
+ ee->e_sibling = splitenv;
+ splitenv = ee;
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ if (q->q_owner == owner)
+ q->q_flags |= QDONTSEND;
+ for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
+ if (q->q_owner != owner)
+ q->q_flags |= QDONTSEND;
+
+ if (e->e_df != NULL && mode != SM_VERIFY)
+ {
+ ee->e_dfp = NULL;
+ ee->e_df = newstr(queuename(ee, 'd'));
+ if (link(e->e_df, ee->e_df) < 0)
+ {
+ syserr("sendall: link(%s, %s)",
+ e->e_df, ee->e_df);
+ }
+ }
+
+ if (mode != SM_VERIFY)
+ openxscript(ee);
+#ifdef LOG
+ if (LogLevel > 4)
+ syslog(LOG_INFO, "%s: clone %s",
+ ee->e_id, e->e_id);
+#endif
+ }
+ }
+
+ if (owner != NULL)
+ {
+ setsender(owner, e, NULL, TRUE);
+ if (tTd(13, 5))
+ {
+ printf("sendall(owner): QDONTSEND ");
+ printaddr(&e->e_from, FALSE);
+ }
+ e->e_from.q_flags |= QDONTSEND;
+ e->e_errormode = EM_MAIL;
+ }
+
+# ifdef QUEUE
+ if ((mode == SM_QUEUE || mode == SM_FORK ||
+ (mode != SM_VERIFY && SuperSafe)) &&
+ !bitset(EF_INQUEUE, e->e_flags))
+ {
+ /* be sure everything is instantiated in the queue */
+ queueup(e, TRUE, announcequeueup);
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ queueup(ee, TRUE, announcequeueup);
+ }
+#endif /* QUEUE */
+
+ if (splitenv != NULL)
+ {
+ if (tTd(13, 1))
+ {
+ printf("\nsendall: Split queue; remaining queue:\n");
+ printaddr(e->e_sendqueue, TRUE);
+ }
+
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ {
+ CurEnv = ee;
+ sendenvelope(ee, mode);
+ }
+
+ CurEnv = e;
+ }
+ sendenvelope(e, mode);
+
+ for (; splitenv != NULL; splitenv = splitenv->e_sibling)
+ dropenvelope(splitenv);
+}
+
+sendenvelope(e, mode)
+ register ENVELOPE *e;
+ char mode;
+{
+ bool oldverbose;
+ int pid;
+ register ADDRESS *q;
+#ifdef LOCKF
+ struct flock lfd;
+#endif
+
+ oldverbose = Verbose;
+ switch (mode)
+ {
+ case SM_VERIFY:
+ Verbose = TRUE;
+ break;
+
+ case SM_QUEUE:
+ queueonly:
+ e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
+ return;
+
+ case SM_FORK:
+ if (e->e_xfp != NULL)
+ (void) fflush(e->e_xfp);
+
+# ifdef LOCKF
+ /*
+ ** Since lockf has the interesting semantic that the
+ ** lock is lost when we fork, we have to risk losing
+ ** the lock here by closing before the fork, and then
+ ** trying to get it back in the child.
+ */
+
+ if (e->e_lockfp != NULL)
+ {
+ (void) xfclose(e->e_lockfp, "sendenvelope", "lockfp");
+ e->e_lockfp = NULL;
+ }
+# endif /* LOCKF */
+
+ pid = fork();
+ if (pid < 0)
+ {
+ goto queueonly;
+ }
+ else if (pid > 0)
+ {
+ /* be sure we leave the temp files to our child */
+ e->e_id = e->e_df = NULL;
+# ifndef LOCKF
+ if (e->e_lockfp != NULL)
+ {
+ (void) xfclose(e->e_lockfp, "sendenvelope", "lockfp");
+ e->e_lockfp = NULL;
+ }
+# endif
+
+ /* close any random open files in the envelope */
+ if (e->e_dfp != NULL)
+ {
+ (void) xfclose(e->e_dfp, "sendenvelope", "dfp");
+ e->e_dfp = NULL;
+ }
+ if (e->e_xfp != NULL)
+ {
+ (void) xfclose(e->e_xfp, "sendenvelope", "xfp");
+ e->e_xfp = NULL;
+ }
+ return;
+ }
+
+ /* double fork to avoid zombies */
+ if (fork() > 0)
+ exit(EX_OK);
+
+ /* be sure we are immune from the terminal */
+ disconnect(FALSE, e);
+
+# ifdef LOCKF
+ /*
+ ** Now try to get our lock back.
+ */
+
+ lfd.l_type = F_WRLCK;
+ lfd.l_whence = lfd.l_start = lfd.l_len = 0;
+ e->e_lockfp = fopen(queuename(e, 'q'), "r+");
+ if (e->e_lockfp == NULL ||
+ fcntl(fileno(e->e_lockfp), F_SETLK, &lfd) < 0)
+ {
+ /* oops.... lost it */
+ if (tTd(13, 1))
+ printf("sendenvelope: %s lost lock: lockfp=%x, %s\n",
+ e->e_id, e->e_lockfp, errstring(errno));
+
+# ifdef LOG
+ if (LogLevel > 29)
+ syslog(LOG_NOTICE, "%s: lost lock: %m",
+ e->e_id);
+# endif /* LOG */
+ exit(EX_OK);
+ }
+# endif /* LOCKF */
+
+ /*
+ ** Close any cached connections.
+ **
+ ** We don't send the QUIT protocol because the parent
+ ** still knows about the connection.
+ **
+ ** This should only happen when delivering an error
+ ** message.
+ */
+
+ mci_flush(FALSE, NULL);
+
+ break;
+ }
+
+ /*
+ ** Run through the list and send everything.
+ */
+
+ e->e_nsent = 0;
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (mode == SM_VERIFY)
+ {
+ e->e_to = q->q_paddr;
+ if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
+ {
+ message("deliverable: mailer %s, host %s, user %s",
+ q->q_mailer->m_name,
+ q->q_host,
+ q->q_user);
+ }
+ }
+ else if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
+ {
+# ifdef QUEUE
+ /*
+ ** Checkpoint the send list every few addresses
+ */
+
+ if (e->e_nsent >= CheckpointInterval)
+ {
+ queueup(e, TRUE, FALSE);
+ e->e_nsent = 0;
+ }
+# endif /* QUEUE */
+ (void) deliver(e, q);
+ }
+ }
+ Verbose = oldverbose;
+
+ /*
+ ** Now run through and check for errors.
+ */
+
+ if (mode == SM_VERIFY)
+ {
+ return;
+ }
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (tTd(13, 3))
+ {
+ printf("Checking ");
+ printaddr(q, FALSE);
+ }
+
+ /* only send errors if the message failed */
+ if (!bitset(QBADADDR, q->q_flags) ||
+ bitset(QDONTSEND, q->q_flags))
+ continue;
+
+ e->e_flags |= EF_FATALERRS;
+
+ if (q->q_owner == NULL && strcmp(e->e_from.q_paddr, "<>") != 0)
+ (void) sendtolist(e->e_from.q_paddr, NULL,
+ &e->e_errorqueue, e);
+ }
+
+ if (mode == SM_FORK)
+ finis();
+}
+\f/*
+** DOFORK -- do a fork, retrying a couple of times on failure.
+**
+** This MUST be a macro, since after a vfork we are running
+** two processes on the same stack!!!
+**
+** Parameters:
+** none.
+**
+** Returns:
+** From a macro??? You've got to be kidding!
+**
+** Side Effects:
+** Modifies the ==> LOCAL <== variable 'pid', leaving:
+** pid of child in parent, zero in child.
+** -1 on unrecoverable error.
+**
+** Notes:
+** I'm awfully sorry this looks so awful. That's
+** vfork for you.....
+*/
+
+# define NFORKTRIES 5
+
+# ifndef FORK
+# define FORK fork
+# endif
+
+# define DOFORK(fORKfN) \
+{\
+ register int i;\
+\
+ for (i = NFORKTRIES; --i >= 0; )\
+ {\
+ pid = fORKfN();\
+ if (pid >= 0)\
+ break;\
+ if (i > 0)\
+ sleep((unsigned) NFORKTRIES - i);\
+ }\
+}
+\f/*
+** DOFORK -- simple fork interface to DOFORK.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** pid of child in parent.
+** zero in child.
+** -1 on error.
+**
+** Side Effects:
+** returns twice, once in parent and once in child.
+*/
+
+dofork()
+{
+ register int pid;
+
+ DOFORK(fork);
+ return (pid);
+}
+\f/*