This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / usr.sbin / sendmail / src / srvrsmtp.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1983 Eric P. Allman
78ed81a3 3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
15637ed4
RG
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35# include "sendmail.h"
36
37#ifndef lint
38#ifdef SMTP
78ed81a3 39static char sccsid[] = "@(#)srvrsmtp.c 8.3 (Berkeley) 7/13/93 (with SMTP)";
15637ed4 40#else
78ed81a3 41static char sccsid[] = "@(#)srvrsmtp.c 8.3 (Berkeley) 7/13/93 (without SMTP)";
15637ed4
RG
42#endif
43#endif /* not lint */
44
45# include <errno.h>
46# include <signal.h>
47
48# ifdef SMTP
49
50/*
51** SMTP -- run the SMTP protocol.
52**
53** Parameters:
54** none.
55**
56** Returns:
57** never.
58**
59** Side Effects:
60** Reads commands from the input channel and processes
61** them.
62*/
63
64struct cmd
65{
66 char *cmdname; /* command name */
67 int cmdcode; /* internal code, see below */
68};
69
70/* values for cmdcode */
71# define CMDERROR 0 /* bad command */
72# define CMDMAIL 1 /* mail -- designate sender */
73# define CMDRCPT 2 /* rcpt -- designate recipient */
74# define CMDDATA 3 /* data -- send message text */
75# define CMDRSET 4 /* rset -- reset state */
76# define CMDVRFY 5 /* vrfy -- verify address */
78ed81a3 77# define CMDEXPN 6 /* expn -- expand address */
15637ed4
RG
78# define CMDNOOP 7 /* noop -- do nothing */
79# define CMDQUIT 8 /* quit -- close connection and die */
80# define CMDHELO 9 /* helo -- be polite */
78ed81a3 81# define CMDHELP 10 /* help -- give usage info */
82# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
83/* non-standard commands */
84# define CMDONEX 16 /* onex -- sending one transaction only */
85# define CMDVERB 17 /* verb -- go into verbose mode */
15637ed4 86/* debugging-only commands, only enabled if SMTPDEBUG is defined */
78ed81a3 87# define CMDDBGQSHOW 24 /* showq -- show send queue */
88# define CMDDBGDEBUG 25 /* debug -- set debug mode */
15637ed4
RG
89
90static struct cmd CmdTab[] =
91{
92 "mail", CMDMAIL,
93 "rcpt", CMDRCPT,
94 "data", CMDDATA,
95 "rset", CMDRSET,
96 "vrfy", CMDVRFY,
78ed81a3 97 "expn", CMDEXPN,
15637ed4
RG
98 "help", CMDHELP,
99 "noop", CMDNOOP,
100 "quit", CMDQUIT,
101 "helo", CMDHELO,
78ed81a3 102 "ehlo", CMDEHLO,
15637ed4
RG
103 "verb", CMDVERB,
104 "onex", CMDONEX,
105 /*
106 * remaining commands are here only
107 * to trap and log attempts to use them
108 */
109 "showq", CMDDBGQSHOW,
110 "debug", CMDDBGDEBUG,
111 NULL, CMDERROR,
112};
113
114bool InChild = FALSE; /* true if running in a subprocess */
115bool OneXact = FALSE; /* one xaction only this run */
116
117#define EX_QUIT 22 /* special code for QUIT command */
118
78ed81a3 119smtp(e)
120 register ENVELOPE *e;
15637ed4
RG
121{
122 register char *p;
123 register struct cmd *c;
124 char *cmd;
125 static char *skipword();
15637ed4
RG
126 auto ADDRESS *vrfyqueue;
127 ADDRESS *a;
78ed81a3 128 bool gotmail; /* mail command received */
129 bool gothello; /* helo command received */
130 bool vrfy; /* set if this is a vrfy command */
131 char *protocol; /* sending protocol */
132 char *sendinghost; /* sending hostname */
133 long msize; /* approximate maximum message size */
134 auto char *delimptr;
135 char *id;
136 int nrcpts; /* number of RCPT commands */
137 bool doublequeue;
15637ed4 138 char inp[MAXLINE];
78ed81a3 139 char cmdbuf[MAXLINE];
15637ed4 140 extern char Version[];
15637ed4 141 extern ENVELOPE BlankEnvelope;
15637ed4 142
78ed81a3 143 if (fileno(OutChannel) != fileno(stdout))
15637ed4
RG
144 {
145 /* arrange for debugging output to go to remote host */
78ed81a3 146 (void) dup2(fileno(OutChannel), fileno(stdout));
15637ed4 147 }
78ed81a3 148 settime(e);
149 CurHostName = RealHostName;
150 setproctitle("server %s startup", CurHostName);
151 expand("\201e", inp, &inp[sizeof inp], e);
152 message("220-%s", inp);
153 message("220 ESMTP spoken here");
154 protocol = NULL;
155 sendinghost = macvalue('s', e);
156 gothello = FALSE;
157 gotmail = FALSE;
15637ed4
RG
158 for (;;)
159 {
160 /* arrange for backout */
161 if (setjmp(TopFrame) > 0 && InChild)
78ed81a3 162 {
163 QuickAbort = FALSE;
164 SuprErrs = TRUE;
15637ed4 165 finis();
78ed81a3 166 }
15637ed4
RG
167 QuickAbort = FALSE;
168 HoldErrs = FALSE;
78ed81a3 169 LogUsrErrs = FALSE;
170 e->e_flags &= ~EF_VRFYONLY;
15637ed4
RG
171
172 /* setup for the read */
78ed81a3 173 e->e_to = NULL;
15637ed4
RG
174 Errors = 0;
175 (void) fflush(stdout);
176
177 /* read the input line */
78ed81a3 178 SmtpPhase = "server cmd read";
179 setproctitle("server %s cmd read", CurHostName);
180 p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
181 SmtpPhase);
15637ed4
RG
182
183 /* handle errors */
184 if (p == NULL)
185 {
186 /* end of file, just die */
78ed81a3 187 message("421 %s Lost input channel from %s",
15637ed4 188 MyHostName, CurHostName);
78ed81a3 189#ifdef LOG
190 if (LogLevel > 1)
191 syslog(LOG_NOTICE, "lost input channel from %s",
192 CurHostName);
193#endif
194 if (InChild)
195 ExitStat = EX_QUIT;
15637ed4
RG
196 finis();
197 }
198
199 /* clean up end of line */
200 fixcrlf(inp, TRUE);
201
202 /* echo command to transcript */
78ed81a3 203 if (e->e_xfp != NULL)
204 fprintf(e->e_xfp, "<<< %s\n", inp);
205
206 if (e->e_id == NULL)
207 setproctitle("%s: %s", CurHostName, inp);
208 else
209 setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
15637ed4
RG
210
211 /* break off command */
78ed81a3 212 for (p = inp; isascii(*p) && isspace(*p); p++)
15637ed4 213 continue;
78ed81a3 214 cmd = cmdbuf;
215 while (*p != '\0' &&
216 !(isascii(*p) && isspace(*p)) &&
217 cmd < &cmdbuf[sizeof cmdbuf - 2])
15637ed4
RG
218 *cmd++ = *p++;
219 *cmd = '\0';
220
221 /* throw away leading whitespace */
78ed81a3 222 while (isascii(*p) && isspace(*p))
15637ed4
RG
223 p++;
224
225 /* decode command */
226 for (c = CmdTab; c->cmdname != NULL; c++)
227 {
228 if (!strcasecmp(c->cmdname, cmdbuf))
229 break;
230 }
231
78ed81a3 232 /* reset errors */
233 errno = 0;
234
15637ed4
RG
235 /* process command */
236 switch (c->cmdcode)
237 {
238 case CMDHELO: /* hello -- introduce yourself */
78ed81a3 239 case CMDEHLO: /* extended hello */
240 if (c->cmdcode == CMDEHLO)
15637ed4 241 {
78ed81a3 242 protocol = "ESMTP";
243 SmtpPhase = "server EHLO";
15637ed4 244 }
78ed81a3 245 else
246 {
247 protocol = "SMTP";
248 SmtpPhase = "server HELO";
249 }
250 sendinghost = newstr(p);
251 if (strcasecmp(p, RealHostName) != 0)
15637ed4 252 {
78ed81a3 253 auth_warning(e, "Host %s claimed to be %s",
254 RealHostName, p);
255 }
256 p = macvalue('_', e);
257 if (p == NULL)
258 p = RealHostName;
15637ed4 259
78ed81a3 260 gothello = TRUE;
261 if (c->cmdcode != CMDEHLO)
262 {
263 /* print old message and be done with it */
264 message("250 %s Hello %s, pleased to meet you",
265 MyHostName, p);
266 break;
15637ed4 267 }
78ed81a3 268
269 /* print extended message and brag */
270 message("250-%s Hello %s, pleased to meet you",
271 MyHostName, p);
272 if (!bitset(PRIV_NOEXPN, PrivacyFlags))
273 message("250-EXPN");
274 if (MaxMessageSize > 0)
275 message("250-SIZE %ld", MaxMessageSize);
15637ed4 276 else
78ed81a3 277 message("250-SIZE");
278 message("250 HELP");
15637ed4
RG
279 break;
280
281 case CMDMAIL: /* mail -- designate sender */
78ed81a3 282 SmtpPhase = "server MAIL";
15637ed4
RG
283
284 /* check for validity of this command */
78ed81a3 285 if (!gothello)
15637ed4 286 {
78ed81a3 287 /* set sending host to our known value */
288 if (sendinghost == NULL)
289 sendinghost = RealHostName;
290
291 if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
292 {
293 message("503 Polite people say HELO first");
294 break;
295 }
296 }
297 if (gotmail)
298 {
299 message("503 Sender already specified");
15637ed4
RG
300 break;
301 }
302 if (InChild)
303 {
304 errno = 0;
78ed81a3 305 syserr("503 Nested MAIL command: MAIL %s", p);
306 finis();
15637ed4
RG
307 }
308
309 /* fork a subprocess to process this command */
78ed81a3 310 if (runinchild("SMTP-MAIL", e) > 0)
15637ed4 311 break;
78ed81a3 312 if (!gothello)
313 {
314 auth_warning(e,
315 "Host %s didn't use HELO protocol",
316 RealHostName);
317 }
318 if (protocol == NULL)
319 protocol = "SMTP";
320 define('r', protocol, e);
321 define('s', sendinghost, e);
322 initsys(e);
323 nrcpts = 0;
324 setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
15637ed4
RG
325
326 /* child -- go do the processing */
327 p = skipword(p, "from");
328 if (p == NULL)
329 break;
78ed81a3 330 if (setjmp(TopFrame) > 0)
15637ed4 331 {
78ed81a3 332 /* this failed -- undo work */
333 if (InChild)
334 {
335 QuickAbort = FALSE;
336 SuprErrs = TRUE;
337 e->e_flags &= ~EF_FATALERRS;
338 finis();
339 }
340 break;
15637ed4 341 }
78ed81a3 342 QuickAbort = TRUE;
343
344 /* must parse sender first */
345 delimptr = NULL;
346 setsender(p, e, &delimptr, FALSE);
347 p = delimptr;
348 if (p != NULL && *p != '\0')
349 *p++ = '\0';
350
351 /* now parse ESMTP arguments */
352 msize = 0;
353 for (; p != NULL && *p != '\0'; p++)
354 {
355 char *kp;
356 char *vp;
357
358 /* locate the beginning of the keyword */
359 while (isascii(*p) && isspace(*p))
360 p++;
361 if (*p == '\0')
362 break;
363 kp = p;
364
365 /* skip to the value portion */
366 while (isascii(*p) && isalnum(*p) || *p == '-')
367 p++;
368 if (*p == '=')
369 {
370 *p++ = '\0';
371 vp = p;
372
373 /* skip to the end of the value */
374 while (*p != '\0' && *p != ' ' &&
375 !(isascii(*p) && iscntrl(*p)) &&
376 *p != '=')
377 p++;
378 }
379
380 if (*p != '\0')
381 *p++ = '\0';
382
383 if (tTd(19, 1))
384 printf("MAIL: got arg %s=%s\n", kp,
385 vp == NULL ? "<null>" : vp);
386
387 if (strcasecmp(kp, "size") == 0)
388 {
389 if (vp == NULL)
390 {
391 usrerr("501 SIZE requires a value");
392 /* NOTREACHED */
393 }
394 msize = atol(vp);
395 }
396 else if (strcasecmp(kp, "body") == 0)
397 {
398 if (vp == NULL)
399 {
400 usrerr("501 BODY requires a value");
401 /* NOTREACHED */
402 }
403# ifdef MIME
404 if (strcasecmp(vp, "8bitmime") == 0)
405 {
406 e->e_bodytype = "8BITMIME";
407 SevenBit = FALSE;
408 }
409 else if (strcasecmp(vp, "7bit") == 0)
410 {
411 e->e_bodytype = "7BIT";
412 SevenBit = TRUE;
413 }
414 else
415 {
416 usrerr("501 Unknown BODY type %s",
417 vp);
418 }
419# endif
420 }
421 else
422 {
423 usrerr("501 %s parameter unrecognized", kp);
424 /* NOTREACHED */
425 }
426 }
427
428 if (MaxMessageSize > 0 && msize > MaxMessageSize)
429 {
430 usrerr("552 Message size exceeds fixed maximum message size (%ld)",
431 MaxMessageSize);
432 /* NOTREACHED */
433 }
434
435 if (!enoughspace(msize))
436 {
437 message("452 Insufficient disk space; try again later");
438 break;
439 }
440 message("250 Sender ok");
441 gotmail = TRUE;
15637ed4
RG
442 break;
443
444 case CMDRCPT: /* rcpt -- designate recipient */
78ed81a3 445 if (!gotmail)
446 {
447 usrerr("503 Need MAIL before RCPT");
448 break;
449 }
450 SmtpPhase = "server RCPT";
15637ed4
RG
451 if (setjmp(TopFrame) > 0)
452 {
78ed81a3 453 e->e_flags &= ~EF_FATALERRS;
15637ed4
RG
454 break;
455 }
456 QuickAbort = TRUE;
78ed81a3 457 LogUsrErrs = TRUE;
458
459 if (e->e_sendmode != SM_DELIVER)
460 e->e_flags |= EF_VRFYONLY;
461
15637ed4
RG
462 p = skipword(p, "to");
463 if (p == NULL)
464 break;
78ed81a3 465 a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
15637ed4
RG
466 if (a == NULL)
467 break;
468 a->q_flags |= QPRIMARY;
78ed81a3 469 a = recipient(a, &e->e_sendqueue, e);
15637ed4
RG
470 if (Errors != 0)
471 break;
472
473 /* no errors during parsing, but might be a duplicate */
78ed81a3 474 e->e_to = p;
15637ed4 475 if (!bitset(QBADADDR, a->q_flags))
78ed81a3 476 {
477 message("250 Recipient ok");
478 nrcpts++;
479 }
15637ed4
RG
480 else
481 {
482 /* punt -- should keep message in ADDRESS.... */
78ed81a3 483 message("550 Addressee unknown");
15637ed4 484 }
78ed81a3 485 e->e_to = NULL;
15637ed4
RG
486 break;
487
488 case CMDDATA: /* data -- text of mail */
78ed81a3 489 SmtpPhase = "server DATA";
490 if (!gotmail)
15637ed4 491 {
78ed81a3 492 message("503 Need MAIL command");
15637ed4
RG
493 break;
494 }
78ed81a3 495 else if (e->e_nrcpts <= 0)
15637ed4 496 {
78ed81a3 497 message("503 Need RCPT (recipient)");
15637ed4
RG
498 break;
499 }
500
78ed81a3 501 /* check to see if we need to re-expand aliases */
502 /* also reset QBADADDR on already-diagnosted addrs */
503 doublequeue = FALSE;
504 for (a = e->e_sendqueue; a != NULL; a = a->q_next)
505 {
506 if (bitset(QVERIFIED, a->q_flags))
507 {
508 /* need to re-expand aliases */
509 doublequeue = TRUE;
510 }
511 if (bitset(QBADADDR, a->q_flags))
512 {
513 /* make this "go away" */
514 a->q_flags |= QDONTSEND;
515 a->q_flags &= ~QBADADDR;
516 }
517 }
518
15637ed4
RG
519 /* collect the text of the message */
520 SmtpPhase = "collect";
78ed81a3 521 collect(TRUE, doublequeue, e);
522 e->e_flags &= ~EF_FATALERRS;
15637ed4 523 if (Errors != 0)
78ed81a3 524 goto abortmessage;
15637ed4
RG
525
526 /*
527 ** Arrange to send to everyone.
528 ** If sending to multiple people, mail back
529 ** errors rather than reporting directly.
530 ** In any case, don't mail back errors for
531 ** anything that has happened up to
532 ** now (the other end will do this).
533 ** Truncate our transcript -- the mail has gotten
534 ** to us successfully, and if we have
535 ** to mail this back, it will be easier
536 ** on the reader.
537 ** Then send to everyone.
538 ** Finally give a reply code. If an error has
539 ** already been given, don't mail a
540 ** message back.
541 ** We goose error returns by clearing error bit.
542 */
543
544 SmtpPhase = "delivery";
78ed81a3 545 if (nrcpts != 1 && !doublequeue)
15637ed4
RG
546 {
547 HoldErrs = TRUE;
78ed81a3 548 e->e_errormode = EM_MAIL;
15637ed4 549 }
78ed81a3 550 e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
551 id = e->e_id;
15637ed4
RG
552
553 /* send to all recipients */
78ed81a3 554 sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
555 e->e_to = NULL;
15637ed4
RG
556
557 /* save statistics */
78ed81a3 558 markstats(e, (ADDRESS *) NULL);
15637ed4
RG
559
560 /* issue success if appropriate and reset */
561 if (Errors == 0 || HoldErrs)
78ed81a3 562 message("250 %s Message accepted for delivery", id);
563
564 if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
565 {
566 /* avoid sending back an extra message */
567 e->e_flags &= ~EF_FATALERRS;
568 e->e_flags |= EF_CLRQUEUE;
569 }
15637ed4 570 else
78ed81a3 571 {
572 /* from now on, we have to operate silently */
573 HoldErrs = TRUE;
574 e->e_errormode = EM_MAIL;
15637ed4 575
78ed81a3 576 /* if we just queued, poke it */
577 if (doublequeue && e->e_sendmode != SM_QUEUE)
578 {
579 unlockqueue(e);
580 dowork(id, TRUE, TRUE, e);
581 e->e_id = NULL;
582 }
583 }
584
585 abortmessage:
15637ed4
RG
586 /* if in a child, pop back to our parent */
587 if (InChild)
588 finis();
589
590 /* clean up a bit */
78ed81a3 591 gotmail = FALSE;
592 dropenvelope(e);
593 CurEnv = e = newenvelope(e, CurEnv);
594 e->e_flags = BlankEnvelope.e_flags;
15637ed4
RG
595 break;
596
597 case CMDRSET: /* rset -- reset state */
78ed81a3 598 message("250 Reset state");
15637ed4
RG
599 if (InChild)
600 finis();
78ed81a3 601
602 /* clean up a bit */
603 gotmail = FALSE;
604 dropenvelope(e);
605 CurEnv = e = newenvelope(e, CurEnv);
15637ed4
RG
606 break;
607
608 case CMDVRFY: /* vrfy -- verify address */
78ed81a3 609 case CMDEXPN: /* expn -- expand address */
610 vrfy = c->cmdcode == CMDVRFY;
611 if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
612 PrivacyFlags))
613 {
614 if (vrfy)
615 message("252 Who's to say?");
616 else
617 message("502 That's none of your business");
15637ed4 618 break;
78ed81a3 619 }
620 else if (!gothello &&
621 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
622 PrivacyFlags))
623 {
624 message("503 I demand that you introduce yourself first");
625 break;
626 }
627 if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
628 break;
629#ifdef LOG
630 if (LogLevel > 5)
631 syslog(LOG_INFO, "%s: %s", CurHostName, inp);
632#endif
15637ed4
RG
633 vrfyqueue = NULL;
634 QuickAbort = TRUE;
78ed81a3 635 if (vrfy)
636 e->e_flags |= EF_VRFYONLY;
637 while (*p != '\0' && isascii(*p) && isspace(*p))
638 *p++;
639 if (*p == '\0')
640 {
641 message("501 Argument required");
642 Errors++;
643 }
644 else
645 {
646 (void) sendtolist(p, (ADDRESS *) NULL,
647 &vrfyqueue, e);
648 }
15637ed4
RG
649 if (Errors != 0)
650 {
651 if (InChild)
652 finis();
653 break;
654 }
78ed81a3 655 if (vrfyqueue == NULL)
656 {
657 message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
658 }
15637ed4
RG
659 while (vrfyqueue != NULL)
660 {
661 register ADDRESS *a = vrfyqueue->q_next;
15637ed4
RG
662
663 while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
664 a = a->q_next;
665
666 if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
78ed81a3 667 printvrfyaddr(vrfyqueue, a == NULL);
15637ed4 668 else if (a == NULL)
78ed81a3 669 message("554 Self destructive alias loop");
15637ed4
RG
670 vrfyqueue = a;
671 }
672 if (InChild)
673 finis();
674 break;
675
676 case CMDHELP: /* help -- give user info */
677 help(p);
678 break;
679
680 case CMDNOOP: /* noop -- do nothing */
78ed81a3 681 message("200 OK");
15637ed4
RG
682 break;
683
684 case CMDQUIT: /* quit -- leave mail */
78ed81a3 685 message("221 %s closing connection", MyHostName);
686
687 /* avoid future 050 messages */
688 Verbose = FALSE;
689
15637ed4
RG
690 if (InChild)
691 ExitStat = EX_QUIT;
692 finis();
693
694 case CMDVERB: /* set verbose mode */
78ed81a3 695 if (bitset(PRIV_NOEXPN, PrivacyFlags))
696 {
697 /* this would give out the same info */
698 message("502 Verbose unavailable");
699 break;
700 }
15637ed4 701 Verbose = TRUE;
78ed81a3 702 e->e_sendmode = SM_DELIVER;
703 message("250 Verbose mode");
15637ed4
RG
704 break;
705
706 case CMDONEX: /* doing one transaction only */
707 OneXact = TRUE;
78ed81a3 708 message("250 Only one transaction");
15637ed4
RG
709 break;
710
711# ifdef SMTPDEBUG
712 case CMDDBGQSHOW: /* show queues */
713 printf("Send Queue=");
78ed81a3 714 printaddr(e->e_sendqueue, TRUE);
15637ed4
RG
715 break;
716
717 case CMDDBGDEBUG: /* set debug mode */
718 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
719 tTflag(p);
78ed81a3 720 message("200 Debug set");
15637ed4
RG
721 break;
722
723# else /* not SMTPDEBUG */
724
725 case CMDDBGQSHOW: /* show queues */
726 case CMDDBGDEBUG: /* set debug mode */
727# ifdef LOG
78ed81a3 728 if (LogLevel > 0)
15637ed4 729 syslog(LOG_NOTICE,
78ed81a3 730 "\"%s\" command from %s (%s)",
15637ed4 731 c->cmdname, RealHostName,
78ed81a3 732 anynet_ntoa(&RealHostAddr));
15637ed4
RG
733# endif
734 /* FALL THROUGH */
735# endif /* SMTPDEBUG */
736
737 case CMDERROR: /* unknown command */
78ed81a3 738 message("500 Command unrecognized");
15637ed4
RG
739 break;
740
741 default:
742 errno = 0;
78ed81a3 743 syserr("500 smtp: unknown code %d", c->cmdcode);
15637ed4
RG
744 break;
745 }
746 }
747}
748\f/*
749** SKIPWORD -- skip a fixed word.
750**
751** Parameters:
752** p -- place to start looking.
753** w -- word to skip.
754**
755** Returns:
756** p following w.
757** NULL on error.
758**
759** Side Effects:
760** clobbers the p data area.
761*/
762
763static char *
764skipword(p, w)
765 register char *p;
766 char *w;
767{
768 register char *q;
769
770 /* find beginning of word */
78ed81a3 771 while (isascii(*p) && isspace(*p))
15637ed4
RG
772 p++;
773 q = p;
774
775 /* find end of word */
78ed81a3 776 while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
15637ed4 777 p++;
78ed81a3 778 while (isascii(*p) && isspace(*p))
15637ed4
RG
779 *p++ = '\0';
780 if (*p != ':')
781 {
782 syntax:
78ed81a3 783 message("501 Syntax error in parameters");
15637ed4
RG
784 Errors++;
785 return (NULL);
786 }
787 *p++ = '\0';
78ed81a3 788 while (isascii(*p) && isspace(*p))
15637ed4
RG
789 p++;
790
78ed81a3 791 if (*p == '\0')
792 goto syntax;
793
15637ed4
RG
794 /* see if the input word matches desired word */
795 if (strcasecmp(q, w))
796 goto syntax;
797
798 return (p);
799}
800\f/*
78ed81a3 801** PRINTVRFYADDR -- print an entry in the verify queue
802**
803** Parameters:
804** a -- the address to print
805** last -- set if this is the last one.
806**
807** Returns:
808** none.
809**
810** Side Effects:
811** Prints the appropriate 250 codes.
812*/
813
814printvrfyaddr(a, last)
815 register ADDRESS *a;
816 bool last;
817{
818 char fmtbuf[20];
819
820 strcpy(fmtbuf, "250");
821 fmtbuf[3] = last ? ' ' : '-';
822
823 if (a->q_fullname == NULL)
824 {
825 if (strchr(a->q_user, '@') == NULL)
826 strcpy(&fmtbuf[4], "<%s@%s>");
827 else
828 strcpy(&fmtbuf[4], "<%s>");
829 message(fmtbuf, a->q_user, MyHostName);
830 }
831 else
832 {
833 if (strchr(a->q_user, '@') == NULL)
834 strcpy(&fmtbuf[4], "%s <%s@%s>");
835 else
836 strcpy(&fmtbuf[4], "%s <%s>");
837 message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
838 }
839}
840\f/*
15637ed4
RG
841** HELP -- implement the HELP command.
842**
843** Parameters:
844** topic -- the topic we want help for.
845**
846** Returns:
847** none.
848**
849** Side Effects:
850** outputs the help file to message output.
851*/
852
853help(topic)
854 char *topic;
855{
856 register FILE *hf;
857 int len;
858 char buf[MAXLINE];
859 bool noinfo;
860
861 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
862 {
863 /* no help */
864 errno = 0;
78ed81a3 865 message("502 HELP not implemented");
15637ed4
RG
866 return;
867 }
868
869 if (topic == NULL || *topic == '\0')
870 topic = "smtp";
871 else
872 makelower(topic);
873
874 len = strlen(topic);
875 noinfo = TRUE;
876
877 while (fgets(buf, sizeof buf, hf) != NULL)
878 {
879 if (strncmp(buf, topic, len) == 0)
880 {
881 register char *p;
882
78ed81a3 883 p = strchr(buf, '\t');
15637ed4
RG
884 if (p == NULL)
885 p = buf;
886 else
887 p++;
888 fixcrlf(p, TRUE);
78ed81a3 889 message("214-%s", p);
15637ed4
RG
890 noinfo = FALSE;
891 }
892 }
893
894 if (noinfo)
78ed81a3 895 message("504 HELP topic unknown");
15637ed4 896 else
78ed81a3 897 message("214 End of HELP info");
15637ed4
RG
898 (void) fclose(hf);
899}
900\f/*
901** RUNINCHILD -- return twice -- once in the child, then in the parent again
902**
903** Parameters:
904** label -- a string used in error messages
905**
906** Returns:
907** zero in the child
908** one in the parent
909**
910** Side Effects:
911** none.
912*/
913
78ed81a3 914runinchild(label, e)
15637ed4 915 char *label;
78ed81a3 916 register ENVELOPE *e;
15637ed4
RG
917{
918 int childpid;
919
920 if (!OneXact)
921 {
922 childpid = dofork();
923 if (childpid < 0)
924 {
925 syserr("%s: cannot fork", label);
926 return (1);
927 }
928 if (childpid > 0)
929 {
930 auto int st;
931
932 /* parent -- wait for child to complete */
78ed81a3 933 setproctitle("server %s child wait", CurHostName);
15637ed4
RG
934 st = waitfor(childpid);
935 if (st == -1)
936 syserr("%s: lost child", label);
937
938 /* if we exited on a QUIT command, complete the process */
939 if (st == (EX_QUIT << 8))
940 finis();
941
942 return (1);
943 }
944 else
945 {
946 /* child */
947 InChild = TRUE;
948 QuickAbort = FALSE;
78ed81a3 949 clearenvelope(e, FALSE);
15637ed4
RG
950 }
951 }
952
953 /* open alias database */
78ed81a3 954 initmaps(FALSE, e);
15637ed4
RG
955
956 return (0);
957}
958
78ed81a3 959# endif /* SMTP */