122712Sdist /*
268839Seric * Copyright (c) 1983, 1995 Eric P. Allman
362532Sbostic * Copyright (c) 1988, 1993
462532Sbostic * The Regents of the University of California. All rights reserved.
533731Sbostic *
642829Sbostic * %sccs.include.redist.c%
733731Sbostic */
822712Sdist
933731Sbostic # include "sendmail.h"
1022712Sdist
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*69972Seric static char sccsid[] = "@(#)srvrsmtp.c 8.83 (Berkeley) 06/21/95 (with SMTP)";
1433731Sbostic #else
15*69972Seric static char sccsid[] = "@(#)srvrsmtp.c 8.83 (Berkeley) 06/21/95 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic
199339Seric # include <errno.h>
204549Seric
2133731Sbostic # ifdef SMTP
224556Seric
234549Seric /*
244549Seric ** SMTP -- run the SMTP protocol.
254549Seric **
264549Seric ** Parameters:
274549Seric ** none.
284549Seric **
294549Seric ** Returns:
304549Seric ** never.
314549Seric **
324549Seric ** Side Effects:
334549Seric ** Reads commands from the input channel and processes
344549Seric ** them.
354549Seric */
364549Seric
374549Seric struct cmd
384549Seric {
394549Seric char *cmdname; /* command name */
404549Seric int cmdcode; /* internal code, see below */
414549Seric };
424549Seric
434549Seric /* values for cmdcode */
444549Seric # define CMDERROR 0 /* bad command */
454549Seric # define CMDMAIL 1 /* mail -- designate sender */
464976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */
474549Seric # define CMDDATA 3 /* data -- send message text */
489339Seric # define CMDRSET 4 /* rset -- reset state */
499339Seric # define CMDVRFY 5 /* vrfy -- verify address */
5058092Seric # define CMDEXPN 6 /* expn -- expand address */
519339Seric # define CMDNOOP 7 /* noop -- do nothing */
529339Seric # define CMDQUIT 8 /* quit -- close connection and die */
539339Seric # define CMDHELO 9 /* helo -- be polite */
5458092Seric # define CMDHELP 10 /* help -- give usage info */
5558323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
5658092Seric /* non-standard commands */
5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */
5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */
5964685Seric /* use this to catch and log "door handle" attempts on your system */
6064685Seric # define CMDLOGBOGUS 23 /* bogus command that should be logged */
6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6258092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */
6358092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */
644549Seric
654549Seric static struct cmd CmdTab[] =
664549Seric {
674549Seric "mail", CMDMAIL,
684976Seric "rcpt", CMDRCPT,
694549Seric "data", CMDDATA,
704549Seric "rset", CMDRSET,
714549Seric "vrfy", CMDVRFY,
7258092Seric "expn", CMDEXPN,
734549Seric "help", CMDHELP,
744549Seric "noop", CMDNOOP,
754549Seric "quit", CMDQUIT,
764976Seric "helo", CMDHELO,
7758323Seric "ehlo", CMDEHLO,
788544Seric "verb", CMDVERB,
799314Seric "onex", CMDONEX,
8036230Skarels /*
8136230Skarels * remaining commands are here only
8236230Skarels * to trap and log attempts to use them
8336230Skarels */
849339Seric "showq", CMDDBGQSHOW,
858544Seric "debug", CMDDBGDEBUG,
8664685Seric "wiz", CMDLOGBOGUS,
874549Seric NULL, CMDERROR,
884549Seric };
894549Seric
909378Seric bool OneXact = FALSE; /* one xaction only this run */
9165017Seric char *CurSmtpClient; /* who's at the other end of channel */
9211146Seric
9363937Seric static char *skipword();
9463937Seric
9566325Seric
9666283Seric #define MAXBADCOMMANDS 25 /* maximum number of bad commands */
9766283Seric
9869748Seric void
smtp(e)9955012Seric smtp(e)
10055012Seric register ENVELOPE *e;
1014549Seric {
1024549Seric register char *p;
1038544Seric register struct cmd *c;
1044549Seric char *cmd;
1055003Seric auto ADDRESS *vrfyqueue;
10612612Seric ADDRESS *a;
10758109Seric bool gotmail; /* mail command received */
10858092Seric bool gothello; /* helo command received */
10958092Seric bool vrfy; /* set if this is a vrfy command */
11058323Seric char *protocol; /* sending protocol */
11159016Seric char *sendinghost; /* sending hostname */
11266005Seric char *peerhostname; /* name of SMTP peer or "localhost" */
11358333Seric auto char *delimptr;
11458714Seric char *id;
11568433Seric int nrcpts = 0; /* number of RCPT commands */
11663787Seric bool doublequeue;
11766283Seric int badcommands = 0; /* count of bad commands */
1188544Seric char inp[MAXLINE];
11957232Seric char cmdbuf[MAXLINE];
12024943Seric extern ENVELOPE BlankEnvelope;
12169748Seric extern void help __P((char *));
1224549Seric
12359066Seric if (fileno(OutChannel) != fileno(stdout))
1247363Seric {
1257363Seric /* arrange for debugging output to go to remote host */
12659066Seric (void) dup2(fileno(OutChannel), fileno(stdout));
1277363Seric }
12855012Seric settime(e);
12966005Seric peerhostname = RealHostName;
13066005Seric if (peerhostname == NULL)
13166005Seric peerhostname = "localhost";
13266005Seric CurHostName = peerhostname;
13365017Seric CurSmtpClient = macvalue('_', e);
13465017Seric if (CurSmtpClient == NULL)
13566003Seric CurSmtpClient = CurHostName;
13665017Seric
13765017Seric setproctitle("server %s startup", CurSmtpClient);
13869792Seric #ifdef LOG
13969792Seric if (LogLevel > 11)
14069792Seric {
14169792Seric /* log connection information */
14269792Seric syslog(LOG_INFO, "SMTP connect from %s (%s)",
14369792Seric CurSmtpClient, anynet_ntoa(&RealHostAddr));
14469792Seric }
14569792Seric #endif
14668769Seric
14768769Seric /* output the first line, inserting "ESMTP" as second word */
14869792Seric expand("\201e", inp, sizeof inp, e);
14968769Seric p = strchr(inp, '\n');
15068769Seric if (p != NULL)
15168769Seric *p++ = '\0';
15268769Seric id = strchr(inp, ' ');
15368769Seric if (id == NULL)
15468769Seric id = &inp[strlen(inp)];
15568769Seric cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
15668769Seric message(cmd, id - inp, inp, id);
15768769Seric
15868769Seric /* output remaining lines */
15968769Seric while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
16064496Seric {
16168769Seric *p++ = '\0';
16269106Seric if (isascii(*id) && isspace(*id))
16369106Seric id++;
16468769Seric message("220-%s", id);
16564496Seric }
16668769Seric if (id != NULL)
16769106Seric {
16869106Seric if (isascii(*id) && isspace(*id))
16969106Seric id++;
17068769Seric message("220 %s", id);
17169106Seric }
17266745Seric
17358330Seric protocol = NULL;
17459016Seric sendinghost = macvalue('s', e);
17558082Seric gothello = FALSE;
17658330Seric gotmail = FALSE;
1774549Seric for (;;)
1784549Seric {
17912612Seric /* arrange for backout */
18065751Seric if (setjmp(TopFrame) > 0)
18159058Seric {
18265751Seric /* if() nesting is necessary for Cray UNICOS */
18365751Seric if (InChild)
18465751Seric {
18565751Seric QuickAbort = FALSE;
18665751Seric SuprErrs = TRUE;
18765751Seric finis();
18865751Seric }
18959058Seric }
19012612Seric QuickAbort = FALSE;
19112612Seric HoldErrs = FALSE;
19251951Seric LogUsrErrs = FALSE;
19363843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
19412612Seric
1957356Seric /* setup for the read */
19655012Seric e->e_to = NULL;
1974577Seric Errors = 0;
1987275Seric (void) fflush(stdout);
1997356Seric
2007356Seric /* read the input line */
20161093Seric SmtpPhase = "server cmd read";
20269788Seric setproctitle("server %s cmd read", CurSmtpClient);
20361093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
20461093Seric SmtpPhase);
2057356Seric
2067685Seric /* handle errors */
2077356Seric if (p == NULL)
2087356Seric {
2094549Seric /* end of file, just die */
21066017Seric disconnect(1, e);
21158151Seric message("421 %s Lost input channel from %s",
21265017Seric MyHostName, CurSmtpClient);
21355464Seric #ifdef LOG
21463843Seric if (LogLevel > (gotmail ? 1 : 19))
21555464Seric syslog(LOG_NOTICE, "lost input channel from %s",
21665017Seric CurSmtpClient);
21755464Seric #endif
21858069Seric if (InChild)
21958069Seric ExitStat = EX_QUIT;
2204549Seric finis();
2214549Seric }
2224549Seric
2234549Seric /* clean up end of line */
2244558Seric fixcrlf(inp, TRUE);
2254549Seric
2264713Seric /* echo command to transcript */
22755012Seric if (e->e_xfp != NULL)
22855012Seric fprintf(e->e_xfp, "<<< %s\n", inp);
2294713Seric
23059060Seric if (e->e_id == NULL)
23165058Seric setproctitle("%s: %.80s", CurSmtpClient, inp);
23259060Seric else
23365058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
23459060Seric
2354549Seric /* break off command */
23658050Seric for (p = inp; isascii(*p) && isspace(*p); p++)
2374549Seric continue;
23857232Seric cmd = cmdbuf;
23958050Seric while (*p != '\0' &&
24058050Seric !(isascii(*p) && isspace(*p)) &&
24158050Seric cmd < &cmdbuf[sizeof cmdbuf - 2])
24224981Seric *cmd++ = *p++;
24324981Seric *cmd = '\0';
2444549Seric
24525691Seric /* throw away leading whitespace */
24658050Seric while (isascii(*p) && isspace(*p))
24725691Seric p++;
24825691Seric
2494549Seric /* decode command */
2504549Seric for (c = CmdTab; c->cmdname != NULL; c++)
2514549Seric {
25233725Sbostic if (!strcasecmp(c->cmdname, cmdbuf))
2534549Seric break;
2544549Seric }
2554549Seric
25651954Seric /* reset errors */
25751954Seric errno = 0;
25851954Seric
2594549Seric /* process command */
2604549Seric switch (c->cmdcode)
2614549Seric {
2624976Seric case CMDHELO: /* hello -- introduce yourself */
26358323Seric case CMDEHLO: /* extended hello */
26458323Seric if (c->cmdcode == CMDEHLO)
26558323Seric {
26658323Seric protocol = "ESMTP";
26761093Seric SmtpPhase = "server EHLO";
26858323Seric }
26958323Seric else
27058323Seric {
27158323Seric protocol = "SMTP";
27261093Seric SmtpPhase = "server HELO";
27358323Seric }
27467445Seric
27567445Seric /* check for valid domain name (re 1123 5.2.5) */
27667445Seric if (*p == '\0')
27767445Seric {
27867445Seric message("501 %s requires domain address",
27967445Seric cmdbuf);
28067445Seric break;
28167445Seric }
28267445Seric else
28367445Seric {
28467445Seric register char *q;
28567445Seric
28667445Seric for (q = p; *q != '\0'; q++)
28767445Seric {
28867445Seric if (!isascii(*q))
28967445Seric break;
29067445Seric if (isalnum(*q))
29167445Seric continue;
29267445Seric if (strchr("[].-_#", *q) == NULL)
29367445Seric break;
29467445Seric }
29567445Seric if (*q != '\0')
29667445Seric {
29767445Seric message("501 Invalid domain name");
29867445Seric break;
29967445Seric }
30067445Seric }
30167445Seric
30259016Seric sendinghost = newstr(p);
30360210Seric gothello = TRUE;
30460210Seric if (c->cmdcode != CMDEHLO)
30560239Seric {
30660239Seric /* print old message and be done with it */
30760239Seric message("250 %s Hello %s, pleased to meet you",
30865017Seric MyHostName, CurSmtpClient);
30960210Seric break;
31060239Seric }
31160239Seric
31260239Seric /* print extended message and brag */
31360239Seric message("250-%s Hello %s, pleased to meet you",
31466760Seric MyHostName, CurSmtpClient);
31558323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags))
31658323Seric message("250-EXPN");
31769480Seric #if MIME8TO7
31867417Seric message("250-8BITMIME");
31969480Seric #endif
32064359Seric if (MaxMessageSize > 0)
32164359Seric message("250-SIZE %ld", MaxMessageSize);
32259271Seric else
32359271Seric message("250-SIZE");
32468848Seric #if DSN
32569858Seric message("250-X-DSN-04 (Draft of May 29, 1995)");
32668028Seric #endif
32758323Seric message("250 HELP");
3284976Seric break;
3294976Seric
3304549Seric case CMDMAIL: /* mail -- designate sender */
33161093Seric SmtpPhase = "server MAIL";
33224943Seric
3339314Seric /* check for validity of this command */
33458789Seric if (!gothello)
33558082Seric {
33658957Seric /* set sending host to our known value */
33759016Seric if (sendinghost == NULL)
33866005Seric sendinghost = peerhostname;
33958957Seric
34058789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
34158821Seric {
34258789Seric message("503 Polite people say HELO first");
34358821Seric break;
34458821Seric }
34558082Seric }
34658109Seric if (gotmail)
3474558Seric {
34858151Seric message("503 Sender already specified");
34963843Seric if (InChild)
35063843Seric finis();
3514558Seric break;
3524558Seric }
3539339Seric if (InChild)
3549339Seric {
35536230Skarels errno = 0;
35658151Seric syserr("503 Nested MAIL command: MAIL %s", p);
35758069Seric finis();
3589339Seric }
3599339Seric
3609339Seric /* fork a subprocess to process this command */
36155012Seric if (runinchild("SMTP-MAIL", e) > 0)
3629339Seric break;
36363753Seric if (!gothello)
36463753Seric {
36563753Seric auth_warning(e,
36666005Seric "Host %s didn't use HELO protocol",
36769788Seric CurSmtpClient);
36863753Seric }
36965947Seric #ifdef PICKY_HELO_CHECK
37066005Seric if (strcasecmp(sendinghost, peerhostname) != 0 &&
37166005Seric (strcasecmp(peerhostname, "localhost") != 0 ||
37265823Seric strcasecmp(sendinghost, MyHostName) != 0))
37365823Seric {
37465823Seric auth_warning(e, "Host %s claimed to be %s",
37569788Seric CurSmtpClient, sendinghost);
37665823Seric }
37765947Seric #endif
37865823Seric
37958323Seric if (protocol == NULL)
38058323Seric protocol = "SMTP";
38158323Seric define('r', protocol, e);
38259016Seric define('s', sendinghost, e);
38355012Seric initsys(e);
38459747Seric nrcpts = 0;
38568582Seric e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
38665058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3879339Seric
3889339Seric /* child -- go do the processing */
3894549Seric p = skipword(p, "from");
3904549Seric if (p == NULL)
3914549Seric break;
39257977Seric if (setjmp(TopFrame) > 0)
39358147Seric {
39458147Seric /* this failed -- undo work */
39558147Seric if (InChild)
39659058Seric {
39759058Seric QuickAbort = FALSE;
39859058Seric SuprErrs = TRUE;
39963787Seric e->e_flags &= ~EF_FATALERRS;
40058147Seric finis();
40159058Seric }
40257977Seric break;
40358147Seric }
40457977Seric QuickAbort = TRUE;
40558333Seric
40658333Seric /* must parse sender first */
40758333Seric delimptr = NULL;
40858704Seric setsender(p, e, &delimptr, FALSE);
40958333Seric p = delimptr;
41058333Seric if (p != NULL && *p != '\0')
41158333Seric *p++ = '\0';
41258333Seric
41366325Seric /* check for possible spoofing */
41466325Seric if (RealUid != 0 && OpMode == MD_SMTP &&
41569757Seric !wordinclass(RealUserName, 't') &&
41667473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
41767473Seric strcmp(e->e_from.q_user, RealUserName) != 0)
41866325Seric {
41966325Seric auth_warning(e, "%s owned process doing -bs",
42066325Seric RealUserName);
42166325Seric }
42266325Seric
42358333Seric /* now parse ESMTP arguments */
42468560Seric e->e_msgsize = 0;
42566764Seric while (p != NULL && *p != '\0')
42658333Seric {
42758333Seric char *kp;
42866304Seric char *vp = NULL;
42969748Seric extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
43058333Seric
43158333Seric /* locate the beginning of the keyword */
43258333Seric while (isascii(*p) && isspace(*p))
43358333Seric p++;
43458333Seric if (*p == '\0')
43558333Seric break;
43658333Seric kp = p;
43758333Seric
43858333Seric /* skip to the value portion */
43958333Seric while (isascii(*p) && isalnum(*p) || *p == '-')
44058333Seric p++;
44158333Seric if (*p == '=')
44258333Seric {
44358333Seric *p++ = '\0';
44458333Seric vp = p;
44558333Seric
44658333Seric /* skip to the end of the value */
44758333Seric while (*p != '\0' && *p != ' ' &&
44858333Seric !(isascii(*p) && iscntrl(*p)) &&
44958333Seric *p != '=')
45058333Seric p++;
45158333Seric }
45258333Seric
45358333Seric if (*p != '\0')
45458333Seric *p++ = '\0';
45558333Seric
45658333Seric if (tTd(19, 1))
45766764Seric printf("MAIL: got arg %s=\"%s\"\n", kp,
45858333Seric vp == NULL ? "<null>" : vp);
45958333Seric
46068559Seric mail_esmtp_args(kp, vp, e);
46158333Seric }
46259284Seric
46368560Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
46459284Seric {
46559284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)",
46659284Seric MaxMessageSize);
46759284Seric /* NOTREACHED */
46859284Seric }
46958333Seric
47069801Seric if (!enoughdiskspace(e->e_msgsize))
47158333Seric {
47258333Seric message("452 Insufficient disk space; try again later");
47358333Seric break;
47458333Seric }
47558151Seric message("250 Sender ok");
47658147Seric gotmail = TRUE;
4774549Seric break;
4784549Seric
4794976Seric case CMDRCPT: /* rcpt -- designate recipient */
48058850Seric if (!gotmail)
48158850Seric {
48258850Seric usrerr("503 Need MAIL before RCPT");
48358850Seric break;
48458850Seric }
48561093Seric SmtpPhase = "server RCPT";
48612612Seric if (setjmp(TopFrame) > 0)
48714785Seric {
48855012Seric e->e_flags &= ~EF_FATALERRS;
48912612Seric break;
49014785Seric }
49112612Seric QuickAbort = TRUE;
49251951Seric LogUsrErrs = TRUE;
49358093Seric
49459699Seric if (e->e_sendmode != SM_DELIVER)
49559699Seric e->e_flags |= EF_VRFYONLY;
49658919Seric
4974549Seric p = skipword(p, "to");
4984549Seric if (p == NULL)
4994549Seric break;
50067880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
50112612Seric if (a == NULL)
50212612Seric break;
50367880Seric p = delimptr;
50467880Seric
50567880Seric /* now parse ESMTP arguments */
50667880Seric while (p != NULL && *p != '\0')
50767880Seric {
50867880Seric char *kp;
50967880Seric char *vp = NULL;
51069748Seric extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
51167880Seric
51267880Seric /* locate the beginning of the keyword */
51367880Seric while (isascii(*p) && isspace(*p))
51467880Seric p++;
51567880Seric if (*p == '\0')
51667880Seric break;
51767880Seric kp = p;
51867880Seric
51967880Seric /* skip to the value portion */
52067880Seric while (isascii(*p) && isalnum(*p) || *p == '-')
52167880Seric p++;
52267880Seric if (*p == '=')
52367880Seric {
52467880Seric *p++ = '\0';
52567880Seric vp = p;
52667880Seric
52767880Seric /* skip to the end of the value */
52867880Seric while (*p != '\0' && *p != ' ' &&
52967880Seric !(isascii(*p) && iscntrl(*p)) &&
53067880Seric *p != '=')
53167880Seric p++;
53267880Seric }
53367880Seric
53467880Seric if (*p != '\0')
53567880Seric *p++ = '\0';
53667880Seric
53767880Seric if (tTd(19, 1))
53867880Seric printf("RCPT: got arg %s=\"%s\"\n", kp,
53967880Seric vp == NULL ? "<null>" : vp);
54067880Seric
54167963Seric rcpt_esmtp_args(a, kp, vp, e);
54267880Seric }
54367980Seric
54467980Seric /* save in recipient list after ESMTP mods */
54567982Seric a = recipient(a, &e->e_sendqueue, 0, e);
54667980Seric
54712612Seric if (Errors != 0)
54812612Seric break;
54912612Seric
55012612Seric /* no errors during parsing, but might be a duplicate */
55155012Seric e->e_to = p;
55212612Seric if (!bitset(QBADADDR, a->q_flags))
55359747Seric {
55464718Seric message("250 Recipient ok%s",
55564718Seric bitset(QQUEUEUP, a->q_flags) ?
55664718Seric " (will queue)" : "");
55759747Seric nrcpts++;
55859747Seric }
55912612Seric else
5604549Seric {
56112612Seric /* punt -- should keep message in ADDRESS.... */
56258151Seric message("550 Addressee unknown");
5634549Seric }
56455012Seric e->e_to = NULL;
5654549Seric break;
5664549Seric
5674549Seric case CMDDATA: /* data -- text of mail */
56861093Seric SmtpPhase = "server DATA";
56958109Seric if (!gotmail)
5704549Seric {
57158151Seric message("503 Need MAIL command");
5724976Seric break;
5734549Seric }
57464718Seric else if (nrcpts <= 0)
5754549Seric {
57658151Seric message("503 Need RCPT (recipient)");
5774976Seric break;
5784549Seric }
5794976Seric
58058929Seric /* check to see if we need to re-expand aliases */
58163787Seric /* also reset QBADADDR on already-diagnosted addrs */
58263787Seric doublequeue = FALSE;
58358929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next)
58458929Seric {
58558929Seric if (bitset(QVERIFIED, a->q_flags))
58663787Seric {
58763787Seric /* need to re-expand aliases */
58863787Seric doublequeue = TRUE;
58963787Seric }
59063787Seric if (bitset(QBADADDR, a->q_flags))
59163787Seric {
59263787Seric /* make this "go away" */
59363787Seric a->q_flags |= QDONTSEND;
59463787Seric a->q_flags &= ~QBADADDR;
59563787Seric }
59658929Seric }
59758929Seric
5984976Seric /* collect the text of the message */
59924943Seric SmtpPhase = "collect";
60068692Seric buffer_errors();
60167546Seric collect(InChannel, TRUE, doublequeue, NULL, e);
60268692Seric flush_errors(TRUE);
60364766Seric if (Errors != 0)
60464766Seric goto abortmessage;
60567131Seric
60668582Seric /* make sure we actually do delivery */
60768582Seric e->e_flags &= ~EF_CLRQUEUE;
60868582Seric
60967131Seric /* from now on, we have to operate silently */
61068692Seric buffer_errors();
61167131Seric e->e_errormode = EM_MAIL;
6124976Seric
6138238Seric /*
6148238Seric ** Arrange to send to everyone.
6158238Seric ** If sending to multiple people, mail back
6168238Seric ** errors rather than reporting directly.
6178238Seric ** In any case, don't mail back errors for
6188238Seric ** anything that has happened up to
6198238Seric ** now (the other end will do this).
62010197Seric ** Truncate our transcript -- the mail has gotten
62110197Seric ** to us successfully, and if we have
62210197Seric ** to mail this back, it will be easier
62310197Seric ** on the reader.
6248238Seric ** Then send to everyone.
6258238Seric ** Finally give a reply code. If an error has
6268238Seric ** already been given, don't mail a
6278238Seric ** message back.
6289339Seric ** We goose error returns by clearing error bit.
6298238Seric */
6308238Seric
63124943Seric SmtpPhase = "delivery";
63255012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
63358714Seric id = e->e_id;
6344976Seric
63567131Seric if (doublequeue)
63659730Seric {
63767131Seric /* make sure it is in the queue */
63867131Seric queueup(e, TRUE, FALSE);
63968035Seric if (e->e_sendmode == SM_QUEUE)
64068035Seric e->e_flags |= EF_KEEPQUEUE;
64159730Seric }
6428238Seric else
64358919Seric {
64467131Seric /* send to all recipients */
64567131Seric sendall(e, SM_DEFAULT);
64667131Seric }
64767131Seric e->e_to = NULL;
64859747Seric
64967131Seric /* issue success message */
65067131Seric message("250 %s Message accepted for delivery", id);
65164296Seric
65267131Seric /* if we just queued, poke it */
65367131Seric if (doublequeue && e->e_sendmode != SM_QUEUE)
65467131Seric {
65567131Seric extern pid_t dowork();
65667131Seric
65767131Seric unlockqueue(e);
65867131Seric (void) dowork(id, TRUE, TRUE, e);
65958919Seric }
66058883Seric
66159747Seric abortmessage:
6629339Seric /* if in a child, pop back to our parent */
6639339Seric if (InChild)
6649339Seric finis();
66524943Seric
66624943Seric /* clean up a bit */
66758109Seric gotmail = FALSE;
66855012Seric dropenvelope(e);
66958179Seric CurEnv = e = newenvelope(e, CurEnv);
67055012Seric e->e_flags = BlankEnvelope.e_flags;
6714549Seric break;
6724549Seric
6734549Seric case CMDRSET: /* rset -- reset state */
67458151Seric message("250 Reset state");
67568603Seric
67668603Seric /* arrange to ignore any current send list */
67768603Seric e->e_sendqueue = NULL;
67864359Seric e->e_flags |= EF_CLRQUEUE;
6799339Seric if (InChild)
6809339Seric finis();
68158109Seric
68258109Seric /* clean up a bit */
68358109Seric gotmail = FALSE;
68458109Seric dropenvelope(e);
68558179Seric CurEnv = e = newenvelope(e, CurEnv);
6869339Seric break;
6874549Seric
6884549Seric case CMDVRFY: /* vrfy -- verify address */
68958092Seric case CMDEXPN: /* expn -- expand address */
69058092Seric vrfy = c->cmdcode == CMDVRFY;
69158092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
69258092Seric PrivacyFlags))
69358082Seric {
69458412Seric if (vrfy)
69567160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
69658412Seric else
69765192Seric message("502 Sorry, we do not allow this operation");
69865017Seric #ifdef LOG
69965017Seric if (LogLevel > 5)
70065017Seric syslog(LOG_INFO, "%s: %s [rejected]",
70165017Seric CurSmtpClient, inp);
70265017Seric #endif
70358082Seric break;
70458082Seric }
70558082Seric else if (!gothello &&
70658092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
70758092Seric PrivacyFlags))
70858082Seric {
70958151Seric message("503 I demand that you introduce yourself first");
71058082Seric break;
71158082Seric }
71258092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
7139339Seric break;
71455173Seric #ifdef LOG
71558020Seric if (LogLevel > 5)
71665017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
71755173Seric #endif
7185003Seric vrfyqueue = NULL;
7197762Seric QuickAbort = TRUE;
72058092Seric if (vrfy)
72158092Seric e->e_flags |= EF_VRFYONLY;
72262373Seric while (*p != '\0' && isascii(*p) && isspace(*p))
72367615Seric p++;
72462373Seric if (*p == '\0')
72562373Seric {
72662373Seric message("501 Argument required");
72762373Seric Errors++;
72862373Seric }
72962373Seric else
73062373Seric {
73167990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
73262373Seric }
7337762Seric if (Errors != 0)
7349339Seric {
7359339Seric if (InChild)
7369339Seric finis();
7377762Seric break;
7389339Seric }
73962373Seric if (vrfyqueue == NULL)
74062373Seric {
74162373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
74262373Seric }
7435003Seric while (vrfyqueue != NULL)
7445003Seric {
74569748Seric extern void printvrfyaddr __P((ADDRESS *, bool));
74669748Seric
74763971Seric a = vrfyqueue;
74863971Seric while ((a = a->q_next) != NULL &&
74963971Seric bitset(QDONTSEND|QBADADDR, a->q_flags))
75063971Seric continue;
7517685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
75258151Seric printvrfyaddr(vrfyqueue, a == NULL);
75363847Seric vrfyqueue = vrfyqueue->q_next;
7545003Seric }
7559339Seric if (InChild)
7569339Seric finis();
7574549Seric break;
7584549Seric
7594549Seric case CMDHELP: /* help -- give user info */
7604577Seric help(p);
7614549Seric break;
7624549Seric
7634549Seric case CMDNOOP: /* noop -- do nothing */
76464122Seric message("250 OK");
7654549Seric break;
7664549Seric
7674549Seric case CMDQUIT: /* quit -- leave mail */
76858151Seric message("221 %s closing connection", MyHostName);
76961051Seric
77066283Seric doquit:
77168603Seric /* arrange to ignore any current send list */
77268603Seric e->e_sendqueue = NULL;
77368603Seric
77461051Seric /* avoid future 050 messages */
77566017Seric disconnect(1, e);
77661051Seric
7779339Seric if (InChild)
7789339Seric ExitStat = EX_QUIT;
7794549Seric finis();
7804549Seric
7818544Seric case CMDVERB: /* set verbose mode */
78259957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags))
78359957Seric {
78459957Seric /* this would give out the same info */
78559957Seric message("502 Verbose unavailable");
78659957Seric break;
78759957Seric }
7888544Seric Verbose = TRUE;
78958734Seric e->e_sendmode = SM_DELIVER;
79059957Seric message("250 Verbose mode");
7918544Seric break;
7928544Seric
7939314Seric case CMDONEX: /* doing one transaction only */
7949378Seric OneXact = TRUE;
79559957Seric message("250 Only one transaction");
7969314Seric break;
7979314Seric
79836230Skarels # ifdef SMTPDEBUG
7999339Seric case CMDDBGQSHOW: /* show queues */
8006907Seric printf("Send Queue=");
80155012Seric printaddr(e->e_sendqueue, TRUE);
8025003Seric break;
8037275Seric
8047275Seric case CMDDBGDEBUG: /* set debug mode */
8057676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
8067676Seric tTflag(p);
80758151Seric message("200 Debug set");
8087275Seric break;
8097275Seric
81036230Skarels # else /* not SMTPDEBUG */
81136230Skarels case CMDDBGQSHOW: /* show queues */
81236230Skarels case CMDDBGDEBUG: /* set debug mode */
81364685Seric # endif /* SMTPDEBUG */
81464685Seric case CMDLOGBOGUS: /* bogus command */
81536233Skarels # ifdef LOG
81658308Seric if (LogLevel > 0)
81764685Seric syslog(LOG_CRIT,
81858020Seric "\"%s\" command from %s (%s)",
81969788Seric c->cmdname, CurSmtpClient,
82058755Seric anynet_ntoa(&RealHostAddr));
82136233Skarels # endif
82236230Skarels /* FALL THROUGH */
82336230Skarels
8244549Seric case CMDERROR: /* unknown command */
82566283Seric if (++badcommands > MAXBADCOMMANDS)
82666283Seric {
82766283Seric message("421 %s Too many bad commands; closing connection",
82866283Seric MyHostName);
82966283Seric goto doquit;
83066283Seric }
83166283Seric
83258151Seric message("500 Command unrecognized");
8334549Seric break;
8344549Seric
8354549Seric default:
83636230Skarels errno = 0;
83758151Seric syserr("500 smtp: unknown code %d", c->cmdcode);
8384549Seric break;
8394549Seric }
8404549Seric }
8414549Seric }
8424549Seric /*
8434549Seric ** SKIPWORD -- skip a fixed word.
8444549Seric **
8454549Seric ** Parameters:
8464549Seric ** p -- place to start looking.
8474549Seric ** w -- word to skip.
8484549Seric **
8494549Seric ** Returns:
8504549Seric ** p following w.
8514549Seric ** NULL on error.
8524549Seric **
8534549Seric ** Side Effects:
8544549Seric ** clobbers the p data area.
8554549Seric */
8564549Seric
8574549Seric static char *
skipword(p,w)8584549Seric skipword(p, w)
8594549Seric register char *p;
8604549Seric char *w;
8614549Seric {
8624549Seric register char *q;
86366005Seric char *firstp = p;
8644549Seric
8654549Seric /* find beginning of word */
86658050Seric while (isascii(*p) && isspace(*p))
8674549Seric p++;
8684549Seric q = p;
8694549Seric
8704549Seric /* find end of word */
87158050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8724549Seric p++;
87358050Seric while (isascii(*p) && isspace(*p))
8744549Seric *p++ = '\0';
8754549Seric if (*p != ':')
8764549Seric {
8774549Seric syntax:
87866005Seric message("501 Syntax error in parameters scanning \"%s\"",
87966005Seric firstp);
8804549Seric Errors++;
8814549Seric return (NULL);
8824549Seric }
8834549Seric *p++ = '\0';
88458050Seric while (isascii(*p) && isspace(*p))
8854549Seric p++;
8864549Seric
88762373Seric if (*p == '\0')
88862373Seric goto syntax;
88962373Seric
8904549Seric /* see if the input word matches desired word */
89133725Sbostic if (strcasecmp(q, w))
8924549Seric goto syntax;
8934549Seric
8944549Seric return (p);
8954549Seric }
8964577Seric /*
89768559Seric ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
89868559Seric **
89968559Seric ** Parameters:
90068559Seric ** kp -- the parameter key.
90168559Seric ** vp -- the value of that parameter.
90268559Seric ** e -- the envelope.
90368559Seric **
90468559Seric ** Returns:
90568559Seric ** none.
90668559Seric */
90768559Seric
90869748Seric void
mail_esmtp_args(kp,vp,e)90968559Seric mail_esmtp_args(kp, vp, e)
91068559Seric char *kp;
91168559Seric char *vp;
91268559Seric ENVELOPE *e;
91368559Seric {
91468559Seric if (strcasecmp(kp, "size") == 0)
91568559Seric {
91668559Seric if (vp == NULL)
91768559Seric {
91868559Seric usrerr("501 SIZE requires a value");
91968559Seric /* NOTREACHED */
92068559Seric }
92168890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
92268560Seric e->e_msgsize = strtoul(vp, (char **) NULL, 10);
92368559Seric # else
92468560Seric e->e_msgsize = strtol(vp, (char **) NULL, 10);
92568559Seric # endif
92668559Seric }
92768559Seric else if (strcasecmp(kp, "body") == 0)
92868559Seric {
92968559Seric if (vp == NULL)
93068559Seric {
93168559Seric usrerr("501 BODY requires a value");
93268559Seric /* NOTREACHED */
93368559Seric }
934*69972Seric else if (strcasecmp(vp, "8bitmime") == 0)
93568559Seric {
93668559Seric SevenBitInput = FALSE;
93768559Seric }
93868559Seric else if (strcasecmp(vp, "7bit") == 0)
93968559Seric {
94068559Seric SevenBitInput = TRUE;
94168559Seric }
94268559Seric else
94368559Seric {
94468559Seric usrerr("501 Unknown BODY type %s",
94568559Seric vp);
94668559Seric /* NOTREACHED */
94768559Seric }
94868559Seric e->e_bodytype = newstr(vp);
94968559Seric }
95068559Seric else if (strcasecmp(kp, "envid") == 0)
95168559Seric {
95268559Seric if (vp == NULL)
95368559Seric {
95468559Seric usrerr("501 ENVID requires a value");
95568559Seric /* NOTREACHED */
95668559Seric }
95768583Seric if (!xtextok(vp))
95868583Seric {
95968583Seric usrerr("501 Syntax error in ENVID parameter value");
96068583Seric /* NOTREACHED */
96168583Seric }
96268583Seric if (e->e_envid != NULL)
96368583Seric {
96468583Seric usrerr("501 Duplicate ENVID parameter");
96568583Seric /* NOTREACHED */
96668583Seric }
96768559Seric e->e_envid = newstr(vp);
96868559Seric }
96968559Seric else if (strcasecmp(kp, "ret") == 0)
97068559Seric {
97168559Seric if (vp == NULL)
97268559Seric {
97368559Seric usrerr("501 RET requires a value");
97468559Seric /* NOTREACHED */
97568559Seric }
97668583Seric if (bitset(EF_RET_PARAM, e->e_flags))
97768583Seric {
97868583Seric usrerr("501 Duplicate RET parameter");
97968583Seric /* NOTREACHED */
98068583Seric }
98168559Seric e->e_flags |= EF_RET_PARAM;
98268559Seric if (strcasecmp(vp, "hdrs") == 0)
98368559Seric e->e_flags |= EF_NO_BODY_RETN;
98468559Seric else if (strcasecmp(vp, "full") != 0)
98568559Seric {
98668559Seric usrerr("501 Bad argument \"%s\" to RET", vp);
98768559Seric /* NOTREACHED */
98868559Seric }
98968559Seric }
99068559Seric else
99168559Seric {
99268559Seric usrerr("501 %s parameter unrecognized", kp);
99368559Seric /* NOTREACHED */
99468559Seric }
99568559Seric }
99668559Seric /*
99767963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
99867963Seric **
99967963Seric ** Parameters:
100067963Seric ** a -- the address corresponding to the To: parameter.
100167963Seric ** kp -- the parameter key.
100267963Seric ** vp -- the value of that parameter.
100367963Seric ** e -- the envelope.
100467963Seric **
100567963Seric ** Returns:
100667963Seric ** none.
100767963Seric */
100867963Seric
100969748Seric void
rcpt_esmtp_args(a,kp,vp,e)101067963Seric rcpt_esmtp_args(a, kp, vp, e)
101167963Seric ADDRESS *a;
101267963Seric char *kp;
101367963Seric char *vp;
101467963Seric ENVELOPE *e;
101567963Seric {
101667963Seric if (strcasecmp(kp, "notify") == 0)
101767963Seric {
101867963Seric char *p;
101967963Seric
102067963Seric if (vp == NULL)
102167963Seric {
102267963Seric usrerr("501 NOTIFY requires a value");
102367963Seric /* NOTREACHED */
102467963Seric }
102567963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
102668595Seric a->q_flags |= QHASNOTIFY;
102767963Seric if (strcasecmp(vp, "never") == 0)
102867963Seric return;
102967963Seric for (p = vp; p != NULL; vp = p)
103067963Seric {
103167963Seric p = strchr(p, ',');
103267963Seric if (p != NULL)
103367963Seric *p++ = '\0';
103467963Seric if (strcasecmp(vp, "success") == 0)
103567963Seric a->q_flags |= QPINGONSUCCESS;
103667963Seric else if (strcasecmp(vp, "failure") == 0)
103767963Seric a->q_flags |= QPINGONFAILURE;
103867963Seric else if (strcasecmp(vp, "delay") == 0)
103967963Seric a->q_flags |= QPINGONDELAY;
104067963Seric else
104167963Seric {
104267963Seric usrerr("501 Bad argument \"%s\" to NOTIFY",
104367963Seric vp);
104467963Seric /* NOTREACHED */
104567963Seric }
104667963Seric }
104767963Seric }
104867963Seric else if (strcasecmp(kp, "orcpt") == 0)
104967963Seric {
105067963Seric if (vp == NULL)
105167963Seric {
105267963Seric usrerr("501 ORCPT requires a value");
105367963Seric /* NOTREACHED */
105467963Seric }
105569888Seric if (strchr(vp, ';') == NULL || !xtextok(vp))
105668583Seric {
105768583Seric usrerr("501 Syntax error in ORCPT parameter value");
105868583Seric /* NOTREACHED */
105968583Seric }
106068583Seric if (a->q_orcpt != NULL)
106168583Seric {
106268583Seric usrerr("501 Duplicate ORCPT parameter");
106368583Seric /* NOTREACHED */
106468583Seric }
106567963Seric a->q_orcpt = newstr(vp);
106667963Seric }
106767963Seric else
106867963Seric {
106967963Seric usrerr("501 %s parameter unrecognized", kp);
107067963Seric /* NOTREACHED */
107167963Seric }
107267963Seric }
107367963Seric /*
107458151Seric ** PRINTVRFYADDR -- print an entry in the verify queue
107558151Seric **
107658151Seric ** Parameters:
107758151Seric ** a -- the address to print
107858151Seric ** last -- set if this is the last one.
107958151Seric **
108058151Seric ** Returns:
108158151Seric ** none.
108258151Seric **
108358151Seric ** Side Effects:
108458151Seric ** Prints the appropriate 250 codes.
108558151Seric */
108658151Seric
108769748Seric void
printvrfyaddr(a,last)108858151Seric printvrfyaddr(a, last)
108958151Seric register ADDRESS *a;
109058151Seric bool last;
109158151Seric {
109258151Seric char fmtbuf[20];
109358151Seric
109458151Seric strcpy(fmtbuf, "250");
109558151Seric fmtbuf[3] = last ? ' ' : '-';
109658151Seric
109759746Seric if (a->q_fullname == NULL)
109859746Seric {
109959746Seric if (strchr(a->q_user, '@') == NULL)
110059746Seric strcpy(&fmtbuf[4], "<%s@%s>");
110159746Seric else
110259746Seric strcpy(&fmtbuf[4], "<%s>");
110359746Seric message(fmtbuf, a->q_user, MyHostName);
110459746Seric }
110558151Seric else
110658151Seric {
110759746Seric if (strchr(a->q_user, '@') == NULL)
110859746Seric strcpy(&fmtbuf[4], "%s <%s@%s>");
110959746Seric else
111059746Seric strcpy(&fmtbuf[4], "%s <%s>");
111159746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
111258151Seric }
111358151Seric }
111458151Seric /*
11154577Seric ** HELP -- implement the HELP command.
11164577Seric **
11174577Seric ** Parameters:
11184577Seric ** topic -- the topic we want help for.
11194577Seric **
11204577Seric ** Returns:
11214577Seric ** none.
11224577Seric **
11234577Seric ** Side Effects:
11244577Seric ** outputs the help file to message output.
11254577Seric */
11264577Seric
112769748Seric void
help(topic)11284577Seric help(topic)
11294577Seric char *topic;
11304577Seric {
11314577Seric register FILE *hf;
11324577Seric int len;
113368751Seric bool noinfo;
11344577Seric char buf[MAXLINE];
113568751Seric extern char Version[];
11364577Seric
113768751Seric
11388269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
11394577Seric {
11404577Seric /* no help */
114111931Seric errno = 0;
114268751Seric message("502 Sendmail %s -- HELP not implemented", Version);
11434577Seric return;
11444577Seric }
11454577Seric
114649669Seric if (topic == NULL || *topic == '\0')
114768751Seric {
114849669Seric topic = "smtp";
114968751Seric message("214-This is Sendmail version %s", Version);
115068751Seric noinfo = FALSE;
115168751Seric }
115249669Seric else
115368751Seric {
115449669Seric makelower(topic);
115568751Seric noinfo = TRUE;
115668751Seric }
115749669Seric
11584577Seric len = strlen(topic);
11594577Seric
11604577Seric while (fgets(buf, sizeof buf, hf) != NULL)
11614577Seric {
11624577Seric if (strncmp(buf, topic, len) == 0)
11634577Seric {
11644577Seric register char *p;
11654577Seric
116656795Seric p = strchr(buf, '\t');
11674577Seric if (p == NULL)
11684577Seric p = buf;
11694577Seric else
11704577Seric p++;
11714577Seric fixcrlf(p, TRUE);
117258151Seric message("214-%s", p);
11734577Seric noinfo = FALSE;
11744577Seric }
11754577Seric }
11764577Seric
11774577Seric if (noinfo)
117858151Seric message("504 HELP topic unknown");
11794577Seric else
118058151Seric message("214 End of HELP info");
11814628Seric (void) fclose(hf);
11824577Seric }
11838544Seric /*
11849339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again
11859339Seric **
11869339Seric ** Parameters:
11879339Seric ** label -- a string used in error messages
11889339Seric **
11899339Seric ** Returns:
11909339Seric ** zero in the child
11919339Seric ** one in the parent
11929339Seric **
11939339Seric ** Side Effects:
11949339Seric ** none.
11959339Seric */
11968544Seric
119769748Seric int
runinchild(label,e)119855012Seric runinchild(label, e)
11999339Seric char *label;
120055012Seric register ENVELOPE *e;
12019339Seric {
12029339Seric int childpid;
12039339Seric
120416158Seric if (!OneXact)
12059339Seric {
120616158Seric childpid = dofork();
120716158Seric if (childpid < 0)
120816158Seric {
120969716Seric syserr("451 %s: cannot fork", label);
121016158Seric return (1);
121116158Seric }
121216158Seric if (childpid > 0)
121316158Seric {
121416158Seric auto int st;
12159339Seric
121616158Seric /* parent -- wait for child to complete */
121769788Seric setproctitle("server %s child wait", CurSmtpClient);
121816158Seric st = waitfor(childpid);
121916158Seric if (st == -1)
122069716Seric syserr("451 %s: lost child", label);
122164948Seric else if (!WIFEXITED(st))
122269716Seric syserr("451 %s: died on signal %d",
122364948Seric label, st & 0177);
12249339Seric
122516158Seric /* if we exited on a QUIT command, complete the process */
122666017Seric if (WEXITSTATUS(st) == EX_QUIT)
122766017Seric {
122866017Seric disconnect(1, e);
122916158Seric finis();
123066017Seric }
12319339Seric
123216158Seric return (1);
123316158Seric }
123416158Seric else
123516158Seric {
123616158Seric /* child */
123716158Seric InChild = TRUE;
123825050Seric QuickAbort = FALSE;
123955012Seric clearenvelope(e, FALSE);
124016158Seric }
12419339Seric }
124215256Seric
124316158Seric /* open alias database */
124460537Seric initmaps(FALSE, e);
124516158Seric
124616158Seric return (0);
12479339Seric }
12489339Seric
124956795Seric # endif /* SMTP */
1250