don't pass through NOTIFY= unless it was given in the SMTP RCPT
[unix-history] / usr / src / usr.sbin / sendmail / src / srvrsmtp.c
CommitLineData
b2a81223 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
24634489
KB
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
bee79b64 5 *
417f7a11 6 * %sccs.include.redist.c%
bee79b64
KB
7 */
8
9# include "sendmail.h"
b2a81223 10
bee79b64
KB
11#ifndef lint
12#ifdef SMTP
e1f691b3 13static char sccsid[] = "@(#)srvrsmtp.c 8.61 (Berkeley) %G% (with SMTP)";
bee79b64 14#else
e1f691b3 15static char sccsid[] = "@(#)srvrsmtp.c 8.61 (Berkeley) %G% (without SMTP)";
bee79b64
KB
16#endif
17#endif /* not lint */
b2a81223 18
e6f08ab1 19# include <errno.h>
6b861048 20
bee79b64 21# ifdef SMTP
d727056e 22
6b861048
EA
23/*
24** SMTP -- run the SMTP protocol.
25**
26** Parameters:
27** none.
28**
29** Returns:
30** never.
31**
32** Side Effects:
33** Reads commands from the input channel and processes
34** them.
35*/
36
37struct cmd
38{
39 char *cmdname; /* command name */
40 int cmdcode; /* internal code, see below */
41};
42
43/* values for cmdcode */
44# define CMDERROR 0 /* bad command */
45# define CMDMAIL 1 /* mail -- designate sender */
4a4ebe09 46# define CMDRCPT 2 /* rcpt -- designate recipient */
6b861048 47# define CMDDATA 3 /* data -- send message text */
7d7fdf93 48# define CMDHOPS 4 /* hops -- specify hop count */
e6f08ab1
EA
49# define CMDRSET 4 /* rset -- reset state */
50# define CMDVRFY 5 /* vrfy -- verify address */
8f48def8 51# define CMDEXPN 6 /* expn -- expand address */
e6f08ab1
EA
52# define CMDNOOP 7 /* noop -- do nothing */
53# define CMDQUIT 8 /* quit -- close connection and die */
54# define CMDHELO 9 /* helo -- be polite */
8f48def8 55# define CMDHELP 10 /* help -- give usage info */
34f2d20e 56# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
8f48def8
EA
57/* non-standard commands */
58# define CMDONEX 16 /* onex -- sending one transaction only */
59# define CMDVERB 17 /* verb -- go into verbose mode */
3e8f46ad
EA
60/* use this to catch and log "door handle" attempts on your system */
61# define CMDLOGBOGUS 23 /* bogus command that should be logged */
ab4889ea 62/* debugging-only commands, only enabled if SMTPDEBUG is defined */
8f48def8
EA
63# define CMDDBGQSHOW 24 /* showq -- show send queue */
64# define CMDDBGDEBUG 25 /* debug -- set debug mode */
6b861048
EA
65
66static struct cmd CmdTab[] =
67{
68 "mail", CMDMAIL,
4a4ebe09 69 "rcpt", CMDRCPT,
6b861048 70 "data", CMDDATA,
6b861048
EA
71 "rset", CMDRSET,
72 "vrfy", CMDVRFY,
8f48def8 73 "expn", CMDEXPN,
633a2e02 74 "expn", CMDVRFY,
6b861048
EA
75 "help", CMDHELP,
76 "noop", CMDNOOP,
77 "quit", CMDQUIT,
4a4ebe09 78 "helo", CMDHELO,
34f2d20e 79 "ehlo", CMDEHLO,
e8ad767d 80 "verb", CMDVERB,
8fe4fb9b 81 "onex", CMDONEX,
7d7fdf93 82 "hops", CMDHOPS,
ab4889ea
MK
83 /*
84 * remaining commands are here only
85 * to trap and log attempts to use them
86 */
e6f08ab1 87 "showq", CMDDBGQSHOW,
ad20d67b 88 "debug", CMDDBGDEBUG,
3e8f46ad 89 "wiz", CMDLOGBOGUS,
6b861048
EA
90 NULL, CMDERROR,
91};
92
7338e3d4 93bool OneXact = FALSE; /* one xaction only this run */
c9af7ef4 94char *CurSmtpClient; /* who's at the other end of channel */
1972fb40 95
e6cb9fc4 96static char *skipword();
90f34501
EA
97extern char RealUserName[];
98
e6cb9fc4 99
f6603f3b
EA
100#define MAXBADCOMMANDS 25 /* maximum number of bad commands */
101
a4076aed
EA
102smtp(e)
103 register ENVELOPE *e;
6b861048 104{
6b861048 105 register char *p;
e8ad767d 106 register struct cmd *c;
6b861048 107 char *cmd;
abae7b2d
EA
108 extern ADDRESS *sendto();
109 ADDRESS *a;
6b861048 110
abae7b2d 111 hasmail = FALSE;
20219e3f 112 if (fileno(OutChannel) != fileno(stdout))
1f1cc003
EA
113 {
114 /* arrange for debugging output to go to remote host */
20219e3f 115 (void) dup2(fileno(OutChannel), fileno(stdout));
1f1cc003 116 }
a4076aed 117 settime(e);
7c8c5b90
EA
118 peerhostname = RealHostName;
119 if (peerhostname == NULL)
120 peerhostname = "localhost";
121 CurHostName = peerhostname;
c9af7ef4
EA
122 CurSmtpClient = macvalue('_', e);
123 if (CurSmtpClient == NULL)
389c0d5e 124 CurSmtpClient = CurHostName;
c9af7ef4
EA
125
126 setproctitle("server %s startup", CurSmtpClient);
832e8a27 127 expand("\201e", inp, sizeof inp, e);
53ae7b6c
EA
128 if (BrokenSmtpPeers)
129 {
5f0e0cf2
EA
130 p = strchr(inp, '\n');
131 if (p != NULL)
132 *p = '\0';
53ae7b6c
EA
133 message("220 %s", inp);
134 }
135 else
136 {
fb617949
EA
137 char *q = inp;
138
139 while (q != NULL)
140 {
5f0e0cf2 141 p = strchr(q, '\n');
fb617949
EA
142 if (p != NULL)
143 *p++ = '\0';
5f0e0cf2
EA
144 message("220-%s", q);
145 q = p;
fb617949 146 }
53ae7b6c
EA
147 message("220 ESMTP spoken here");
148 }
6fd6d536 149 protocol = NULL;
37444fe1 150 sendinghost = macvalue('s', e);
1c7897ef 151 gothello = FALSE;
6fd6d536 152 gotmail = FALSE;
6b861048
EA
153 for (;;)
154 {
d344c0b7 155 /* arrange for backout */
c583011b 156 if (setjmp(TopFrame) > 0)
b8bf5eba 157 {
c583011b
EA
158 /* if() nesting is necessary for Cray UNICOS */
159 if (InChild)
160 {
161 QuickAbort = FALSE;
162 SuprErrs = TRUE;
163 finis();
164 }
b8bf5eba 165 }
d344c0b7
EA
166 QuickAbort = FALSE;
167 HoldErrs = FALSE;
f61c3c40 168 LogUsrErrs = FALSE;
4b72e6db 169 e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
d344c0b7 170
37eaaadb 171 /* setup for the read */
a4076aed 172 e->e_to = NULL;
34d37b7d 173 Errors = 0;
09eb49d8 174 (void) fflush(stdout);
37eaaadb 175
37eaaadb 176 /* read the input line */
8e948497
EA
177 SmtpPhase = "server cmd read";
178 setproctitle("server %s cmd read", CurHostName);
179 p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
180 SmtpPhase);
37eaaadb 181
2439b900 182 /* handle errors */
37eaaadb 183 if (p == NULL)
6b861048
EA
184 {
185 /* end of file, just die */
33cbaada 186 disconnect(1, e);
b6edea3d 187 message("421 %s Lost input channel from %s",
c9af7ef4 188 MyHostName, CurSmtpClient);
6d6f9196 189#ifdef LOG
4b72e6db 190 if (LogLevel > (gotmail ? 1 : 19))
6d6f9196 191 syslog(LOG_NOTICE, "lost input channel from %s",
c9af7ef4 192 CurSmtpClient);
6d6f9196 193#endif
074b0256
EA
194 if (InChild)
195 ExitStat = EX_QUIT;
6b861048
EA
196 finis();
197 }
198
199 /* clean up end of line */
2768afe3 200 fixcrlf(inp, TRUE);
6b861048 201
49086753 202 /* echo command to transcript */
a4076aed
EA
203 if (e->e_xfp != NULL)
204 fprintf(e->e_xfp, "<<< %s\n", inp);
49086753 205
ade7da2a 206 if (e->e_id == NULL)
e42c7981 207 setproctitle("%s: %.80s", CurSmtpClient, inp);
ade7da2a 208 else
e42c7981 209 setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
ade7da2a 210
6b861048 211 /* break off command */
2bee003d 212 for (p = inp; isascii(*p) && isspace(*p); p++)
6b861048 213 continue;
f43b04ce 214 cmd = cmdbuf;
2bee003d
EA
215 while (*p != '\0' &&
216 !(isascii(*p) && isspace(*p)) &&
217 cmd < &cmdbuf[sizeof cmdbuf - 2])
a0225d08
EA
218 *cmd++ = *p++;
219 *cmd = '\0';
6b861048 220
a1a07282 221 /* throw away leading whitespace */
2bee003d 222 while (isascii(*p) && isspace(*p))
a1a07282
EA
223 p++;
224
6b861048
EA
225 /* decode command */
226 for (c = CmdTab; c->cmdname != NULL; c++)
227 {
ed73ef1d 228 if (!strcasecmp(c->cmdname, cmdbuf))
6b861048
EA
229 break;
230 }
231
5ae51cd5
EA
232 /* reset errors */
233 errno = 0;
234
6b861048
EA
235 /* process command */
236 switch (c->cmdcode)
237 {
4a4ebe09 238 case CMDHELO: /* hello -- introduce yourself */
34f2d20e
EA
239 case CMDEHLO: /* extended hello */
240 if (c->cmdcode == CMDEHLO)
241 {
242 protocol = "ESMTP";
8e948497 243 SmtpPhase = "server EHLO";
34f2d20e
EA
244 }
245 else
246 {
247 protocol = "SMTP";
8e948497 248 SmtpPhase = "server HELO";
34f2d20e 249 }
825424db
EA
250
251 /* check for valid domain name (re 1123 5.2.5) */
252 if (*p == '\0')
253 {
254 message("501 %s requires domain address",
255 cmdbuf);
256 break;
257 }
258 else
259 {
260 register char *q;
261
262 for (q = p; *q != '\0'; q++)
263 {
264 if (!isascii(*q))
265 break;
266 if (isalnum(*q))
267 continue;
268 if (strchr("[].-_#", *q) == NULL)
269 break;
270 }
271 if (*q != '\0')
272 {
273 message("501 Invalid domain name");
274 break;
275 }
276 }
277
37444fe1 278 sendinghost = newstr(p);
abae7b2d 279 message("250", "%s Hello %s, pleased to meet you", HostName, p);
4a4ebe09
EA
280 break;
281
6b861048 282 case CMDMAIL: /* mail -- designate sender */
8e948497 283 SmtpPhase = "server MAIL";
2e3062fe 284
8fe4fb9b 285 /* check for validity of this command */
94bc039a 286 if (!gothello)
1c7897ef 287 {
a9bac7a9 288 /* set sending host to our known value */
37444fe1 289 if (sendinghost == NULL)
7c8c5b90 290 sendinghost = peerhostname;
a9bac7a9 291
94bc039a 292 if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
f967c46a 293 {
94bc039a 294 message("503 Polite people say HELO first");
f967c46a
EA
295 break;
296 }
1c7897ef 297 }
3b87200d 298 if (gotmail)
2768afe3 299 {
b6edea3d 300 message("503 Sender already specified");
4b72e6db
EA
301 if (InChild)
302 finis();
2768afe3
EA
303 break;
304 }
e6f08ab1
EA
305 if (InChild)
306 {
ab4889ea 307 errno = 0;
b6edea3d 308 syserr("503 Nested MAIL command: MAIL %s", p);
074b0256 309 finis();
e6f08ab1
EA
310 }
311
312 /* fork a subprocess to process this command */
a4076aed 313 if (runinchild("SMTP-MAIL", e) > 0)
e6f08ab1 314 break;
8e5c6745
EA
315 if (!gothello)
316 {
317 auth_warning(e,
7c8c5b90
EA
318 "Host %s didn't use HELO protocol",
319 peerhostname);
8e5c6745 320 }
3bb5d0e6 321#ifdef PICKY_HELO_CHECK
7c8c5b90
EA
322 if (strcasecmp(sendinghost, peerhostname) != 0 &&
323 (strcasecmp(peerhostname, "localhost") != 0 ||
2d5db20a
EA
324 strcasecmp(sendinghost, MyHostName) != 0))
325 {
326 auth_warning(e, "Host %s claimed to be %s",
7c8c5b90 327 peerhostname, sendinghost);
2d5db20a 328 }
3bb5d0e6 329#endif
2d5db20a 330
34f2d20e
EA
331 if (protocol == NULL)
332 protocol = "SMTP";
333 define('r', protocol, e);
37444fe1 334 define('s', sendinghost, e);
a4076aed 335 initsys(e);
fba945cd 336 nrcpts = 0;
3dd1581c 337 e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
e42c7981 338 setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
e6f08ab1
EA
339
340 /* child -- go do the processing */
6b861048
EA
341 p = skipword(p, "from");
342 if (p == NULL)
343 break;
aa102c71 344 if (setjmp(TopFrame) > 0)
182c060a
EA
345 {
346 /* this failed -- undo work */
347 if (InChild)
b8bf5eba
EA
348 {
349 QuickAbort = FALSE;
350 SuprErrs = TRUE;
fd57f063 351 e->e_flags &= ~EF_FATALERRS;
182c060a 352 finis();
b8bf5eba 353 }
aa102c71 354 break;
182c060a 355 }
aa102c71 356 QuickAbort = TRUE;
9e2cf26f
EA
357
358 /* must parse sender first */
359 delimptr = NULL;
4a2da288 360 setsender(p, e, &delimptr, FALSE);
9e2cf26f
EA
361 p = delimptr;
362 if (p != NULL && *p != '\0')
363 *p++ = '\0';
364
90f34501
EA
365 /* check for possible spoofing */
366 if (RealUid != 0 && OpMode == MD_SMTP &&
2bade550
EA
367 !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
368 strcmp(e->e_from.q_user, RealUserName) != 0)
90f34501
EA
369 {
370 auth_warning(e, "%s owned process doing -bs",
371 RealUserName);
372 }
373
9e2cf26f 374 /* now parse ESMTP arguments */
81e7e79e 375 e->e_msgsize = 0;
24e2a159 376 while (p != NULL && *p != '\0')
9e2cf26f
EA
377 {
378 char *kp;
89346287 379 char *vp = NULL;
9e2cf26f
EA
380
381 /* locate the beginning of the keyword */
382 while (isascii(*p) && isspace(*p))
383 p++;
384 if (*p == '\0')
385 break;
386 kp = p;
387
388 /* skip to the value portion */
389 while (isascii(*p) && isalnum(*p) || *p == '-')
390 p++;
391 if (*p == '=')
392 {
393 *p++ = '\0';
394 vp = p;
395
396 /* skip to the end of the value */
397 while (*p != '\0' && *p != ' ' &&
398 !(isascii(*p) && iscntrl(*p)) &&
399 *p != '=')
400 p++;
401 }
402
403 if (*p != '\0')
404 *p++ = '\0';
405
406 if (tTd(19, 1))
24e2a159 407 printf("MAIL: got arg %s=\"%s\"\n", kp,
9e2cf26f
EA
408 vp == NULL ? "<null>" : vp);
409
c7a0eaaf 410 mail_esmtp_args(kp, vp, e);
9e2cf26f 411 }
346fe280 412
81e7e79e 413 if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
346fe280
EA
414 {
415 usrerr("552 Message size exceeds fixed maximum message size (%ld)",
416 MaxMessageSize);
417 /* NOTREACHED */
418 }
9e2cf26f 419
81e7e79e 420 if (!enoughspace(e->e_msgsize))
9e2cf26f
EA
421 {
422 message("452 Insufficient disk space; try again later");
423 break;
424 }
b6edea3d 425 message("250 Sender ok");
182c060a 426 gotmail = TRUE;
506a2500
EA
427
428 /* optimize: non-interactive, don't expand aliases */
28a8a6ef 429 if (e->e_sendmode != SM_DELIVER)
506a2500
EA
430 e->e_flags |= EF_VRFYONLY;
431
6b861048
EA
432 break;
433
4a4ebe09 434 case CMDRCPT: /* rcpt -- designate recipient */
d7f41a7b
EA
435 if (!gotmail)
436 {
437 usrerr("503 Need MAIL before RCPT");
438 break;
439 }
8e948497 440 SmtpPhase = "server RCPT";
d344c0b7 441 if (setjmp(TopFrame) > 0)
9bfb75c1 442 {
a4076aed 443 e->e_flags &= ~EF_FATALERRS;
d344c0b7 444 break;
9bfb75c1 445 }
d344c0b7 446 QuickAbort = TRUE;
f61c3c40 447 LogUsrErrs = TRUE;
6b861048
EA
448 p = skipword(p, "to");
449 if (p == NULL)
450 break;
abae7b2d
EA
451 a = sendto(p, 1, (ADDRESS *) NULL, 0);
452# ifdef DEBUG
453 if (Debug > 1)
454 printaddr(a, TRUE);
455# endif DEBUG
d344c0b7
EA
456 if (Errors != 0)
457 break;
458
459 /* no errors during parsing, but might be a duplicate */
a4076aed 460 e->e_to = p;
d344c0b7 461 if (!bitset(QBADADDR, a->q_flags))
fba945cd 462 {
fe3849ea
EA
463 message("250 Recipient ok%s",
464 bitset(QQUEUEUP, a->q_flags) ?
465 " (will queue)" : "");
fba945cd
EA
466 nrcpts++;
467 }
d344c0b7 468 else
6b861048 469 {
d344c0b7 470 /* punt -- should keep message in ADDRESS.... */
b6edea3d 471 message("550 Addressee unknown");
6b861048 472 }
a4076aed 473 e->e_to = NULL;
6b861048
EA
474 break;
475
476 case CMDDATA: /* data -- text of mail */
8e948497 477 SmtpPhase = "server DATA";
3b87200d 478 if (!gotmail)
4a4ebe09 479 {
b6edea3d 480 message("503 Need MAIL command");
4a4ebe09
EA
481 break;
482 }
fe3849ea 483 else if (nrcpts <= 0)
6b861048 484 {
b6edea3d 485 message("503 Need RCPT (recipient)");
4a4ebe09 486 break;
6b861048 487 }
4a4ebe09 488
959cf51d 489 /* check to see if we need to re-expand aliases */
fd57f063
EA
490 /* also reset QBADADDR on already-diagnosted addrs */
491 doublequeue = FALSE;
959cf51d
EA
492 for (a = e->e_sendqueue; a != NULL; a = a->q_next)
493 {
494 if (bitset(QVERIFIED, a->q_flags))
fd57f063
EA
495 {
496 /* need to re-expand aliases */
497 doublequeue = TRUE;
498 }
499 if (bitset(QBADADDR, a->q_flags))
500 {
501 /* make this "go away" */
502 a->q_flags |= QDONTSEND;
503 a->q_flags &= ~QBADADDR;
504 }
959cf51d
EA
505 }
506
4a4ebe09 507 /* collect the text of the message */
2e3062fe 508 SmtpPhase = "collect";
c23930c0 509 collect(InChannel, TRUE, doublequeue, NULL, e);
4f8e0a23
EA
510 if (Errors != 0)
511 goto abortmessage;
439fd0d9 512
3dd1581c
EA
513 /* make sure we actually do delivery */
514 e->e_flags &= ~EF_CLRQUEUE;
515
439fd0d9 516 /* from now on, we have to operate silently */
4f8e0a23 517 HoldErrs = TRUE;
439fd0d9 518 e->e_errormode = EM_MAIL;
4a4ebe09 519
8eedb496
EA
520 /*
521 ** Arrange to send to everyone.
522 ** If sending to multiple people, mail back
523 ** errors rather than reporting directly.
524 ** In any case, don't mail back errors for
525 ** anything that has happened up to
526 ** now (the other end will do this).
bcf74f25
EA
527 ** Truncate our transcript -- the mail has gotten
528 ** to us successfully, and if we have
529 ** to mail this back, it will be easier
530 ** on the reader.
8eedb496
EA
531 ** Then send to everyone.
532 ** Finally give a reply code. If an error has
533 ** already been given, don't mail a
534 ** message back.
e6f08ab1 535 ** We goose error returns by clearing error bit.
8eedb496
EA
536 */
537
2e3062fe 538 SmtpPhase = "delivery";
a4076aed 539 e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
4aed8f3a 540 id = e->e_id;
4a4ebe09 541
439fd0d9 542 if (doublequeue)
2f6a8a78 543 {
439fd0d9
EA
544 /* make sure it is in the queue */
545 queueup(e, TRUE, FALSE);
96a1575a
EA
546 if (e->e_sendmode == SM_QUEUE)
547 e->e_flags |= EF_KEEPQUEUE;
2f6a8a78
EA
548 }
549 else
a891bb7e 550 {
439fd0d9
EA
551 /* send to all recipients */
552 sendall(e, SM_DEFAULT);
553 }
554 e->e_to = NULL;
fba945cd 555
439fd0d9
EA
556 /* issue success message */
557 message("250 %s Message accepted for delivery", id);
d0268270 558
439fd0d9
EA
559 /* if we just queued, poke it */
560 if (doublequeue && e->e_sendmode != SM_QUEUE)
561 {
562 extern pid_t dowork();
563
564 unlockqueue(e);
565 (void) dowork(id, TRUE, TRUE, e);
a891bb7e
EA
566 }
567
6366413a
EA
568 /* now make it really happen */
569 if (!Verbose && e->e_sendmode != SM_QUEUE)
570 dowork(id, TRUE, e);
571
fba945cd 572 abortmessage:
e6f08ab1
EA
573 /* if in a child, pop back to our parent */
574 if (InChild)
575 finis();
2e3062fe
EA
576
577 /* clean up a bit */
3b87200d 578 gotmail = FALSE;
a4076aed 579 dropenvelope(e);
fda58daa 580 CurEnv = e = newenvelope(e, CurEnv);
a4076aed 581 e->e_flags = BlankEnvelope.e_flags;
6b861048
EA
582 break;
583
584 case CMDRSET: /* rset -- reset state */
b6edea3d 585 message("250 Reset state");
80662ec6 586 e->e_flags |= EF_CLRQUEUE;
e6f08ab1
EA
587 if (InChild)
588 finis();
3b87200d
EA
589
590 /* clean up a bit */
591 gotmail = FALSE;
592 dropenvelope(e);
fda58daa 593 CurEnv = e = newenvelope(e, CurEnv);
e6f08ab1 594 break;
6b861048
EA
595
596 case CMDVRFY: /* vrfy -- verify address */
8f48def8
EA
597 case CMDEXPN: /* expn -- expand address */
598 vrfy = c->cmdcode == CMDVRFY;
599 if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
600 PrivacyFlags))
1c7897ef 601 {
efae59f3 602 if (vrfy)
cb282b01 603 message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
efae59f3 604 else
c5b74eae 605 message("502 Sorry, we do not allow this operation");
c9af7ef4
EA
606#ifdef LOG
607 if (LogLevel > 5)
608 syslog(LOG_INFO, "%s: %s [rejected]",
609 CurSmtpClient, inp);
610#endif
1c7897ef
EA
611 break;
612 }
613 else if (!gothello &&
8f48def8
EA
614 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
615 PrivacyFlags))
1c7897ef 616 {
b6edea3d 617 message("503 I demand that you introduce yourself first");
1c7897ef
EA
618 break;
619 }
8f48def8 620 if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
e6f08ab1 621 break;
01e8020e 622#ifdef LOG
68f7099c 623 if (LogLevel > 5)
c9af7ef4 624 syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
01e8020e 625#endif
abae7b2d 626 paddrtree(a);
6b861048
EA
627 break;
628
629 case CMDHELP: /* help -- give user info */
34d37b7d 630 help(p);
6b861048
EA
631 break;
632
633 case CMDNOOP: /* noop -- do nothing */
422d9f83 634 message("250 OK");
6b861048
EA
635 break;
636
637 case CMDQUIT: /* quit -- leave mail */
b6edea3d 638 message("221 %s closing connection", MyHostName);
251126e0 639
f6603f3b 640doquit:
251126e0 641 /* avoid future 050 messages */
33cbaada 642 disconnect(1, e);
251126e0 643
e6f08ab1
EA
644 if (InChild)
645 ExitStat = EX_QUIT;
6b861048
EA
646 finis();
647
e8ad767d 648 case CMDVERB: /* set verbose mode */
de975e1b
EA
649 if (bitset(PRIV_NOEXPN, PrivacyFlags))
650 {
651 /* this would give out the same info */
652 message("502 Verbose unavailable");
653 break;
654 }
e8ad767d 655 Verbose = TRUE;
8c8e8e94 656 e->e_sendmode = SM_DELIVER;
de975e1b 657 message("250 Verbose mode");
e8ad767d
EA
658 break;
659
8fe4fb9b 660 case CMDONEX: /* doing one transaction only */
7338e3d4 661 OneXact = TRUE;
de975e1b 662 message("250 Only one transaction");
8fe4fb9b
EA
663 break;
664
ab4889ea 665# ifdef SMTPDEBUG
e6f08ab1 666 case CMDDBGQSHOW: /* show queues */
2654b031 667 printf("Send Queue=");
a4076aed 668 printaddr(e->e_sendqueue, TRUE);
d4f42161 669 break;
09eb49d8
EA
670
671 case CMDDBGDEBUG: /* set debug mode */
9678c96d
EA
672 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
673 tTflag(p);
b6edea3d 674 message("200 Debug set");
09eb49d8
EA
675 break;
676
ab4889ea 677# else /* not SMTPDEBUG */
ab4889ea
MK
678 case CMDDBGQSHOW: /* show queues */
679 case CMDDBGDEBUG: /* set debug mode */
3e8f46ad
EA
680# endif /* SMTPDEBUG */
681 case CMDLOGBOGUS: /* bogus command */
2e15a2d8 682# ifdef LOG
5973222c 683 if (LogLevel > 0)
3e8f46ad 684 syslog(LOG_CRIT,
68f7099c 685 "\"%s\" command from %s (%s)",
7c8c5b90 686 c->cmdname, peerhostname,
3341995c 687 anynet_ntoa(&RealHostAddr));
2e15a2d8 688# endif
ab4889ea 689 /* FALL THROUGH */
d4f42161 690
6b861048 691 case CMDERROR: /* unknown command */
f6603f3b
EA
692 if (++badcommands > MAXBADCOMMANDS)
693 {
694 message("421 %s Too many bad commands; closing connection",
695 MyHostName);
696 goto doquit;
697 }
698
b6edea3d 699 message("500 Command unrecognized");
6b861048
EA
700 break;
701
702 default:
ab4889ea 703 errno = 0;
b6edea3d 704 syserr("500 smtp: unknown code %d", c->cmdcode);
6b861048
EA
705 break;
706 }
707 }
708}
709\f/*
710** SKIPWORD -- skip a fixed word.
711**
712** Parameters:
713** p -- place to start looking.
714** w -- word to skip.
715**
716** Returns:
717** p following w.
718** NULL on error.
719**
720** Side Effects:
721** clobbers the p data area.
722*/
723
724static char *
725skipword(p, w)
726 register char *p;
727 char *w;
728{
729 register char *q;
7c8c5b90 730 char *firstp = p;
6b861048
EA
731
732 /* find beginning of word */
2bee003d 733 while (isascii(*p) && isspace(*p))
6b861048
EA
734 p++;
735 q = p;
736
737 /* find end of word */
2bee003d 738 while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6b861048 739 p++;
2bee003d 740 while (isascii(*p) && isspace(*p))
6b861048
EA
741 *p++ = '\0';
742 if (*p != ':')
743 {
744 syntax:
7c8c5b90
EA
745 message("501 Syntax error in parameters scanning \"%s\"",
746 firstp);
6b861048
EA
747 Errors++;
748 return (NULL);
749 }
750 *p++ = '\0';
2bee003d 751 while (isascii(*p) && isspace(*p))
6b861048
EA
752 p++;
753
20f20f1e
EA
754 if (*p == '\0')
755 goto syntax;
756
6b861048 757 /* see if the input word matches desired word */
ed73ef1d 758 if (strcasecmp(q, w))
6b861048
EA
759 goto syntax;
760
761 return (p);
762}
34d37b7d 763\f/*
c7a0eaaf
EA
764** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
765**
766** Parameters:
767** kp -- the parameter key.
768** vp -- the value of that parameter.
769** e -- the envelope.
770**
771** Returns:
772** none.
773*/
774
775mail_esmtp_args(kp, vp, e)
776 char *kp;
777 char *vp;
778 ENVELOPE *e;
779{
780 if (strcasecmp(kp, "size") == 0)
781 {
782 if (vp == NULL)
783 {
784 usrerr("501 SIZE requires a value");
785 /* NOTREACHED */
786 }
787# ifdef __STDC__
81e7e79e 788 e->e_msgsize = strtoul(vp, (char **) NULL, 10);
c7a0eaaf 789# else
81e7e79e 790 e->e_msgsize = strtol(vp, (char **) NULL, 10);
c7a0eaaf
EA
791# endif
792 }
793 else if (strcasecmp(kp, "body") == 0)
794 {
795 if (vp == NULL)
796 {
797 usrerr("501 BODY requires a value");
798 /* NOTREACHED */
799 }
800 if (strcasecmp(vp, "8bitmime") == 0)
801 {
802 SevenBitInput = FALSE;
803 }
804 else if (strcasecmp(vp, "7bit") == 0)
805 {
806 SevenBitInput = TRUE;
807 }
808 else
809 {
810 usrerr("501 Unknown BODY type %s",
811 vp);
812 /* NOTREACHED */
813 }
814 e->e_bodytype = newstr(vp);
815 }
816 else if (strcasecmp(kp, "envid") == 0)
817 {
818 if (vp == NULL)
819 {
820 usrerr("501 ENVID requires a value");
821 /* NOTREACHED */
822 }
8b6906e3
EA
823 if (!xtextok(vp))
824 {
825 usrerr("501 Syntax error in ENVID parameter value");
826 /* NOTREACHED */
827 }
828 if (e->e_envid != NULL)
829 {
830 usrerr("501 Duplicate ENVID parameter");
831 /* NOTREACHED */
832 }
c7a0eaaf
EA
833 e->e_envid = newstr(vp);
834 }
835 else if (strcasecmp(kp, "ret") == 0)
836 {
837 if (vp == NULL)
838 {
839 usrerr("501 RET requires a value");
840 /* NOTREACHED */
841 }
8b6906e3
EA
842 if (bitset(EF_RET_PARAM, e->e_flags))
843 {
844 usrerr("501 Duplicate RET parameter");
845 /* NOTREACHED */
846 }
c7a0eaaf
EA
847 e->e_flags |= EF_RET_PARAM;
848 if (strcasecmp(vp, "hdrs") == 0)
849 e->e_flags |= EF_NO_BODY_RETN;
850 else if (strcasecmp(vp, "full") != 0)
851 {
852 usrerr("501 Bad argument \"%s\" to RET", vp);
853 /* NOTREACHED */
854 }
855 }
856 else
857 {
858 usrerr("501 %s parameter unrecognized", kp);
859 /* NOTREACHED */
860 }
861}
862\f/*
82e3dc75
EA
863** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
864**
865** Parameters:
866** a -- the address corresponding to the To: parameter.
867** kp -- the parameter key.
868** vp -- the value of that parameter.
869** e -- the envelope.
870**
871** Returns:
872** none.
873*/
874
875rcpt_esmtp_args(a, kp, vp, e)
876 ADDRESS *a;
877 char *kp;
878 char *vp;
879 ENVELOPE *e;
880{
881 if (strcasecmp(kp, "notify") == 0)
882 {
883 char *p;
884
885 if (vp == NULL)
886 {
887 usrerr("501 NOTIFY requires a value");
888 /* NOTREACHED */
889 }
890 a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
e1f691b3 891 a->q_flags |= QHASNOTIFY;
82e3dc75
EA
892 if (strcasecmp(vp, "never") == 0)
893 return;
894 for (p = vp; p != NULL; vp = p)
895 {
896 p = strchr(p, ',');
897 if (p != NULL)
898 *p++ = '\0';
899 if (strcasecmp(vp, "success") == 0)
900 a->q_flags |= QPINGONSUCCESS;
901 else if (strcasecmp(vp, "failure") == 0)
902 a->q_flags |= QPINGONFAILURE;
903 else if (strcasecmp(vp, "delay") == 0)
904 a->q_flags |= QPINGONDELAY;
905 else
906 {
907 usrerr("501 Bad argument \"%s\" to NOTIFY",
908 vp);
909 /* NOTREACHED */
910 }
911 }
912 }
82e3dc75
EA
913 else if (strcasecmp(kp, "orcpt") == 0)
914 {
915 if (vp == NULL)
916 {
917 usrerr("501 ORCPT requires a value");
918 /* NOTREACHED */
919 }
8b6906e3
EA
920 if (!xtextok(vp))
921 {
922 usrerr("501 Syntax error in ORCPT parameter value");
923 /* NOTREACHED */
924 }
925 if (a->q_orcpt != NULL)
926 {
927 usrerr("501 Duplicate ORCPT parameter");
928 /* NOTREACHED */
929 }
82e3dc75
EA
930 a->q_orcpt = newstr(vp);
931 }
932 else
933 {
934 usrerr("501 %s parameter unrecognized", kp);
935 /* NOTREACHED */
936 }
937}
938\f/*
b6edea3d
EA
939** PRINTVRFYADDR -- print an entry in the verify queue
940**
941** Parameters:
942** a -- the address to print
943** last -- set if this is the last one.
944**
945** Returns:
946** none.
947**
948** Side Effects:
949** Prints the appropriate 250 codes.
950*/
951
952printvrfyaddr(a, last)
953 register ADDRESS *a;
954 bool last;
955{
956 char fmtbuf[20];
957
958 strcpy(fmtbuf, "250");
959 fmtbuf[3] = last ? ' ' : '-';
960
ac820be5
EA
961 if (a->q_fullname == NULL)
962 {
963 if (strchr(a->q_user, '@') == NULL)
964 strcpy(&fmtbuf[4], "<%s@%s>");
965 else
966 strcpy(&fmtbuf[4], "<%s>");
967 message(fmtbuf, a->q_user, MyHostName);
968 }
b6edea3d
EA
969 else
970 {
ac820be5
EA
971 if (strchr(a->q_user, '@') == NULL)
972 strcpy(&fmtbuf[4], "%s <%s@%s>");
973 else
974 strcpy(&fmtbuf[4], "%s <%s>");
975 message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
b6edea3d 976 }
b6edea3d
EA
977}
978\f/*
34d37b7d
EA
979** HELP -- implement the HELP command.
980**
981** Parameters:
982** topic -- the topic we want help for.
983**
984** Returns:
985** none.
986**
987** Side Effects:
988** outputs the help file to message output.
989*/
990
991help(topic)
992 char *topic;
993{
994 register FILE *hf;
995 int len;
996 char buf[MAXLINE];
997 bool noinfo;
998
c1e24818 999 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
34d37b7d
EA
1000 {
1001 /* no help */
1b932f66 1002 errno = 0;
b6edea3d 1003 message("502 HELP not implemented");
34d37b7d
EA
1004 return;
1005 }
1006
e97afd4a
EA
1007 if (topic == NULL || *topic == '\0')
1008 topic = "smtp";
1009 else
1010 makelower(topic);
1011
34d37b7d 1012 len = strlen(topic);
34d37b7d
EA
1013 noinfo = TRUE;
1014
1015 while (fgets(buf, sizeof buf, hf) != NULL)
1016 {
1017 if (strncmp(buf, topic, len) == 0)
1018 {
1019 register char *p;
1020
f3d8f6d6 1021 p = strchr(buf, '\t');
34d37b7d
EA
1022 if (p == NULL)
1023 p = buf;
1024 else
1025 p++;
1026 fixcrlf(p, TRUE);
b6edea3d 1027 message("214-%s", p);
34d37b7d
EA
1028 noinfo = FALSE;
1029 }
1030 }
1031
1032 if (noinfo)
b6edea3d 1033 message("504 HELP topic unknown");
34d37b7d 1034 else
b6edea3d 1035 message("214 End of HELP info");
ed45aae1 1036 (void) fclose(hf);
34d37b7d 1037}
abae7b2d 1038\f/*
e6f08ab1
EA
1039** RUNINCHILD -- return twice -- once in the child, then in the parent again
1040**
1041** Parameters:
1042** label -- a string used in error messages
1043**
1044** Returns:
1045** zero in the child
1046** one in the parent
1047**
1048** Side Effects:
1049** none.
1050*/
1051
a4076aed 1052runinchild(label, e)
e6f08ab1 1053 char *label;
a4076aed 1054 register ENVELOPE *e;
e6f08ab1
EA
1055{
1056 int childpid;
1057
c1a66acf 1058 if (!OneXact)
e6f08ab1 1059 {
c1a66acf
EA
1060 childpid = dofork();
1061 if (childpid < 0)
1062 {
1063 syserr("%s: cannot fork", label);
1064 return (1);
1065 }
1066 if (childpid > 0)
1067 {
1068 auto int st;
e6f08ab1 1069
c1a66acf 1070 /* parent -- wait for child to complete */
8e948497 1071 setproctitle("server %s child wait", CurHostName);
c1a66acf
EA
1072 st = waitfor(childpid);
1073 if (st == -1)
1074 syserr("%s: lost child", label);
39824631
EA
1075 else if (!WIFEXITED(st))
1076 syserr("%s: died on signal %d",
1077 label, st & 0177);
e6f08ab1 1078
c1a66acf 1079 /* if we exited on a QUIT command, complete the process */
33cbaada
EA
1080 if (WEXITSTATUS(st) == EX_QUIT)
1081 {
1082 disconnect(1, e);
c1a66acf 1083 finis();
33cbaada 1084 }
e6f08ab1 1085
c1a66acf
EA
1086 return (1);
1087 }
1088 else
1089 {
1090 /* child */
1091 InChild = TRUE;
57c97d4a 1092 QuickAbort = FALSE;
a4076aed 1093 clearenvelope(e, FALSE);
c1a66acf 1094 }
e6f08ab1 1095 }
55f0da62 1096
c1a66acf 1097 /* open alias database */
36b09297 1098 initmaps(FALSE, e);
c1a66acf
EA
1099
1100 return (0);
e6f08ab1
EA
1101}
1102\f/*
abae7b2d
EA
1103** PADDRTREE -- print address tree
1104**
1105** Used by VRFY and EXPD to dump the tree of addresses produced.
1106**
1107** Parameters:
1108** a -- address of root.
1109**
1110** Returns:
1111** none.
1112**
1113** Side Effects:
1114** prints the tree in a nice order.
1115*/
1116
1117paddrtree(a)
1118 register ADDRESS *a;
1119{
1120 static ADDRESS *prev;
1121 static int lev;
1122
1123 if (a == NULL)
1124 return;
1125 lev++;
1126 if (!bitset(QDONTSEND, a->q_flags))
1127 {
1128 if (prev != NULL)
1129 {
1130 if (prev->q_fullname != NULL)
1131 message("250-", "%s <%s>", prev->q_fullname, prev->q_paddr);
1132 else
1133 message("250-", "<%s>", prev->q_paddr);
1134 }
1135 prev = a;
1136 }
1137 paddrtree(a->q_child);
1138 paddrtree(a->q_sibling);
1139 if (--lev <= 0)
1140 {
1141 if (prev != NULL)
1142 {
1143 /* last one */
1144 if (prev->q_fullname != NULL)
1145 message("250", "%s <%s>", prev->q_fullname, prev->q_paddr);
1146 else
1147 message("250", "<%s>", prev->q_paddr);
1148 prev = NULL;
1149 }
1150 else
1151 message("550", "User unknown");
1152 }
1153}
884a20cb 1154
f3d8f6d6 1155# endif /* SMTP */