improve DSN handling somewhat + some bug fixes
[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
d51d925d 13static char sccsid[] = "@(#)srvrsmtp.c 8.50 (Berkeley) %G% (with SMTP)";
bee79b64 14#else
d51d925d 15static char sccsid[] = "@(#)srvrsmtp.c 8.50 (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);
2bee003d 127 expand("\201e", inp, &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;
804e6d6d 337 e->e_flags |= EF_LOGSENDER;
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
EA
374 /* now parse ESMTP arguments */
375 msize = 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
410 if (strcasecmp(kp, "size") == 0)
411 {
96bfbc2c 412 if (vp == NULL)
9e2cf26f
EA
413 {
414 usrerr("501 SIZE requires a value");
415 /* NOTREACHED */
416 }
13b2cc41
EA
417# ifdef __STDC__
418 msize = strtoul(vp, (char **) NULL, 10);
419# else
420 msize = strtol(vp, (char **) NULL, 10);
421# endif
9e2cf26f 422 }
96bfbc2c
EA
423 else if (strcasecmp(kp, "body") == 0)
424 {
425 if (vp == NULL)
426 {
427 usrerr("501 BODY requires a value");
428 /* NOTREACHED */
429 }
df04c7c2 430 e->e_bodytype = newstr(vp);
96bfbc2c
EA
431 if (strcasecmp(vp, "8bitmime") == 0)
432 {
c23930c0 433 SevenBitInput = FALSE;
96bfbc2c
EA
434 }
435 else if (strcasecmp(vp, "7bit") == 0)
436 {
c23930c0 437 SevenBitInput = TRUE;
96bfbc2c
EA
438 }
439 else
440 {
441 usrerr("501 Unknown BODY type %s",
442 vp);
df04c7c2 443 /* NOTREACHED */
96bfbc2c 444 }
96bfbc2c 445 }
68d9129a
EA
446 else if (strcasecmp(kp, "envid") == 0)
447 {
448 if (vp == NULL)
449 {
450 usrerr("501 ENVID requires a value");
451 /* NOTREACHED */
452 }
453 e->e_envid = newstr(vp);
454 }
82e3dc75
EA
455 else if (strcasecmp(kp, "omts") == 0)
456 {
457 if (vp == NULL)
458 {
459 usrerr("501 OMTS requires a value");
460 /* NOTREACHED */
461 }
462 e->e_omts = newstr(vp);
463 }
9e2cf26f
EA
464 else
465 {
466 usrerr("501 %s parameter unrecognized", kp);
467 /* NOTREACHED */
468 }
469 }
346fe280
EA
470
471 if (MaxMessageSize > 0 && msize > MaxMessageSize)
472 {
473 usrerr("552 Message size exceeds fixed maximum message size (%ld)",
474 MaxMessageSize);
475 /* NOTREACHED */
476 }
9e2cf26f
EA
477
478 if (!enoughspace(msize))
479 {
480 message("452 Insufficient disk space; try again later");
481 break;
482 }
b6edea3d 483 message("250 Sender ok");
182c060a 484 gotmail = TRUE;
506a2500
EA
485
486 /* optimize: non-interactive, don't expand aliases */
28a8a6ef 487 if (e->e_sendmode != SM_DELIVER)
506a2500
EA
488 e->e_flags |= EF_VRFYONLY;
489
6b861048
EA
490 break;
491
4a4ebe09 492 case CMDRCPT: /* rcpt -- designate recipient */
d7f41a7b
EA
493 if (!gotmail)
494 {
495 usrerr("503 Need MAIL before RCPT");
496 break;
497 }
8e948497 498 SmtpPhase = "server RCPT";
d344c0b7 499 if (setjmp(TopFrame) > 0)
9bfb75c1 500 {
a4076aed 501 e->e_flags &= ~EF_FATALERRS;
d344c0b7 502 break;
9bfb75c1 503 }
d344c0b7 504 QuickAbort = TRUE;
f61c3c40 505 LogUsrErrs = TRUE;
6b861048
EA
506 p = skipword(p, "to");
507 if (p == NULL)
508 break;
abae7b2d
EA
509 a = sendto(p, 1, (ADDRESS *) NULL, 0);
510# ifdef DEBUG
511 if (Debug > 1)
512 printaddr(a, TRUE);
513# endif DEBUG
d344c0b7
EA
514 if (Errors != 0)
515 break;
516
517 /* no errors during parsing, but might be a duplicate */
a4076aed 518 e->e_to = p;
d344c0b7 519 if (!bitset(QBADADDR, a->q_flags))
fba945cd 520 {
fe3849ea
EA
521 message("250 Recipient ok%s",
522 bitset(QQUEUEUP, a->q_flags) ?
523 " (will queue)" : "");
fba945cd
EA
524 nrcpts++;
525 }
d344c0b7 526 else
6b861048 527 {
d344c0b7 528 /* punt -- should keep message in ADDRESS.... */
b6edea3d 529 message("550 Addressee unknown");
6b861048 530 }
a4076aed 531 e->e_to = NULL;
6b861048
EA
532 break;
533
534 case CMDDATA: /* data -- text of mail */
8e948497 535 SmtpPhase = "server DATA";
3b87200d 536 if (!gotmail)
4a4ebe09 537 {
b6edea3d 538 message("503 Need MAIL command");
4a4ebe09
EA
539 break;
540 }
fe3849ea 541 else if (nrcpts <= 0)
6b861048 542 {
b6edea3d 543 message("503 Need RCPT (recipient)");
4a4ebe09 544 break;
6b861048 545 }
4a4ebe09 546
959cf51d 547 /* check to see if we need to re-expand aliases */
fd57f063
EA
548 /* also reset QBADADDR on already-diagnosted addrs */
549 doublequeue = FALSE;
959cf51d
EA
550 for (a = e->e_sendqueue; a != NULL; a = a->q_next)
551 {
552 if (bitset(QVERIFIED, a->q_flags))
fd57f063
EA
553 {
554 /* need to re-expand aliases */
555 doublequeue = TRUE;
556 }
557 if (bitset(QBADADDR, a->q_flags))
558 {
559 /* make this "go away" */
560 a->q_flags |= QDONTSEND;
561 a->q_flags &= ~QBADADDR;
562 }
959cf51d
EA
563 }
564
4a4ebe09 565 /* collect the text of the message */
2e3062fe 566 SmtpPhase = "collect";
c23930c0 567 collect(InChannel, TRUE, doublequeue, NULL, e);
4f8e0a23
EA
568 if (Errors != 0)
569 goto abortmessage;
439fd0d9
EA
570
571 /* from now on, we have to operate silently */
4f8e0a23 572 HoldErrs = TRUE;
439fd0d9 573 e->e_errormode = EM_MAIL;
4a4ebe09 574
8eedb496
EA
575 /*
576 ** Arrange to send to everyone.
577 ** If sending to multiple people, mail back
578 ** errors rather than reporting directly.
579 ** In any case, don't mail back errors for
580 ** anything that has happened up to
581 ** now (the other end will do this).
bcf74f25
EA
582 ** Truncate our transcript -- the mail has gotten
583 ** to us successfully, and if we have
584 ** to mail this back, it will be easier
585 ** on the reader.
8eedb496
EA
586 ** Then send to everyone.
587 ** Finally give a reply code. If an error has
588 ** already been given, don't mail a
589 ** message back.
e6f08ab1 590 ** We goose error returns by clearing error bit.
8eedb496
EA
591 */
592
2e3062fe 593 SmtpPhase = "delivery";
a4076aed 594 e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
4aed8f3a 595 id = e->e_id;
4a4ebe09 596
439fd0d9 597 if (doublequeue)
2f6a8a78 598 {
439fd0d9
EA
599 /* make sure it is in the queue */
600 queueup(e, TRUE, FALSE);
2f6a8a78
EA
601 }
602 else
a891bb7e 603 {
439fd0d9
EA
604 /* send to all recipients */
605 sendall(e, SM_DEFAULT);
606 }
607 e->e_to = NULL;
fba945cd 608
439fd0d9
EA
609 /* issue success message */
610 message("250 %s Message accepted for delivery", id);
d0268270 611
439fd0d9
EA
612 /* if we just queued, poke it */
613 if (doublequeue && e->e_sendmode != SM_QUEUE)
614 {
615 extern pid_t dowork();
616
617 unlockqueue(e);
618 (void) dowork(id, TRUE, TRUE, e);
a891bb7e
EA
619 }
620
6366413a
EA
621 /* now make it really happen */
622 if (!Verbose && e->e_sendmode != SM_QUEUE)
623 dowork(id, TRUE, e);
624
fba945cd 625 abortmessage:
e6f08ab1
EA
626 /* if in a child, pop back to our parent */
627 if (InChild)
628 finis();
2e3062fe
EA
629
630 /* clean up a bit */
3b87200d 631 gotmail = FALSE;
a4076aed 632 dropenvelope(e);
fda58daa 633 CurEnv = e = newenvelope(e, CurEnv);
a4076aed 634 e->e_flags = BlankEnvelope.e_flags;
6b861048
EA
635 break;
636
637 case CMDRSET: /* rset -- reset state */
b6edea3d 638 message("250 Reset state");
80662ec6 639 e->e_flags |= EF_CLRQUEUE;
e6f08ab1
EA
640 if (InChild)
641 finis();
3b87200d
EA
642
643 /* clean up a bit */
644 gotmail = FALSE;
645 dropenvelope(e);
fda58daa 646 CurEnv = e = newenvelope(e, CurEnv);
e6f08ab1 647 break;
6b861048
EA
648
649 case CMDVRFY: /* vrfy -- verify address */
8f48def8
EA
650 case CMDEXPN: /* expn -- expand address */
651 vrfy = c->cmdcode == CMDVRFY;
652 if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
653 PrivacyFlags))
1c7897ef 654 {
efae59f3 655 if (vrfy)
cb282b01 656 message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
efae59f3 657 else
c5b74eae 658 message("502 Sorry, we do not allow this operation");
c9af7ef4
EA
659#ifdef LOG
660 if (LogLevel > 5)
661 syslog(LOG_INFO, "%s: %s [rejected]",
662 CurSmtpClient, inp);
663#endif
1c7897ef
EA
664 break;
665 }
666 else if (!gothello &&
8f48def8
EA
667 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
668 PrivacyFlags))
1c7897ef 669 {
b6edea3d 670 message("503 I demand that you introduce yourself first");
1c7897ef
EA
671 break;
672 }
8f48def8 673 if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
e6f08ab1 674 break;
01e8020e 675#ifdef LOG
68f7099c 676 if (LogLevel > 5)
c9af7ef4 677 syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
01e8020e 678#endif
abae7b2d 679 paddrtree(a);
6b861048
EA
680 break;
681
682 case CMDHELP: /* help -- give user info */
34d37b7d 683 help(p);
6b861048
EA
684 break;
685
686 case CMDNOOP: /* noop -- do nothing */
422d9f83 687 message("250 OK");
6b861048
EA
688 break;
689
690 case CMDQUIT: /* quit -- leave mail */
b6edea3d 691 message("221 %s closing connection", MyHostName);
251126e0 692
f6603f3b 693doquit:
251126e0 694 /* avoid future 050 messages */
33cbaada 695 disconnect(1, e);
251126e0 696
e6f08ab1
EA
697 if (InChild)
698 ExitStat = EX_QUIT;
6b861048
EA
699 finis();
700
e8ad767d 701 case CMDVERB: /* set verbose mode */
de975e1b
EA
702 if (bitset(PRIV_NOEXPN, PrivacyFlags))
703 {
704 /* this would give out the same info */
705 message("502 Verbose unavailable");
706 break;
707 }
e8ad767d 708 Verbose = TRUE;
8c8e8e94 709 e->e_sendmode = SM_DELIVER;
de975e1b 710 message("250 Verbose mode");
e8ad767d
EA
711 break;
712
8fe4fb9b 713 case CMDONEX: /* doing one transaction only */
7338e3d4 714 OneXact = TRUE;
de975e1b 715 message("250 Only one transaction");
8fe4fb9b
EA
716 break;
717
ab4889ea 718# ifdef SMTPDEBUG
e6f08ab1 719 case CMDDBGQSHOW: /* show queues */
2654b031 720 printf("Send Queue=");
a4076aed 721 printaddr(e->e_sendqueue, TRUE);
d4f42161 722 break;
09eb49d8
EA
723
724 case CMDDBGDEBUG: /* set debug mode */
9678c96d
EA
725 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
726 tTflag(p);
b6edea3d 727 message("200 Debug set");
09eb49d8
EA
728 break;
729
ab4889ea 730# else /* not SMTPDEBUG */
ab4889ea
MK
731 case CMDDBGQSHOW: /* show queues */
732 case CMDDBGDEBUG: /* set debug mode */
3e8f46ad
EA
733# endif /* SMTPDEBUG */
734 case CMDLOGBOGUS: /* bogus command */
2e15a2d8 735# ifdef LOG
5973222c 736 if (LogLevel > 0)
3e8f46ad 737 syslog(LOG_CRIT,
68f7099c 738 "\"%s\" command from %s (%s)",
7c8c5b90 739 c->cmdname, peerhostname,
3341995c 740 anynet_ntoa(&RealHostAddr));
2e15a2d8 741# endif
ab4889ea 742 /* FALL THROUGH */
d4f42161 743
6b861048 744 case CMDERROR: /* unknown command */
f6603f3b
EA
745 if (++badcommands > MAXBADCOMMANDS)
746 {
747 message("421 %s Too many bad commands; closing connection",
748 MyHostName);
749 goto doquit;
750 }
751
b6edea3d 752 message("500 Command unrecognized");
6b861048
EA
753 break;
754
755 default:
ab4889ea 756 errno = 0;
b6edea3d 757 syserr("500 smtp: unknown code %d", c->cmdcode);
6b861048
EA
758 break;
759 }
760 }
761}
762\f/*
763** SKIPWORD -- skip a fixed word.
764**
765** Parameters:
766** p -- place to start looking.
767** w -- word to skip.
768**
769** Returns:
770** p following w.
771** NULL on error.
772**
773** Side Effects:
774** clobbers the p data area.
775*/
776
777static char *
778skipword(p, w)
779 register char *p;
780 char *w;
781{
782 register char *q;
7c8c5b90 783 char *firstp = p;
6b861048
EA
784
785 /* find beginning of word */
2bee003d 786 while (isascii(*p) && isspace(*p))
6b861048
EA
787 p++;
788 q = p;
789
790 /* find end of word */
2bee003d 791 while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6b861048 792 p++;
2bee003d 793 while (isascii(*p) && isspace(*p))
6b861048
EA
794 *p++ = '\0';
795 if (*p != ':')
796 {
797 syntax:
7c8c5b90
EA
798 message("501 Syntax error in parameters scanning \"%s\"",
799 firstp);
6b861048
EA
800 Errors++;
801 return (NULL);
802 }
803 *p++ = '\0';
2bee003d 804 while (isascii(*p) && isspace(*p))
6b861048
EA
805 p++;
806
20f20f1e
EA
807 if (*p == '\0')
808 goto syntax;
809
6b861048 810 /* see if the input word matches desired word */
ed73ef1d 811 if (strcasecmp(q, w))
6b861048
EA
812 goto syntax;
813
814 return (p);
815}
34d37b7d 816\f/*
82e3dc75
EA
817** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
818**
819** Parameters:
820** a -- the address corresponding to the To: parameter.
821** kp -- the parameter key.
822** vp -- the value of that parameter.
823** e -- the envelope.
824**
825** Returns:
826** none.
827*/
828
829rcpt_esmtp_args(a, kp, vp, e)
830 ADDRESS *a;
831 char *kp;
832 char *vp;
833 ENVELOPE *e;
834{
835 if (strcasecmp(kp, "notify") == 0)
836 {
837 char *p;
838
839 if (vp == NULL)
840 {
841 usrerr("501 NOTIFY requires a value");
842 /* NOTREACHED */
843 }
844 a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
845 if (strcasecmp(vp, "never") == 0)
846 return;
847 for (p = vp; p != NULL; vp = p)
848 {
849 p = strchr(p, ',');
850 if (p != NULL)
851 *p++ = '\0';
852 if (strcasecmp(vp, "success") == 0)
853 a->q_flags |= QPINGONSUCCESS;
854 else if (strcasecmp(vp, "failure") == 0)
855 a->q_flags |= QPINGONFAILURE;
856 else if (strcasecmp(vp, "delay") == 0)
857 a->q_flags |= QPINGONDELAY;
858 else
859 {
860 usrerr("501 Bad argument \"%s\" to NOTIFY",
861 vp);
862 /* NOTREACHED */
863 }
864 }
865 }
866 else if (strcasecmp(kp, "ret") == 0)
867 {
868 if (vp == NULL)
869 {
870 usrerr("501 RET requires a value");
871 /* NOTREACHED */
872 }
873 a->q_flags |= QHAS_RET_PARAM;
874 if (strcasecmp(vp, "hdrs") == 0)
875 a->q_flags |= QRET_HDRS;
876 else if (strcasecmp(vp, "full") != 0)
877 {
878 usrerr("501 Bad argument \"%s\" to RET", vp);
879 /* NOTREACHED */
880 }
881 }
882 else if (strcasecmp(kp, "orcpt") == 0)
883 {
884 if (vp == NULL)
885 {
886 usrerr("501 ORCPT requires a value");
887 /* NOTREACHED */
888 }
889 a->q_orcpt = newstr(vp);
890 }
891 else
892 {
893 usrerr("501 %s parameter unrecognized", kp);
894 /* NOTREACHED */
895 }
896}
897\f/*
b6edea3d
EA
898** PRINTVRFYADDR -- print an entry in the verify queue
899**
900** Parameters:
901** a -- the address to print
902** last -- set if this is the last one.
903**
904** Returns:
905** none.
906**
907** Side Effects:
908** Prints the appropriate 250 codes.
909*/
910
911printvrfyaddr(a, last)
912 register ADDRESS *a;
913 bool last;
914{
915 char fmtbuf[20];
916
917 strcpy(fmtbuf, "250");
918 fmtbuf[3] = last ? ' ' : '-';
919
ac820be5
EA
920 if (a->q_fullname == NULL)
921 {
922 if (strchr(a->q_user, '@') == NULL)
923 strcpy(&fmtbuf[4], "<%s@%s>");
924 else
925 strcpy(&fmtbuf[4], "<%s>");
926 message(fmtbuf, a->q_user, MyHostName);
927 }
b6edea3d
EA
928 else
929 {
ac820be5
EA
930 if (strchr(a->q_user, '@') == NULL)
931 strcpy(&fmtbuf[4], "%s <%s@%s>");
932 else
933 strcpy(&fmtbuf[4], "%s <%s>");
934 message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
b6edea3d 935 }
b6edea3d
EA
936}
937\f/*
34d37b7d
EA
938** HELP -- implement the HELP command.
939**
940** Parameters:
941** topic -- the topic we want help for.
942**
943** Returns:
944** none.
945**
946** Side Effects:
947** outputs the help file to message output.
948*/
949
950help(topic)
951 char *topic;
952{
953 register FILE *hf;
954 int len;
955 char buf[MAXLINE];
956 bool noinfo;
957
c1e24818 958 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
34d37b7d
EA
959 {
960 /* no help */
1b932f66 961 errno = 0;
b6edea3d 962 message("502 HELP not implemented");
34d37b7d
EA
963 return;
964 }
965
e97afd4a
EA
966 if (topic == NULL || *topic == '\0')
967 topic = "smtp";
968 else
969 makelower(topic);
970
34d37b7d 971 len = strlen(topic);
34d37b7d
EA
972 noinfo = TRUE;
973
974 while (fgets(buf, sizeof buf, hf) != NULL)
975 {
976 if (strncmp(buf, topic, len) == 0)
977 {
978 register char *p;
979
f3d8f6d6 980 p = strchr(buf, '\t');
34d37b7d
EA
981 if (p == NULL)
982 p = buf;
983 else
984 p++;
985 fixcrlf(p, TRUE);
b6edea3d 986 message("214-%s", p);
34d37b7d
EA
987 noinfo = FALSE;
988 }
989 }
990
991 if (noinfo)
b6edea3d 992 message("504 HELP topic unknown");
34d37b7d 993 else
b6edea3d 994 message("214 End of HELP info");
ed45aae1 995 (void) fclose(hf);
34d37b7d 996}
abae7b2d 997\f/*
e6f08ab1
EA
998** RUNINCHILD -- return twice -- once in the child, then in the parent again
999**
1000** Parameters:
1001** label -- a string used in error messages
1002**
1003** Returns:
1004** zero in the child
1005** one in the parent
1006**
1007** Side Effects:
1008** none.
1009*/
1010
a4076aed 1011runinchild(label, e)
e6f08ab1 1012 char *label;
a4076aed 1013 register ENVELOPE *e;
e6f08ab1
EA
1014{
1015 int childpid;
1016
c1a66acf 1017 if (!OneXact)
e6f08ab1 1018 {
c1a66acf
EA
1019 childpid = dofork();
1020 if (childpid < 0)
1021 {
1022 syserr("%s: cannot fork", label);
1023 return (1);
1024 }
1025 if (childpid > 0)
1026 {
1027 auto int st;
e6f08ab1 1028
c1a66acf 1029 /* parent -- wait for child to complete */
8e948497 1030 setproctitle("server %s child wait", CurHostName);
c1a66acf
EA
1031 st = waitfor(childpid);
1032 if (st == -1)
1033 syserr("%s: lost child", label);
39824631
EA
1034 else if (!WIFEXITED(st))
1035 syserr("%s: died on signal %d",
1036 label, st & 0177);
e6f08ab1 1037
c1a66acf 1038 /* if we exited on a QUIT command, complete the process */
33cbaada
EA
1039 if (WEXITSTATUS(st) == EX_QUIT)
1040 {
1041 disconnect(1, e);
c1a66acf 1042 finis();
33cbaada 1043 }
e6f08ab1 1044
c1a66acf
EA
1045 return (1);
1046 }
1047 else
1048 {
1049 /* child */
1050 InChild = TRUE;
57c97d4a 1051 QuickAbort = FALSE;
a4076aed 1052 clearenvelope(e, FALSE);
c1a66acf 1053 }
e6f08ab1 1054 }
55f0da62 1055
c1a66acf 1056 /* open alias database */
36b09297 1057 initmaps(FALSE, e);
c1a66acf
EA
1058
1059 return (0);
e6f08ab1
EA
1060}
1061\f/*
abae7b2d
EA
1062** PADDRTREE -- print address tree
1063**
1064** Used by VRFY and EXPD to dump the tree of addresses produced.
1065**
1066** Parameters:
1067** a -- address of root.
1068**
1069** Returns:
1070** none.
1071**
1072** Side Effects:
1073** prints the tree in a nice order.
1074*/
1075
1076paddrtree(a)
1077 register ADDRESS *a;
1078{
1079 static ADDRESS *prev;
1080 static int lev;
1081
1082 if (a == NULL)
1083 return;
1084 lev++;
1085 if (!bitset(QDONTSEND, a->q_flags))
1086 {
1087 if (prev != NULL)
1088 {
1089 if (prev->q_fullname != NULL)
1090 message("250-", "%s <%s>", prev->q_fullname, prev->q_paddr);
1091 else
1092 message("250-", "<%s>", prev->q_paddr);
1093 }
1094 prev = a;
1095 }
1096 paddrtree(a->q_child);
1097 paddrtree(a->q_sibling);
1098 if (--lev <= 0)
1099 {
1100 if (prev != NULL)
1101 {
1102 /* last one */
1103 if (prev->q_fullname != NULL)
1104 message("250", "%s <%s>", prev->q_fullname, prev->q_paddr);
1105 else
1106 message("250", "<%s>", prev->q_paddr);
1107 prev = NULL;
1108 }
1109 else
1110 message("550", "User unknown");
1111 }
1112}
884a20cb 1113
f3d8f6d6 1114# endif /* SMTP */