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