📄 deliver.c
字号:
#ifndef lintstatic char *sccsid = "@(#)deliver.c 4.1 (ULTRIX) 7/2/90";#endif lint/************************************************************************ * * * Copyright (c) 1987 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used and * * copied only in accordance with the terms of such license and * * with the inclusion of the above copyright notice. This * * software or any other copies thereof may not be provided or * * otherwise made available to any other person. No title to and * * ownership of the software is hereby transferred. * * * * This software is derived from software received from the * * University of California, Berkeley, and from Bell * * Laboratories. Use, duplication, or disclosure is subject to * * restrictions under license agreements with University of * * California and with AT&T. * * * * The information in this software is subject to change without * * notice and should not be construed as a commitment by Digital * * Equipment Corporation. * * * * Digital assumes no responsibility for the use or reliability * * of its software on equipment which is not supplied by Digital. * * * ************************************************************************/# include <sys/ioctl.h># include <signal.h># include <errno.h># include "sendmail.h"# include <sys/stat.h># include <netdb.h># include <sys/svcinfo.h># include <arpa/nameser.h># include <resolv.h>extern int h_errno;/*** 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 */ ADDRESS *ctladdr; register ADDRESS *to = firstto; bool clever = FALSE; /* running user smtp to this mailer */ ADDRESS *tochain = NULL; /* chain of users in this mailer call */ int rcode; /* response code */ char *pv[MAXPV+1]; char tobuf[MAXLINE-50]; /* text line of to people */ char buf[MAXNAME]; char tfrombuf[MAXNAME]; /* translated from person */ extern bool checkcompat(); extern ADDRESS *getctladdr(); extern char *remotename(); errno = 0; if (bitset(QDONTSEND, to->q_flags)) return (0); m = to->q_mailer; host = to->q_host;# ifdef DEBUG if (tTd(10, 1)) printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n", m->m_mno, host, to->q_user);# endif DEBUG /* ** If this mailer is expensive, and if we don't want to make ** connections now, just mark these addresses and return. ** This is useful if we want to batch connections to ** reduce load. This will cause the messages to be ** queued up, and a daemon will come along to send the ** messages later. ** This should be on a per-mailer basis. */ if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) && !Verbose) { for (; to != NULL; to = to->q_next) { if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m) continue; to->q_flags |= QQUEUEUP|QDONTSEND; e->e_to = to->q_paddr; message(Arpa_Info, "queued"); if (LogLevel > 4) logdelivery("queued"); } e->e_to = NULL; return (0); } /* ** Do initial argv setup. ** Insert the mailer name. Notice that $x expansion is ** NOT done on the mailer name. Then, if the mailer has ** a picky -f flag, we insert it as appropriate. This ** code does not check for 'pv' overflow; this places a ** manifest lower limit of 4 for MAXPV. ** The from address rewrite is expected to make ** the address relative to the other end. */ /* rewrite from address, using rewriting rules */ expand("\001f", buf, &buf[sizeof buf - 1], e); (void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE)); define('g', tfrombuf, e); /* translated sender address */ define('h', host, e); /* to host */ Errors = 0; pvp = pv; *pvp++ = m->m_argv[0]; /* insert -f or -r flag as appropriate */ if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags))) { if (bitnset(M_FOPT, m->m_flags)) *pvp++ = "-f"; else *pvp++ = "-r"; expand("\001g", buf, &buf[sizeof buf - 1], e); *pvp++ = newstr(buf); } /* ** Append the other fixed parts of the argv. These run ** up to the first entry containing "$u". There can only ** be one of these, and there are only a few more slots ** in the pv after it. */ for (mvp = m->m_argv; (p = *++mvp) != NULL; ) { while ((p = index(p, '\001')) != NULL) if (*++p == 'u') break; if (p != NULL) break; /* this entry is safe -- go ahead and process it */ expand(*mvp, buf, &buf[sizeof buf - 1], e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV - 3]) { syserr("Too many parameters to %s before $u", pv[0]); return (-1); } } /* ** If we have no substitution for the user name in the argument ** list, we know that we must supply the names otherwise -- and ** SMTP is the answer!! */ if (*mvp == NULL) { /* running SMTP */# ifdef SMTP clever = TRUE; *pvp = NULL;# else SMTP /* oops! we don't implement SMTP */ syserr("SMTP style mailer"); return (EX_SOFTWARE);# endif SMTP } /* ** At this point *mvp points to the argument with $u. We ** run through our address list and append all the addresses ** we can. If we run out of space, do not fret! We can ** always send another copy later. */ tobuf[0] = '\0'; e->e_to = tobuf; ctladdr = NULL; for (; to != NULL; to = to->q_next) { /* avoid sending multiple recipients to dumb mailers */ if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags)) break; /* if already sent or not for this host, don't send */ if (bitset(QDONTSEND, to->q_flags) || strcmp(to->q_host, host) != 0 || to->q_mailer != firstto->q_mailer) continue; /* avoid overflowing tobuf */ if (sizeof tobuf - (strlen(to->q_paddr) + strlen(tobuf) + 2) < 0) break;# ifdef DEBUG if (tTd(10, 1)) { printf("\nsend to "); printaddr(to, FALSE); }# endif DEBUG /* compute effective uid/gid when sending */ if (to->q_mailer == ProgMailer) ctladdr = getctladdr(to); user = to->q_user; e->e_to = to->q_paddr; to->q_flags |= QDONTSEND; /* ** Check to see that these people are allowed to ** talk to each other. */ if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize) { usrerr("Message is too large; %ld bytes max", m->m_maxsize); NoReturn = TRUE; giveresponse(EX_UNAVAILABLE, m, e); continue; } if (!checkcompat(to)) { giveresponse(EX_UNAVAILABLE, m, e); continue; } /* ** Strip quote bits from names if the mailer is dumb ** about them. */ if (bitnset(M_STRIPQ, m->m_flags)) { stripquotes(user, TRUE); stripquotes(host, TRUE); } else { stripquotes(user, FALSE); stripquotes(host, FALSE); } /* hack attack -- delivermail compatibility */ if (m == ProgMailer && *user == '|') user++; /* ** If an error message has already been given, don't ** bother to send to this address. ** ** >>>>>>>>>> This clause assumes that the local mailer ** >> NOTE >> cannot do any further aliasing; that ** >>>>>>>>>> function is subsumed by sendmail. */ if (bitset(QBADADDR|QQUEUEUP, to->q_flags)) continue; /* save statistics.... */ markstats(e, to); /* ** See if this user name is "special". ** If the user name has a slash in it, assume that this ** is a file -- send it off without further ado. Note ** that this type of addresses is not processed along ** with the others, so we fudge on the To person. */ if (m == LocalMailer) { if (user[0] == '/') { rcode = mailfile(user, getctladdr(to)); giveresponse(rcode, m, e); continue; } } /* ** Address is verified -- add this user to mailer ** argv, and add it to the print list of recipients. */ /* link together the chain of recipients */ to->q_tchain = tochain; tochain = to; /* create list of users for error messages */ (void) strcat(tobuf, ","); (void) strcat(tobuf, to->q_paddr); define('u', user, e); /* to user */ define('z', to->q_home, e); /* user's home */ /* ** Expand out this user into argument list. */ if (!clever) { expand(*mvp, buf, &buf[sizeof buf - 1], e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV - 2]) { /* allow some space for trailing parms */ break; } } } /* see if any addresses still exist */ if (tobuf[0] == '\0') { define('g', (char *) NULL, e); return (0); } /* print out messages as full list */ e->e_to = tobuf + 1; /* ** Fill out any parameters after the $u parameter. */ while (!clever && *++mvp != NULL) { expand(*mvp, buf, &buf[sizeof buf - 1], e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV]) syserr("deliver: pv overflow after $u for %s", pv[0]); } *pvp++ = NULL; /* ** Call the mailer. ** The argument vector gets built, pipes ** are created as necessary, and we fork & exec as ** appropriate. ** If we are running SMTP, we just need to clean up. */ if (index(host, '.') != 0) /* canonical if has dots */ _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); else /* not canonical 'cause local */ _res.options &= ~(RES_DNSRCH);# ifdef SMTP if (clever) { register int i; register struct svcinfo *svcinfo; expand("\001w", buf, &buf[sizeof buf - 1], e); rcode = EX_OK; nmx = 1; mxhosts[0] = host; if ((svcinfo = getsvc()) != NULL) for (i = 0; (svc_lastlookup = svcinfo->svcpath[SVC_HOSTS][i++]) != SVC_LAST; ) if (svc_lastlookup == SVC_BIND) { if ((nmx = getmxrr(host, mxhosts, buf, &rcode)) < 0) { if (rcode == EX_NOHOST) { /* * ignore not having a host in BIND * as we may later find it elsewhere */#ifdef DEBUG printf("getmxrr: EX_NOHOST\n");#endif DEBUG rcode = EX_OK; nmx = 1; mxhosts[0] = host; } } else {#ifdef DEBUG printf("getmxrr() found the following hosts:\n"); for (i = 0; i < nmx; i++) printf(" %s\n", mxhosts[i]);#endif /* Just done successful MX lookup */ break; } /* break out of loop now have called getmxrr() */ break; } /* send the initial SMTP protocol */ if (rcode == EX_OK) { message(Arpa_Info, "Connecting to %s (%s)...", mxhosts[0], m->m_name);#ifndef DNUMODS rcode = smtpinit(m, pv);#else DNUMODS rcode = smtpinit(m, pv, e);#endif DNUMODS } if (rcode == EX_OK) { /* send the recipient list */ tobuf[0] = '\0'; for (to = tochain; to != NULL; to = to->q_tchain) { int i; e->e_to = to->q_paddr; i = smtprcpt(to, m); if (i != EX_OK) { markfailure(e, to, i); giveresponse(i, m, e); } else { (void) strcat(tobuf, ","); (void) strcat(tobuf, to->q_paddr); } } /* now send the data */ if (tobuf[0] == '\0') e->e_to = NULL; else { e->e_to = tobuf + 1; rcode = smtpdata(m, e);#ifdef DNUMODS if( rcode == EX_OK ) { extern int SmtpMultiStatus; if( ! SmtpMultiStatus ) rcode = smtpdeliverstat(m); else for (tobuf[0] = '\0', to = tochain; to != NULL; to = to->q_tchain) { int i; e->e_to = to->q_paddr; i = smtpdeliverstat(m); if( i != EX_OK ) { markfailure(e, to, i); giveresponse(i, m, e); } else { (void) strcat(tobuf, ","); (void) strcat(tobuf, to->q_paddr); } } }#endif DNUMODS } /* now close the connection */ smtpquit(m); } } else# endif SMTP { message(Arpa_Info, "Connecting to %s (%s)...", host, m->m_name); rcode = sendoff(e, m, pv, ctladdr); } _res.options |= (RES_DEFNAMES | RES_DNSRCH); /* ** Do final status disposal. ** We check for something in tobuf for the SMTP case. ** If we got a temporary failure, arrange to queue the ** addressees. */ if (tobuf[0] != '\0') giveresponse(rcode, m, e); if (rcode != EX_OK) { for (to = tochain; to != NULL; to = to->q_tchain) markfailure(e, to, rcode); } errno = 0; define('g', (char *) NULL, e); return (rcode);}/*** MARKFAILURE -- mark a failure on a specific address.**** Parameters:** e -- the envelope we are sending.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -