📄 deliver.c
字号:
/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)deliver.c 8.82 (Berkeley) 4/18/94";#endif /* not lint */#include "sendmail.h"#include <netdb.h>#include <errno.h>#if NAMED_BIND#include <arpa/nameser.h>#include <resolv.h>extern int h_errno;#endifextern char SmtpError[];/*** 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 we have had global, fatal errors, don't bother sending ** the message at all if we are in SMTP mode. Local errors ** (e.g., a single address failing) will still cause the other ** addresses to be sent. */ if (bitset(EF_FATALERRS, e->e_flags) && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { e->e_flags |= EF_CLRQUEUE; return; } /* determine actual delivery mode */ CurrentLA = getla(); 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("\n===== SENDALL: mode %c, id %s, e_from ", mode, e->e_id); 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; e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; syserr("554 too many hops %d (%d max): from %s via %s, to %s", e->e_hopcount, MaxHopCount, e->e_from.q_paddr, RealHostName == NULL ? "localhost" : RealHostName, 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 (!bitset(EF_METOO, e->e_flags) && !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|EF_SENDRECEIPT); ee->e_flags |= EF_NORECEIPT; 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_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; q->q_flags &= ~QQUEUEUP; } for (q = ee->e_sendqueue; q != NULL; q = q->q_next) if (q->q_owner != owner) { q->q_flags |= QDONTSEND; q->q_flags &= ~QQUEUEUP; } if (e->e_df != NULL && mode != SM_VERIFY) { ee->e_dfp = NULL; ee->e_df = queuename(ee, 'd'); ee->e_df = newstr(ee->e_df); if (link(e->e_df, ee->e_df) < 0) { syserr("sendall: link(%s, %s)", e->e_df, ee->e_df); } }#ifdef LOG if (LogLevel > 4) syslog(LOG_INFO, "%s: clone %s, owner=%s", ee->e_id, e->e_id, owner);#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; e->e_flags |= EF_NORECEIPT; }# 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; if (mode != SM_VERIFY) openxscript(ee); sendenvelope(ee, mode); dropenvelope(ee); } CurEnv = e; } sendenvelope(e, mode);}sendenvelope(e, mode) register ENVELOPE *e; char mode;{ bool oldverbose; int pid; register ADDRESS *q; char *qf; char *id; /* ** If we have had global, fatal errors, don't bother sending ** the message at all if we are in SMTP mode. Local errors ** (e.g., a single address failing) will still cause the other ** addresses to be sent. */ if (bitset(EF_FATALERRS, e->e_flags) && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { e->e_flags |= EF_CLRQUEUE; return; } 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);# if !HASFLOCK /* ** Since fcntl locking has the interesting semantic that ** the lock is owned by a process, not by an open file ** descriptor, we have to flush this to the queue, and ** then restart from scratch in the child. */ /* save id for future use */ id = e->e_id; /* now drop the envelope in the parent */ e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE; dropenvelope(e); /* and reacquire in the child */ (void) dowork(id, TRUE, FALSE, e); return;# else /* HASFLOCK */ pid = fork(); if (pid < 0) { goto queueonly; } else if (pid > 0) { /* be sure we leave the temp files to our child */ /* can't call unlockqueue to avoid unlink of xfp */ if (e->e_lockfp != NULL) (void) xfclose(e->e_lockfp, "sendenvelope", "lockfp"); e->e_lockfp = NULL; /* close any random open files in the envelope */ closexscript(e); if (e->e_dfp != NULL) (void) xfclose(e->e_dfp, "sendenvelope", e->e_df); e->e_dfp = NULL; e->e_id = e->e_df = NULL; /* catch intermediate zombie */ (void) waitfor(pid); return; } /* double fork to avoid zombies */ pid = fork(); if (pid > 0) exit(EX_OK); /* be sure we are immune from the terminal */ disconnect(1, e); /* prevent parent from waiting if there was an error */ if (pid < 0) { e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE; finis(); } /* ** 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);# endif /* HASFLOCK */ break; } /* ** Run through the list and send everything. ** ** Set EF_GLOBALERRS so that error messages during delivery ** result in returned mail. */ e->e_nsent = 0; e->e_flags |= EF_GLOBALERRS; /* now run through the queue */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) {#ifdef XDEBUG char wbuf[MAXNAME + 20]; (void) sprintf(wbuf, "sendall(%s)", q->q_paddr); checkfd012(wbuf);#endif if (mode == SM_VERIFY) { e->e_to = q->q_paddr; if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) { if (q->q_host != NULL && q->q_host[0] != '\0') message("deliverable: mailer %s, host %s, user %s", q->q_mailer->m_name, q->q_host, q->q_user); else message("deliverable: mailer %s, user %s", q->q_mailer->m_name, 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;#ifdef XDEBUG checkfd012("end of sendenvelope");#endif if (mode == SM_FORK) finis();}/*** 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);\ }\}/*** 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);}/*** DELIVER -- Deliver a message to a list of addresses.**** This routine delivers to everyone on the same host as the** user on the head of the list. It is clever about mailers** that don't handle multiple users. It is NOT guaranteed** that it will deliver to all these addresses however -- so** deliver should be called once for each address on the** list.**** Parameters:** e -- the envelope to deliver.** firstto -- head of the address list to deliver to.**** Returns:** zero -- successfully delivered.** else -- some failure, see ExitStat for more info.**** Side Effects:** The standard input is passed off to someone.*/deliver(e, firstto) register ENVELOPE *e; ADDRESS *firstto;{ char *host; /* host being sent to */ char *user; /* user being sent to */ char **pvp; register char **mvp; register char *p; register MAILER *m; /* mailer for this recipient */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -