more cleanup for DSN drafts
[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
66d16835 13static char sccsid[] = "@(#)srvrsmtp.c 8.62 (Berkeley) %G% (with SMTP)";
bee79b64 14#else
66d16835 15static char sccsid[] = "@(#)srvrsmtp.c 8.62 (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");
66d16835
EA
586
587 /* arrange to ignore any current send list */
588 e->e_sendqueue = NULL;
80662ec6 589 e->e_flags |= EF_CLRQUEUE;
e6f08ab1
EA
590 if (InChild)
591 finis();
3b87200d
EA
592
593 /* clean up a bit */
594 gotmail = FALSE;
595 dropenvelope(e);
fda58daa 596 CurEnv = e = newenvelope(e, CurEnv);
e6f08ab1 597 break;
6b861048
EA
598
599 case CMDVRFY: /* vrfy -- verify address */
8f48def8
EA
600 case CMDEXPN: /* expn -- expand address */
601 vrfy = c->cmdcode == CMDVRFY;
602 if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
603 PrivacyFlags))
1c7897ef 604 {
efae59f3 605 if (vrfy)
cb282b01 606 message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
efae59f3 607 else
c5b74eae 608 message("502 Sorry, we do not allow this operation");
c9af7ef4
EA
609#ifdef LOG
610 if (LogLevel > 5)
611 syslog(LOG_INFO, "%s: %s [rejected]",
612 CurSmtpClient, inp);
613#endif
1c7897ef
EA
614 break;
615 }
616 else if (!gothello &&
8f48def8
EA
617 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
618 PrivacyFlags))
1c7897ef 619 {
b6edea3d 620 message("503 I demand that you introduce yourself first");
1c7897ef
EA
621 break;
622 }
8f48def8 623 if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
e6f08ab1 624 break;
01e8020e 625#ifdef LOG
68f7099c 626 if (LogLevel > 5)
c9af7ef4 627 syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
01e8020e 628#endif
abae7b2d 629 paddrtree(a);
6b861048
EA
630 break;
631
632 case CMDHELP: /* help -- give user info */
34d37b7d 633 help(p);
6b861048
EA
634 break;
635
636 case CMDNOOP: /* noop -- do nothing */
422d9f83 637 message("250 OK");
6b861048
EA
638 break;
639
640 case CMDQUIT: /* quit -- leave mail */
b6edea3d 641 message("221 %s closing connection", MyHostName);
251126e0 642
f6603f3b 643doquit:
66d16835
EA
644 /* arrange to ignore any current send list */
645 e->e_sendqueue = NULL;
646
251126e0 647 /* avoid future 050 messages */
33cbaada 648 disconnect(1, e);
251126e0 649
e6f08ab1
EA
650 if (InChild)
651 ExitStat = EX_QUIT;
6b861048
EA
652 finis();
653
e8ad767d 654 case CMDVERB: /* set verbose mode */
de975e1b
EA
655 if (bitset(PRIV_NOEXPN, PrivacyFlags))
656 {
657 /* this would give out the same info */
658 message("502 Verbose unavailable");
659 break;
660 }
e8ad767d 661 Verbose = TRUE;
8c8e8e94 662 e->e_sendmode = SM_DELIVER;
de975e1b 663 message("250 Verbose mode");
e8ad767d
EA
664 break;
665
8fe4fb9b 666 case CMDONEX: /* doing one transaction only */
7338e3d4 667 OneXact = TRUE;
de975e1b 668 message("250 Only one transaction");
8fe4fb9b
EA
669 break;
670
ab4889ea 671# ifdef SMTPDEBUG
e6f08ab1 672 case CMDDBGQSHOW: /* show queues */
2654b031 673 printf("Send Queue=");
a4076aed 674 printaddr(e->e_sendqueue, TRUE);
d4f42161 675 break;
09eb49d8
EA
676
677 case CMDDBGDEBUG: /* set debug mode */
9678c96d
EA
678 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
679 tTflag(p);
b6edea3d 680 message("200 Debug set");
09eb49d8
EA
681 break;
682
ab4889ea 683# else /* not SMTPDEBUG */
ab4889ea
MK
684 case CMDDBGQSHOW: /* show queues */
685 case CMDDBGDEBUG: /* set debug mode */
3e8f46ad
EA
686# endif /* SMTPDEBUG */
687 case CMDLOGBOGUS: /* bogus command */
2e15a2d8 688# ifdef LOG
5973222c 689 if (LogLevel > 0)
3e8f46ad 690 syslog(LOG_CRIT,
68f7099c 691 "\"%s\" command from %s (%s)",
7c8c5b90 692 c->cmdname, peerhostname,
3341995c 693 anynet_ntoa(&RealHostAddr));
2e15a2d8 694# endif
ab4889ea 695 /* FALL THROUGH */
d4f42161 696
6b861048 697 case CMDERROR: /* unknown command */
f6603f3b
EA
698 if (++badcommands > MAXBADCOMMANDS)
699 {
700 message("421 %s Too many bad commands; closing connection",
701 MyHostName);
702 goto doquit;
703 }
704
b6edea3d 705 message("500 Command unrecognized");
6b861048
EA
706 break;
707
708 default:
ab4889ea 709 errno = 0;
b6edea3d 710 syserr("500 smtp: unknown code %d", c->cmdcode);
6b861048
EA
711 break;
712 }
713 }
714}
715\f/*
716** SKIPWORD -- skip a fixed word.
717**
718** Parameters:
719** p -- place to start looking.
720** w -- word to skip.
721**
722** Returns:
723** p following w.
724** NULL on error.
725**
726** Side Effects:
727** clobbers the p data area.
728*/
729
730static char *
731skipword(p, w)
732 register char *p;
733 char *w;
734{
735 register char *q;
7c8c5b90 736 char *firstp = p;
6b861048
EA
737
738 /* find beginning of word */
2bee003d 739 while (isascii(*p) && isspace(*p))
6b861048
EA
740 p++;
741 q = p;
742
743 /* find end of word */
2bee003d 744 while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6b861048 745 p++;
2bee003d 746 while (isascii(*p) && isspace(*p))
6b861048
EA
747 *p++ = '\0';
748 if (*p != ':')
749 {
750 syntax:
7c8c5b90
EA
751 message("501 Syntax error in parameters scanning \"%s\"",
752 firstp);
6b861048
EA
753 Errors++;
754 return (NULL);
755 }
756 *p++ = '\0';
2bee003d 757 while (isascii(*p) && isspace(*p))
6b861048
EA
758 p++;
759
20f20f1e
EA
760 if (*p == '\0')
761 goto syntax;
762
6b861048 763 /* see if the input word matches desired word */
ed73ef1d 764 if (strcasecmp(q, w))
6b861048
EA
765 goto syntax;
766
767 return (p);
768}
34d37b7d 769\f/*
c7a0eaaf
EA
770** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
771**
772** Parameters:
773** kp -- the parameter key.
774** vp -- the value of that parameter.
775** e -- the envelope.
776**
777** Returns:
778** none.
779*/
780
781mail_esmtp_args(kp, vp, e)
782 char *kp;
783 char *vp;
784 ENVELOPE *e;
785{
786 if (strcasecmp(kp, "size") == 0)
787 {
788 if (vp == NULL)
789 {
790 usrerr("501 SIZE requires a value");
791 /* NOTREACHED */
792 }
793# ifdef __STDC__
81e7e79e 794 e->e_msgsize = strtoul(vp, (char **) NULL, 10);
c7a0eaaf 795# else
81e7e79e 796 e->e_msgsize = strtol(vp, (char **) NULL, 10);
c7a0eaaf
EA
797# endif
798 }
799 else if (strcasecmp(kp, "body") == 0)
800 {
801 if (vp == NULL)
802 {
803 usrerr("501 BODY requires a value");
804 /* NOTREACHED */
805 }
806 if (strcasecmp(vp, "8bitmime") == 0)
807 {
808 SevenBitInput = FALSE;
809 }
810 else if (strcasecmp(vp, "7bit") == 0)
811 {
812 SevenBitInput = TRUE;
813 }
814 else
815 {
816 usrerr("501 Unknown BODY type %s",
817 vp);
818 /* NOTREACHED */
819 }
820 e->e_bodytype = newstr(vp);
821 }
822 else if (strcasecmp(kp, "envid") == 0)
823 {
824 if (vp == NULL)
825 {
826 usrerr("501 ENVID requires a value");
827 /* NOTREACHED */
828 }
8b6906e3
EA
829 if (!xtextok(vp))
830 {
831 usrerr("501 Syntax error in ENVID parameter value");
832 /* NOTREACHED */
833 }
834 if (e->e_envid != NULL)
835 {
836 usrerr("501 Duplicate ENVID parameter");
837 /* NOTREACHED */
838 }
c7a0eaaf
EA
839 e->e_envid = newstr(vp);
840 }
841 else if (strcasecmp(kp, "ret") == 0)
842 {
843 if (vp == NULL)
844 {
845 usrerr("501 RET requires a value");
846 /* NOTREACHED */
847 }
8b6906e3
EA
848 if (bitset(EF_RET_PARAM, e->e_flags))
849 {
850 usrerr("501 Duplicate RET parameter");
851 /* NOTREACHED */
852 }
c7a0eaaf
EA
853 e->e_flags |= EF_RET_PARAM;
854 if (strcasecmp(vp, "hdrs") == 0)
855 e->e_flags |= EF_NO_BODY_RETN;
856 else if (strcasecmp(vp, "full") != 0)
857 {
858 usrerr("501 Bad argument \"%s\" to RET", vp);
859 /* NOTREACHED */
860 }
861 }
862 else
863 {
864 usrerr("501 %s parameter unrecognized", kp);
865 /* NOTREACHED */
866 }
867}
868\f/*
82e3dc75
EA
869** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
870**
871** Parameters:
872** a -- the address corresponding to the To: parameter.
873** kp -- the parameter key.
874** vp -- the value of that parameter.
875** e -- the envelope.
876**
877** Returns:
878** none.
879*/
880
881rcpt_esmtp_args(a, kp, vp, e)
882 ADDRESS *a;
883 char *kp;
884 char *vp;
885 ENVELOPE *e;
886{
887 if (strcasecmp(kp, "notify") == 0)
888 {
889 char *p;
890
891 if (vp == NULL)
892 {
893 usrerr("501 NOTIFY requires a value");
894 /* NOTREACHED */
895 }
896 a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
e1f691b3 897 a->q_flags |= QHASNOTIFY;
82e3dc75
EA
898 if (strcasecmp(vp, "never") == 0)
899 return;
900 for (p = vp; p != NULL; vp = p)
901 {
902 p = strchr(p, ',');
903 if (p != NULL)
904 *p++ = '\0';
905 if (strcasecmp(vp, "success") == 0)
906 a->q_flags |= QPINGONSUCCESS;
907 else if (strcasecmp(vp, "failure") == 0)
908 a->q_flags |= QPINGONFAILURE;
909 else if (strcasecmp(vp, "delay") == 0)
910 a->q_flags |= QPINGONDELAY;
911 else
912 {
913 usrerr("501 Bad argument \"%s\" to NOTIFY",
914 vp);
915 /* NOTREACHED */
916 }
917 }
918 }
82e3dc75
EA
919 else if (strcasecmp(kp, "orcpt") == 0)
920 {
921 if (vp == NULL)
922 {
923 usrerr("501 ORCPT requires a value");
924 /* NOTREACHED */
925 }
8b6906e3
EA
926 if (!xtextok(vp))
927 {
928 usrerr("501 Syntax error in ORCPT parameter value");
929 /* NOTREACHED */
930 }
931 if (a->q_orcpt != NULL)
932 {
933 usrerr("501 Duplicate ORCPT parameter");
934 /* NOTREACHED */
935 }
82e3dc75
EA
936 a->q_orcpt = newstr(vp);
937 }
938 else
939 {
940 usrerr("501 %s parameter unrecognized", kp);
941 /* NOTREACHED */
942 }
943}
944\f/*
b6edea3d
EA
945** PRINTVRFYADDR -- print an entry in the verify queue
946**
947** Parameters:
948** a -- the address to print
949** last -- set if this is the last one.
950**
951** Returns:
952** none.
953**
954** Side Effects:
955** Prints the appropriate 250 codes.
956*/
957
958printvrfyaddr(a, last)
959 register ADDRESS *a;
960 bool last;
961{
962 char fmtbuf[20];
963
964 strcpy(fmtbuf, "250");
965 fmtbuf[3] = last ? ' ' : '-';
966
ac820be5
EA
967 if (a->q_fullname == NULL)
968 {
969 if (strchr(a->q_user, '@') == NULL)
970 strcpy(&fmtbuf[4], "<%s@%s>");
971 else
972 strcpy(&fmtbuf[4], "<%s>");
973 message(fmtbuf, a->q_user, MyHostName);
974 }
b6edea3d
EA
975 else
976 {
ac820be5
EA
977 if (strchr(a->q_user, '@') == NULL)
978 strcpy(&fmtbuf[4], "%s <%s@%s>");
979 else
980 strcpy(&fmtbuf[4], "%s <%s>");
981 message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
b6edea3d 982 }
b6edea3d
EA
983}
984\f/*
34d37b7d
EA
985** HELP -- implement the HELP command.
986**
987** Parameters:
988** topic -- the topic we want help for.
989**
990** Returns:
991** none.
992**
993** Side Effects:
994** outputs the help file to message output.
995*/
996
997help(topic)
998 char *topic;
999{
1000 register FILE *hf;
1001 int len;
1002 char buf[MAXLINE];
1003 bool noinfo;
1004
c1e24818 1005 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
34d37b7d
EA
1006 {
1007 /* no help */
1b932f66 1008 errno = 0;
b6edea3d 1009 message("502 HELP not implemented");
34d37b7d
EA
1010 return;
1011 }
1012
e97afd4a
EA
1013 if (topic == NULL || *topic == '\0')
1014 topic = "smtp";
1015 else
1016 makelower(topic);
1017
34d37b7d 1018 len = strlen(topic);
34d37b7d
EA
1019 noinfo = TRUE;
1020
1021 while (fgets(buf, sizeof buf, hf) != NULL)
1022 {
1023 if (strncmp(buf, topic, len) == 0)
1024 {
1025 register char *p;
1026
f3d8f6d6 1027 p = strchr(buf, '\t');
34d37b7d
EA
1028 if (p == NULL)
1029 p = buf;
1030 else
1031 p++;
1032 fixcrlf(p, TRUE);
b6edea3d 1033 message("214-%s", p);
34d37b7d
EA
1034 noinfo = FALSE;
1035 }
1036 }
1037
1038 if (noinfo)
b6edea3d 1039 message("504 HELP topic unknown");
34d37b7d 1040 else
b6edea3d 1041 message("214 End of HELP info");
ed45aae1 1042 (void) fclose(hf);
34d37b7d 1043}
abae7b2d 1044\f/*
e6f08ab1
EA
1045** RUNINCHILD -- return twice -- once in the child, then in the parent again
1046**
1047** Parameters:
1048** label -- a string used in error messages
1049**
1050** Returns:
1051** zero in the child
1052** one in the parent
1053**
1054** Side Effects:
1055** none.
1056*/
1057
a4076aed 1058runinchild(label, e)
e6f08ab1 1059 char *label;
a4076aed 1060 register ENVELOPE *e;
e6f08ab1
EA
1061{
1062 int childpid;
1063
c1a66acf 1064 if (!OneXact)
e6f08ab1 1065 {
c1a66acf
EA
1066 childpid = dofork();
1067 if (childpid < 0)
1068 {
1069 syserr("%s: cannot fork", label);
1070 return (1);
1071 }
1072 if (childpid > 0)
1073 {
1074 auto int st;
e6f08ab1 1075
c1a66acf 1076 /* parent -- wait for child to complete */
8e948497 1077 setproctitle("server %s child wait", CurHostName);
c1a66acf
EA
1078 st = waitfor(childpid);
1079 if (st == -1)
1080 syserr("%s: lost child", label);
39824631
EA
1081 else if (!WIFEXITED(st))
1082 syserr("%s: died on signal %d",
1083 label, st & 0177);
e6f08ab1 1084
c1a66acf 1085 /* if we exited on a QUIT command, complete the process */
33cbaada
EA
1086 if (WEXITSTATUS(st) == EX_QUIT)
1087 {
1088 disconnect(1, e);
c1a66acf 1089 finis();
33cbaada 1090 }
e6f08ab1 1091
c1a66acf
EA
1092 return (1);
1093 }
1094 else
1095 {
1096 /* child */
1097 InChild = TRUE;
57c97d4a 1098 QuickAbort = FALSE;
a4076aed 1099 clearenvelope(e, FALSE);
c1a66acf 1100 }
e6f08ab1 1101 }
55f0da62 1102
c1a66acf 1103 /* open alias database */
36b09297 1104 initmaps(FALSE, e);
c1a66acf
EA
1105
1106 return (0);
e6f08ab1
EA
1107}
1108\f/*
abae7b2d
EA
1109** PADDRTREE -- print address tree
1110**
1111** Used by VRFY and EXPD to dump the tree of addresses produced.
1112**
1113** Parameters:
1114** a -- address of root.
1115**
1116** Returns:
1117** none.
1118**
1119** Side Effects:
1120** prints the tree in a nice order.
1121*/
1122
1123paddrtree(a)
1124 register ADDRESS *a;
1125{
1126 static ADDRESS *prev;
1127 static int lev;
1128
1129 if (a == NULL)
1130 return;
1131 lev++;
1132 if (!bitset(QDONTSEND, a->q_flags))
1133 {
1134 if (prev != NULL)
1135 {
1136 if (prev->q_fullname != NULL)
1137 message("250-", "%s <%s>", prev->q_fullname, prev->q_paddr);
1138 else
1139 message("250-", "<%s>", prev->q_paddr);
1140 }
1141 prev = a;
1142 }
1143 paddrtree(a->q_child);
1144 paddrtree(a->q_sibling);
1145 if (--lev <= 0)
1146 {
1147 if (prev != NULL)
1148 {
1149 /* last one */
1150 if (prev->q_fullname != NULL)
1151 message("250", "%s <%s>", prev->q_fullname, prev->q_paddr);
1152 else
1153 message("250", "<%s>", prev->q_paddr);
1154 prev = NULL;
1155 }
1156 else
1157 message("550", "User unknown");
1158 }
1159}
884a20cb 1160
f3d8f6d6 1161# endif /* SMTP */