fixes for problems causing mail to be both sent and rejected
[unix-history] / usr / src / usr.sbin / sendmail / src / srvrsmtp.c
CommitLineData
b2a81223 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
bee79b64
KB
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
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
fba945cd 13static char sccsid[] = "@(#)srvrsmtp.c 6.52 (Berkeley) %G% (with SMTP)";
bee79b64 14#else
fba945cd 15static char sccsid[] = "@(#)srvrsmtp.c 6.52 (Berkeley) %G% (without SMTP)";
bee79b64
KB
16#endif
17#endif /* not lint */
b2a81223 18
e6f08ab1 19# include <errno.h>
671d4b5c 20# include <signal.h>
6b861048 21
bee79b64 22# ifdef SMTP
d727056e 23
6b861048
EA
24/*
25** SMTP -- run the SMTP protocol.
26**
27** Parameters:
28** none.
29**
30** Returns:
31** never.
32**
33** Side Effects:
34** Reads commands from the input channel and processes
35** them.
36*/
37
38struct cmd
39{
40 char *cmdname; /* command name */
41 int cmdcode; /* internal code, see below */
42};
43
44/* values for cmdcode */
45# define CMDERROR 0 /* bad command */
46# define CMDMAIL 1 /* mail -- designate sender */
4a4ebe09 47# define CMDRCPT 2 /* rcpt -- designate recipient */
6b861048 48# define CMDDATA 3 /* data -- send message text */
7d7fdf93 49# define CMDHOPS 4 /* hops -- specify hop count */
e6f08ab1
EA
50# define CMDRSET 4 /* rset -- reset state */
51# define CMDVRFY 5 /* vrfy -- verify address */
8f48def8 52# define CMDEXPN 6 /* expn -- expand address */
e6f08ab1
EA
53# define CMDNOOP 7 /* noop -- do nothing */
54# define CMDQUIT 8 /* quit -- close connection and die */
55# define CMDHELO 9 /* helo -- be polite */
8f48def8 56# define CMDHELP 10 /* help -- give usage info */
34f2d20e 57# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
8f48def8
EA
58/* non-standard commands */
59# define CMDONEX 16 /* onex -- sending one transaction only */
60# define CMDVERB 17 /* verb -- go into verbose mode */
ab4889ea 61/* debugging-only commands, only enabled if SMTPDEBUG is defined */
8f48def8
EA
62# define CMDDBGQSHOW 24 /* showq -- show send queue */
63# define CMDDBGDEBUG 25 /* debug -- set debug mode */
6b861048
EA
64
65static struct cmd CmdTab[] =
66{
67 "mail", CMDMAIL,
4a4ebe09 68 "rcpt", CMDRCPT,
6b861048 69 "data", CMDDATA,
6b861048
EA
70 "rset", CMDRSET,
71 "vrfy", CMDVRFY,
8f48def8 72 "expn", CMDEXPN,
633a2e02 73 "expn", CMDVRFY,
6b861048
EA
74 "help", CMDHELP,
75 "noop", CMDNOOP,
76 "quit", CMDQUIT,
4a4ebe09 77 "helo", CMDHELO,
34f2d20e 78 "ehlo", CMDEHLO,
e8ad767d 79 "verb", CMDVERB,
8fe4fb9b 80 "onex", CMDONEX,
7d7fdf93 81 "hops", CMDHOPS,
ab4889ea
MK
82 /*
83 * remaining commands are here only
84 * to trap and log attempts to use them
85 */
e6f08ab1 86 "showq", CMDDBGQSHOW,
ad20d67b 87 "debug", CMDDBGDEBUG,
6b861048
EA
88 NULL, CMDERROR,
89};
90
e6f08ab1 91bool InChild = FALSE; /* true if running in a subprocess */
7338e3d4 92bool OneXact = FALSE; /* one xaction only this run */
1972fb40 93
e6f08ab1 94#define EX_QUIT 22 /* special code for QUIT command */
e8ad767d 95
a4076aed
EA
96smtp(e)
97 register ENVELOPE *e;
6b861048 98{
6b861048 99 register char *p;
e8ad767d 100 register struct cmd *c;
6b861048 101 char *cmd;
0df908a9 102 static char *skipword();
abae7b2d
EA
103 extern ADDRESS *sendto();
104 ADDRESS *a;
6b861048 105
abae7b2d 106 hasmail = FALSE;
20219e3f 107 if (fileno(OutChannel) != fileno(stdout))
1f1cc003
EA
108 {
109 /* arrange for debugging output to go to remote host */
20219e3f 110 (void) dup2(fileno(OutChannel), fileno(stdout));
1f1cc003 111 }
a4076aed 112 settime(e);
6e99f903 113 CurHostName = RealHostName;
ade7da2a 114 setproctitle("srvrsmtp %s startup", CurHostName);
2bee003d 115 expand("\201e", inp, &inp[sizeof inp], e);
b6edea3d 116 message("220 %s", inp);
6fd6d536 117 protocol = NULL;
37444fe1 118 sendinghost = macvalue('s', e);
1c7897ef 119 gothello = FALSE;
6fd6d536 120 gotmail = FALSE;
6b861048
EA
121 for (;;)
122 {
d344c0b7
EA
123 /* arrange for backout */
124 if (setjmp(TopFrame) > 0 && InChild)
b8bf5eba
EA
125 {
126 QuickAbort = FALSE;
127 SuprErrs = TRUE;
d344c0b7 128 finis();
b8bf5eba 129 }
d344c0b7
EA
130 QuickAbort = FALSE;
131 HoldErrs = FALSE;
f61c3c40 132 LogUsrErrs = FALSE;
8f48def8 133 e->e_flags &= ~EF_VRFYONLY;
d344c0b7 134
37eaaadb 135 /* setup for the read */
a4076aed 136 e->e_to = NULL;
34d37b7d 137 Errors = 0;
09eb49d8 138 (void) fflush(stdout);
37eaaadb 139
37eaaadb 140 /* read the input line */
ade7da2a
EA
141 SmtpPhase = "srvrsmtp cmd read";
142 setproctitle("srvrsmtp %s cmd read", CurHostName);
3b87200d 143 p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
37eaaadb 144
2439b900 145 /* handle errors */
37eaaadb 146 if (p == NULL)
6b861048
EA
147 {
148 /* end of file, just die */
b6edea3d 149 message("421 %s Lost input channel from %s",
57c97d4a 150 MyHostName, CurHostName);
6d6f9196 151#ifdef LOG
68f7099c 152 if (LogLevel > 1)
6d6f9196
EA
153 syslog(LOG_NOTICE, "lost input channel from %s",
154 CurHostName);
155#endif
074b0256
EA
156 if (InChild)
157 ExitStat = EX_QUIT;
6b861048
EA
158 finis();
159 }
160
161 /* clean up end of line */
2768afe3 162 fixcrlf(inp, TRUE);
6b861048 163
49086753 164 /* echo command to transcript */
a4076aed
EA
165 if (e->e_xfp != NULL)
166 fprintf(e->e_xfp, "<<< %s\n", inp);
49086753 167
ade7da2a
EA
168 if (e->e_id == NULL)
169 setproctitle("%s: %s", CurHostName, inp);
170 else
171 setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
172
6b861048 173 /* break off command */
2bee003d 174 for (p = inp; isascii(*p) && isspace(*p); p++)
6b861048 175 continue;
f43b04ce 176 cmd = cmdbuf;
2bee003d
EA
177 while (*p != '\0' &&
178 !(isascii(*p) && isspace(*p)) &&
179 cmd < &cmdbuf[sizeof cmdbuf - 2])
a0225d08
EA
180 *cmd++ = *p++;
181 *cmd = '\0';
6b861048 182
a1a07282 183 /* throw away leading whitespace */
2bee003d 184 while (isascii(*p) && isspace(*p))
a1a07282
EA
185 p++;
186
6b861048
EA
187 /* decode command */
188 for (c = CmdTab; c->cmdname != NULL; c++)
189 {
ed73ef1d 190 if (!strcasecmp(c->cmdname, cmdbuf))
6b861048
EA
191 break;
192 }
193
5ae51cd5
EA
194 /* reset errors */
195 errno = 0;
196
6b861048
EA
197 /* process command */
198 switch (c->cmdcode)
199 {
4a4ebe09 200 case CMDHELO: /* hello -- introduce yourself */
34f2d20e
EA
201 case CMDEHLO: /* extended hello */
202 if (c->cmdcode == CMDEHLO)
203 {
204 protocol = "ESMTP";
205 SmtpPhase = "EHLO";
206 }
207 else
208 {
209 protocol = "SMTP";
210 SmtpPhase = "HELO";
211 }
37444fe1 212 sendinghost = newstr(p);
5973222c 213 if (strcasecmp(p, RealHostName) != 0)
1972fb40 214 {
94bc039a
EA
215 auth_warning(e, "Host %s claimed to be %s",
216 RealHostName, p);
1972fb40 217 }
a9bac7a9
EA
218 p = macvalue('_', e);
219 if (p == NULL)
37444fe1 220 p = RealHostName;
abae7b2d 221 message("250", "%s Hello %s, pleased to meet you", HostName, p);
4a4ebe09
EA
222 break;
223
6b861048 224 case CMDMAIL: /* mail -- designate sender */
2e3062fe
EA
225 SmtpPhase = "MAIL";
226
8fe4fb9b 227 /* check for validity of this command */
94bc039a 228 if (!gothello)
1c7897ef 229 {
a9bac7a9 230 /* set sending host to our known value */
37444fe1
EA
231 if (sendinghost == NULL)
232 sendinghost = RealHostName;
a9bac7a9 233
94bc039a 234 if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
f967c46a 235 {
94bc039a 236 message("503 Polite people say HELO first");
f967c46a
EA
237 break;
238 }
94bc039a 239 else
f967c46a 240 {
94bc039a
EA
241 auth_warning(e,
242 "Host %s didn't use HELO protocol",
243 RealHostName);
f967c46a 244 }
1c7897ef 245 }
3b87200d 246 if (gotmail)
2768afe3 247 {
b6edea3d 248 message("503 Sender already specified");
2768afe3
EA
249 break;
250 }
e6f08ab1
EA
251 if (InChild)
252 {
ab4889ea 253 errno = 0;
b6edea3d 254 syserr("503 Nested MAIL command: MAIL %s", p);
074b0256 255 finis();
e6f08ab1
EA
256 }
257
258 /* fork a subprocess to process this command */
a4076aed 259 if (runinchild("SMTP-MAIL", e) > 0)
e6f08ab1 260 break;
34f2d20e
EA
261 if (protocol == NULL)
262 protocol = "SMTP";
263 define('r', protocol, e);
37444fe1 264 define('s', sendinghost, e);
a4076aed 265 initsys(e);
fba945cd 266 nrcpts = 0;
7aa1e2bc 267 setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
e6f08ab1
EA
268
269 /* child -- go do the processing */
6b861048
EA
270 p = skipword(p, "from");
271 if (p == NULL)
272 break;
aa102c71 273 if (setjmp(TopFrame) > 0)
182c060a
EA
274 {
275 /* this failed -- undo work */
276 if (InChild)
b8bf5eba
EA
277 {
278 QuickAbort = FALSE;
279 SuprErrs = TRUE;
182c060a 280 finis();
b8bf5eba 281 }
aa102c71 282 break;
182c060a 283 }
aa102c71 284 QuickAbort = TRUE;
9e2cf26f
EA
285
286 /* must parse sender first */
287 delimptr = NULL;
4a2da288 288 setsender(p, e, &delimptr, FALSE);
9e2cf26f
EA
289 p = delimptr;
290 if (p != NULL && *p != '\0')
291 *p++ = '\0';
292
293 /* now parse ESMTP arguments */
294 msize = 0;
295 for (; p != NULL && *p != '\0'; p++)
296 {
297 char *kp;
298 char *vp;
299
300 /* locate the beginning of the keyword */
301 while (isascii(*p) && isspace(*p))
302 p++;
303 if (*p == '\0')
304 break;
305 kp = p;
306
307 /* skip to the value portion */
308 while (isascii(*p) && isalnum(*p) || *p == '-')
309 p++;
310 if (*p == '=')
311 {
312 *p++ = '\0';
313 vp = p;
314
315 /* skip to the end of the value */
316 while (*p != '\0' && *p != ' ' &&
317 !(isascii(*p) && iscntrl(*p)) &&
318 *p != '=')
319 p++;
320 }
321
322 if (*p != '\0')
323 *p++ = '\0';
324
325 if (tTd(19, 1))
326 printf("MAIL: got arg %s=%s\n", kp,
327 vp == NULL ? "<null>" : vp);
328
329 if (strcasecmp(kp, "size") == 0)
330 {
96bfbc2c 331 if (vp == NULL)
9e2cf26f
EA
332 {
333 usrerr("501 SIZE requires a value");
334 /* NOTREACHED */
335 }
336 msize = atol(vp);
337 }
96bfbc2c
EA
338 else if (strcasecmp(kp, "body") == 0)
339 {
340 if (vp == NULL)
341 {
342 usrerr("501 BODY requires a value");
343 /* NOTREACHED */
344 }
345# ifdef MIME
346 if (strcasecmp(vp, "8bitmime") == 0)
347 {
348 e->e_bodytype = "8BITMIME";
6a7569d3 349 SevenBit = FALSE;
96bfbc2c
EA
350 }
351 else if (strcasecmp(vp, "7bit") == 0)
352 {
353 e->e_bodytype = "7BIT";
6a7569d3 354 SevenBit = TRUE;
96bfbc2c
EA
355 }
356 else
357 {
358 usrerr("501 Unknown BODY type %s",
359 vp);
360 }
361# endif
362 }
9e2cf26f
EA
363 else
364 {
365 usrerr("501 %s parameter unrecognized", kp);
366 /* NOTREACHED */
367 }
368 }
346fe280
EA
369
370 if (MaxMessageSize > 0 && msize > MaxMessageSize)
371 {
372 usrerr("552 Message size exceeds fixed maximum message size (%ld)",
373 MaxMessageSize);
374 /* NOTREACHED */
375 }
9e2cf26f
EA
376
377 if (!enoughspace(msize))
378 {
379 message("452 Insufficient disk space; try again later");
380 break;
381 }
b6edea3d 382 message("250 Sender ok");
182c060a 383 gotmail = TRUE;
506a2500
EA
384
385 /* optimize: non-interactive, don't expand aliases */
28a8a6ef 386 if (e->e_sendmode != SM_DELIVER)
506a2500
EA
387 e->e_flags |= EF_VRFYONLY;
388
6b861048
EA
389 break;
390
4a4ebe09 391 case CMDRCPT: /* rcpt -- designate recipient */
d7f41a7b
EA
392 if (!gotmail)
393 {
394 usrerr("503 Need MAIL before RCPT");
395 break;
396 }
2e3062fe 397 SmtpPhase = "RCPT";
d344c0b7 398 if (setjmp(TopFrame) > 0)
9bfb75c1 399 {
a4076aed 400 e->e_flags &= ~EF_FATALERRS;
d344c0b7 401 break;
9bfb75c1 402 }
d344c0b7 403 QuickAbort = TRUE;
f61c3c40 404 LogUsrErrs = TRUE;
6b861048
EA
405 p = skipword(p, "to");
406 if (p == NULL)
407 break;
abae7b2d
EA
408 a = sendto(p, 1, (ADDRESS *) NULL, 0);
409# ifdef DEBUG
410 if (Debug > 1)
411 printaddr(a, TRUE);
412# endif DEBUG
d344c0b7
EA
413 if (Errors != 0)
414 break;
415
416 /* no errors during parsing, but might be a duplicate */
a4076aed 417 e->e_to = p;
d344c0b7 418 if (!bitset(QBADADDR, a->q_flags))
fba945cd 419 {
b6edea3d 420 message("250 Recipient ok");
fba945cd
EA
421 nrcpts++;
422 }
d344c0b7 423 else
6b861048 424 {
d344c0b7 425 /* punt -- should keep message in ADDRESS.... */
b6edea3d 426 message("550 Addressee unknown");
6b861048 427 }
a4076aed 428 e->e_to = NULL;
6b861048
EA
429 break;
430
431 case CMDDATA: /* data -- text of mail */
2e3062fe 432 SmtpPhase = "DATA";
3b87200d 433 if (!gotmail)
4a4ebe09 434 {
b6edea3d 435 message("503 Need MAIL command");
4a4ebe09
EA
436 break;
437 }
a4076aed 438 else if (e->e_nrcpts <= 0)
6b861048 439 {
b6edea3d 440 message("503 Need RCPT (recipient)");
4a4ebe09 441 break;
6b861048 442 }
4a4ebe09 443
959cf51d
EA
444 /* check to see if we need to re-expand aliases */
445 for (a = e->e_sendqueue; a != NULL; a = a->q_next)
446 {
447 if (bitset(QVERIFIED, a->q_flags))
448 break;
449 }
450
4a4ebe09 451 /* collect the text of the message */
2e3062fe 452 SmtpPhase = "collect";
959cf51d 453 collect(TRUE, a != NULL, e);
2f6a8a78 454 e->e_flags &= ~EF_FATALERRS;
4a4ebe09 455 if (Errors != 0)
fba945cd 456 goto abortmessage;
4a4ebe09 457
8eedb496
EA
458 /*
459 ** Arrange to send to everyone.
460 ** If sending to multiple people, mail back
461 ** errors rather than reporting directly.
462 ** In any case, don't mail back errors for
463 ** anything that has happened up to
464 ** now (the other end will do this).
bcf74f25
EA
465 ** Truncate our transcript -- the mail has gotten
466 ** to us successfully, and if we have
467 ** to mail this back, it will be easier
468 ** on the reader.
8eedb496
EA
469 ** Then send to everyone.
470 ** Finally give a reply code. If an error has
471 ** already been given, don't mail a
472 ** message back.
e6f08ab1 473 ** We goose error returns by clearing error bit.
8eedb496
EA
474 */
475
2e3062fe 476 SmtpPhase = "delivery";
fba945cd 477 if (nrcpts != 1 && a == NULL)
7338e3d4
EA
478 {
479 HoldErrs = TRUE;
8c8e8e94 480 e->e_errormode = EM_MAIL;
7338e3d4 481 }
a4076aed 482 e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
4aed8f3a 483 id = e->e_id;
4a4ebe09
EA
484
485 /* send to all recipients */
6366413a 486 sendall(e, Verbose ? SM_DELIVER : SM_QUEUE);
a4076aed 487 e->e_to = NULL;
4a4ebe09 488
666b39ad 489 /* save statistics */
a4076aed 490 markstats(e, (ADDRESS *) NULL);
666b39ad 491
6366413a
EA
492 unlockqueue(e);
493
8eedb496
EA
494 /* issue success if appropriate and reset */
495 if (Errors == 0 || HoldErrs)
c10e5f89 496 message("250 %s Message accepted for delivery", id);
fba945cd
EA
497
498 if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
2f6a8a78
EA
499 {
500 /* avoid sending back an extra message */
a4076aed 501 e->e_flags &= ~EF_FATALERRS;
fba945cd 502 e->e_flags |= EF_CLRQUEUE;
2f6a8a78
EA
503 }
504 else
a891bb7e 505 {
fba945cd
EA
506 /* from now on, we have to operate silently */
507 HoldErrs = TRUE;
508 e->e_errormode = EM_MAIL;
509
2f6a8a78
EA
510 /* if we just queued, poke it */
511 if (a != NULL && e->e_sendmode != SM_QUEUE)
512 {
513 unlockqueue(e);
514 dowork(id, TRUE, TRUE, e);
515 e->e_id = NULL;
516 }
a891bb7e
EA
517 }
518
6366413a
EA
519 /* now make it really happen */
520 if (!Verbose && e->e_sendmode != SM_QUEUE)
521 dowork(id, TRUE, e);
522
fba945cd 523 abortmessage:
e6f08ab1
EA
524 /* if in a child, pop back to our parent */
525 if (InChild)
526 finis();
2e3062fe
EA
527
528 /* clean up a bit */
3b87200d 529 gotmail = FALSE;
a4076aed 530 dropenvelope(e);
fda58daa 531 CurEnv = e = newenvelope(e, CurEnv);
a4076aed 532 e->e_flags = BlankEnvelope.e_flags;
6b861048
EA
533 break;
534
535 case CMDRSET: /* rset -- reset state */
b6edea3d 536 message("250 Reset state");
e6f08ab1
EA
537 if (InChild)
538 finis();
3b87200d
EA
539
540 /* clean up a bit */
541 gotmail = FALSE;
542 dropenvelope(e);
fda58daa 543 CurEnv = e = newenvelope(e, CurEnv);
e6f08ab1 544 break;
6b861048
EA
545
546 case CMDVRFY: /* vrfy -- verify address */
8f48def8
EA
547 case CMDEXPN: /* expn -- expand address */
548 vrfy = c->cmdcode == CMDVRFY;
549 if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
550 PrivacyFlags))
1c7897ef 551 {
efae59f3
EA
552 if (vrfy)
553 message("252 Who's to say?");
554 else
555 message("502 That's none of your business");
1c7897ef
EA
556 break;
557 }
558 else if (!gothello &&
8f48def8
EA
559 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
560 PrivacyFlags))
1c7897ef 561 {
b6edea3d 562 message("503 I demand that you introduce yourself first");
1c7897ef
EA
563 break;
564 }
8f48def8 565 if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
e6f08ab1 566 break;
01e8020e 567#ifdef LOG
68f7099c 568 if (LogLevel > 5)
01e8020e
EA
569 syslog(LOG_INFO, "%s: %s", CurHostName, inp);
570#endif
abae7b2d 571 paddrtree(a);
6b861048
EA
572 break;
573
574 case CMDHELP: /* help -- give user info */
34d37b7d 575 help(p);
6b861048
EA
576 break;
577
578 case CMDNOOP: /* noop -- do nothing */
b6edea3d 579 message("200 OK");
6b861048
EA
580 break;
581
582 case CMDQUIT: /* quit -- leave mail */
b6edea3d 583 message("221 %s closing connection", MyHostName);
e6f08ab1
EA
584 if (InChild)
585 ExitStat = EX_QUIT;
6b861048
EA
586 finis();
587
e8ad767d
EA
588 case CMDVERB: /* set verbose mode */
589 Verbose = TRUE;
8c8e8e94 590 e->e_sendmode = SM_DELIVER;
b6edea3d 591 message("200 Verbose mode");
e8ad767d
EA
592 break;
593
8fe4fb9b 594 case CMDONEX: /* doing one transaction only */
7338e3d4 595 OneXact = TRUE;
b6edea3d 596 message("200 Only one transaction");
8fe4fb9b
EA
597 break;
598
ab4889ea 599# ifdef SMTPDEBUG
e6f08ab1 600 case CMDDBGQSHOW: /* show queues */
2654b031 601 printf("Send Queue=");
a4076aed 602 printaddr(e->e_sendqueue, TRUE);
d4f42161 603 break;
09eb49d8
EA
604
605 case CMDDBGDEBUG: /* set debug mode */
9678c96d
EA
606 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
607 tTflag(p);
b6edea3d 608 message("200 Debug set");
09eb49d8
EA
609 break;
610
ab4889ea
MK
611# else /* not SMTPDEBUG */
612
613 case CMDDBGQSHOW: /* show queues */
614 case CMDDBGDEBUG: /* set debug mode */
2e15a2d8 615# ifdef LOG
5973222c 616 if (LogLevel > 0)
ab4889ea 617 syslog(LOG_NOTICE,
68f7099c 618 "\"%s\" command from %s (%s)",
ab4889ea 619 c->cmdname, RealHostName,
3341995c 620 anynet_ntoa(&RealHostAddr));
2e15a2d8 621# endif
ab4889ea
MK
622 /* FALL THROUGH */
623# endif /* SMTPDEBUG */
d4f42161 624
6b861048 625 case CMDERROR: /* unknown command */
b6edea3d 626 message("500 Command unrecognized");
6b861048
EA
627 break;
628
629 default:
ab4889ea 630 errno = 0;
b6edea3d 631 syserr("500 smtp: unknown code %d", c->cmdcode);
6b861048
EA
632 break;
633 }
634 }
635}
636\f/*
637** SKIPWORD -- skip a fixed word.
638**
639** Parameters:
640** p -- place to start looking.
641** w -- word to skip.
642**
643** Returns:
644** p following w.
645** NULL on error.
646**
647** Side Effects:
648** clobbers the p data area.
649*/
650
651static char *
652skipword(p, w)
653 register char *p;
654 char *w;
655{
656 register char *q;
6b861048
EA
657
658 /* find beginning of word */
2bee003d 659 while (isascii(*p) && isspace(*p))
6b861048
EA
660 p++;
661 q = p;
662
663 /* find end of word */
2bee003d 664 while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6b861048 665 p++;
2bee003d 666 while (isascii(*p) && isspace(*p))
6b861048
EA
667 *p++ = '\0';
668 if (*p != ':')
669 {
670 syntax:
b6edea3d 671 message("501 Syntax error");
6b861048
EA
672 Errors++;
673 return (NULL);
674 }
675 *p++ = '\0';
2bee003d 676 while (isascii(*p) && isspace(*p))
6b861048
EA
677 p++;
678
679 /* see if the input word matches desired word */
ed73ef1d 680 if (strcasecmp(q, w))
6b861048
EA
681 goto syntax;
682
683 return (p);
684}
34d37b7d 685\f/*
b6edea3d
EA
686** PRINTVRFYADDR -- print an entry in the verify queue
687**
688** Parameters:
689** a -- the address to print
690** last -- set if this is the last one.
691**
692** Returns:
693** none.
694**
695** Side Effects:
696** Prints the appropriate 250 codes.
697*/
698
699printvrfyaddr(a, last)
700 register ADDRESS *a;
701 bool last;
702{
703 char fmtbuf[20];
704
705 strcpy(fmtbuf, "250");
706 fmtbuf[3] = last ? ' ' : '-';
707
ac820be5
EA
708 if (a->q_fullname == NULL)
709 {
710 if (strchr(a->q_user, '@') == NULL)
711 strcpy(&fmtbuf[4], "<%s@%s>");
712 else
713 strcpy(&fmtbuf[4], "<%s>");
714 message(fmtbuf, a->q_user, MyHostName);
715 }
b6edea3d
EA
716 else
717 {
ac820be5
EA
718 if (strchr(a->q_user, '@') == NULL)
719 strcpy(&fmtbuf[4], "%s <%s@%s>");
720 else
721 strcpy(&fmtbuf[4], "%s <%s>");
722 message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
b6edea3d 723 }
b6edea3d
EA
724}
725\f/*
34d37b7d
EA
726** HELP -- implement the HELP command.
727**
728** Parameters:
729** topic -- the topic we want help for.
730**
731** Returns:
732** none.
733**
734** Side Effects:
735** outputs the help file to message output.
736*/
737
738help(topic)
739 char *topic;
740{
741 register FILE *hf;
742 int len;
743 char buf[MAXLINE];
744 bool noinfo;
745
c1e24818 746 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
34d37b7d
EA
747 {
748 /* no help */
1b932f66 749 errno = 0;
b6edea3d 750 message("502 HELP not implemented");
34d37b7d
EA
751 return;
752 }
753
e97afd4a
EA
754 if (topic == NULL || *topic == '\0')
755 topic = "smtp";
756 else
757 makelower(topic);
758
34d37b7d 759 len = strlen(topic);
34d37b7d
EA
760 noinfo = TRUE;
761
762 while (fgets(buf, sizeof buf, hf) != NULL)
763 {
764 if (strncmp(buf, topic, len) == 0)
765 {
766 register char *p;
767
f3d8f6d6 768 p = strchr(buf, '\t');
34d37b7d
EA
769 if (p == NULL)
770 p = buf;
771 else
772 p++;
773 fixcrlf(p, TRUE);
b6edea3d 774 message("214-%s", p);
34d37b7d
EA
775 noinfo = FALSE;
776 }
777 }
778
779 if (noinfo)
b6edea3d 780 message("504 HELP topic unknown");
34d37b7d 781 else
b6edea3d 782 message("214 End of HELP info");
ed45aae1 783 (void) fclose(hf);
34d37b7d 784}
abae7b2d 785\f/*
e6f08ab1
EA
786** RUNINCHILD -- return twice -- once in the child, then in the parent again
787**
788** Parameters:
789** label -- a string used in error messages
790**
791** Returns:
792** zero in the child
793** one in the parent
794**
795** Side Effects:
796** none.
797*/
798
a4076aed 799runinchild(label, e)
e6f08ab1 800 char *label;
a4076aed 801 register ENVELOPE *e;
e6f08ab1
EA
802{
803 int childpid;
804
c1a66acf 805 if (!OneXact)
e6f08ab1 806 {
c1a66acf
EA
807 childpid = dofork();
808 if (childpid < 0)
809 {
810 syserr("%s: cannot fork", label);
811 return (1);
812 }
813 if (childpid > 0)
814 {
815 auto int st;
e6f08ab1 816
c1a66acf 817 /* parent -- wait for child to complete */
ade7da2a 818 setproctitle("srvrsmtp %s child wait", CurHostName);
c1a66acf
EA
819 st = waitfor(childpid);
820 if (st == -1)
821 syserr("%s: lost child", label);
e6f08ab1 822
c1a66acf
EA
823 /* if we exited on a QUIT command, complete the process */
824 if (st == (EX_QUIT << 8))
825 finis();
e6f08ab1 826
c1a66acf
EA
827 return (1);
828 }
829 else
830 {
831 /* child */
832 InChild = TRUE;
57c97d4a 833 QuickAbort = FALSE;
a4076aed 834 clearenvelope(e, FALSE);
c1a66acf 835 }
e6f08ab1 836 }
55f0da62 837
c1a66acf 838 /* open alias database */
595aeb43 839 initaliases(FALSE, e);
c1a66acf
EA
840
841 return (0);
e6f08ab1
EA
842}
843\f/*
abae7b2d
EA
844** PADDRTREE -- print address tree
845**
846** Used by VRFY and EXPD to dump the tree of addresses produced.
847**
848** Parameters:
849** a -- address of root.
850**
851** Returns:
852** none.
853**
854** Side Effects:
855** prints the tree in a nice order.
856*/
857
858paddrtree(a)
859 register ADDRESS *a;
860{
861 static ADDRESS *prev;
862 static int lev;
863
864 if (a == NULL)
865 return;
866 lev++;
867 if (!bitset(QDONTSEND, a->q_flags))
868 {
869 if (prev != NULL)
870 {
871 if (prev->q_fullname != NULL)
872 message("250-", "%s <%s>", prev->q_fullname, prev->q_paddr);
873 else
874 message("250-", "<%s>", prev->q_paddr);
875 }
876 prev = a;
877 }
878 paddrtree(a->q_child);
879 paddrtree(a->q_sibling);
880 if (--lev <= 0)
881 {
882 if (prev != NULL)
883 {
884 /* last one */
885 if (prev->q_fullname != NULL)
886 message("250", "%s <%s>", prev->q_fullname, prev->q_paddr);
887 else
888 message("250", "<%s>", prev->q_paddr);
889 prev = NULL;
890 }
891 else
892 message("550", "User unknown");
893 }
894}
884a20cb 895
f3d8f6d6 896# endif /* SMTP */