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.39 (Berkeley) 05/17/94 (with SMTP)";
14 #else
15 static char sccsid[] = "@(#)srvrsmtp.c	8.39 (Berkeley) 05/17/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 
556 			/* from now on, we have to operate silently */
557 			HoldErrs = TRUE;
558 			e->e_errormode = EM_MAIL;
559 
560 			/*
561 			**  Arrange to send to everyone.
562 			**	If sending to multiple people, mail back
563 			**		errors rather than reporting directly.
564 			**	In any case, don't mail back errors for
565 			**		anything that has happened up to
566 			**		now (the other end will do this).
567 			**	Truncate our transcript -- the mail has gotten
568 			**		to us successfully, and if we have
569 			**		to mail this back, it will be easier
570 			**		on the reader.
571 			**	Then send to everyone.
572 			**	Finally give a reply code.  If an error has
573 			**		already been given, don't mail a
574 			**		message back.
575 			**	We goose error returns by clearing error bit.
576 			*/
577 
578 			SmtpPhase = "delivery";
579 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
580 			id = e->e_id;
581 
582 			if (doublequeue)
583 			{
584 				/* make sure it is in the queue */
585 				queueup(e, TRUE, FALSE);
586 			}
587 			else
588 			{
589 				/* send to all recipients */
590 				sendall(e, SM_DEFAULT);
591 			}
592 			e->e_to = NULL;
593 
594 			/* issue success message */
595 			message("250 %s Message accepted for delivery", id);
596 
597 			/* if we just queued, poke it */
598 			if (doublequeue && e->e_sendmode != SM_QUEUE)
599 			{
600 				extern pid_t dowork();
601 
602 				unlockqueue(e);
603 				(void) dowork(id, TRUE, TRUE, e);
604 			}
605 
606   abortmessage:
607 			/* if in a child, pop back to our parent */
608 			if (InChild)
609 				finis();
610 
611 			/* clean up a bit */
612 			gotmail = FALSE;
613 			dropenvelope(e);
614 			CurEnv = e = newenvelope(e, CurEnv);
615 			e->e_flags = BlankEnvelope.e_flags;
616 			break;
617 
618 		  case CMDRSET:		/* rset -- reset state */
619 			message("250 Reset state");
620 			e->e_flags |= EF_CLRQUEUE;
621 			if (InChild)
622 				finis();
623 
624 			/* clean up a bit */
625 			gotmail = FALSE;
626 			dropenvelope(e);
627 			CurEnv = e = newenvelope(e, CurEnv);
628 			break;
629 
630 		  case CMDVRFY:		/* vrfy -- verify address */
631 		  case CMDEXPN:		/* expn -- expand address */
632 			vrfy = c->cmdcode == CMDVRFY;
633 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
634 						PrivacyFlags))
635 			{
636 				if (vrfy)
637 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
638 				else
639 					message("502 Sorry, we do not allow this operation");
640 #ifdef LOG
641 				if (LogLevel > 5)
642 					syslog(LOG_INFO, "%s: %s [rejected]",
643 						CurSmtpClient, inp);
644 #endif
645 				break;
646 			}
647 			else if (!gothello &&
648 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
649 						PrivacyFlags))
650 			{
651 				message("503 I demand that you introduce yourself first");
652 				break;
653 			}
654 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
655 				break;
656 #ifdef LOG
657 			if (LogLevel > 5)
658 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
659 #endif
660 			vrfyqueue = NULL;
661 			QuickAbort = TRUE;
662 			if (vrfy)
663 				e->e_flags |= EF_VRFYONLY;
664 			while (*p != '\0' && isascii(*p) && isspace(*p))
665 				*p++;
666 			if (*p == '\0')
667 			{
668 				message("501 Argument required");
669 				Errors++;
670 			}
671 			else
672 			{
673 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
674 			}
675 			if (Errors != 0)
676 			{
677 				if (InChild)
678 					finis();
679 				break;
680 			}
681 			if (vrfyqueue == NULL)
682 			{
683 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
684 			}
685 			while (vrfyqueue != NULL)
686 			{
687 				a = vrfyqueue;
688 				while ((a = a->q_next) != NULL &&
689 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
690 					continue;
691 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
692 					printvrfyaddr(vrfyqueue, a == NULL);
693 				vrfyqueue = vrfyqueue->q_next;
694 			}
695 			if (InChild)
696 				finis();
697 			break;
698 
699 		  case CMDHELP:		/* help -- give user info */
700 			help(p);
701 			break;
702 
703 		  case CMDNOOP:		/* noop -- do nothing */
704 			message("250 OK");
705 			break;
706 
707 		  case CMDQUIT:		/* quit -- leave mail */
708 			message("221 %s closing connection", MyHostName);
709 
710 doquit:
711 			/* avoid future 050 messages */
712 			disconnect(1, e);
713 
714 			if (InChild)
715 				ExitStat = EX_QUIT;
716 			finis();
717 
718 		  case CMDVERB:		/* set verbose mode */
719 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
720 			{
721 				/* this would give out the same info */
722 				message("502 Verbose unavailable");
723 				break;
724 			}
725 			Verbose = TRUE;
726 			e->e_sendmode = SM_DELIVER;
727 			message("250 Verbose mode");
728 			break;
729 
730 		  case CMDONEX:		/* doing one transaction only */
731 			OneXact = TRUE;
732 			message("250 Only one transaction");
733 			break;
734 
735 # ifdef SMTPDEBUG
736 		  case CMDDBGQSHOW:	/* show queues */
737 			printf("Send Queue=");
738 			printaddr(e->e_sendqueue, TRUE);
739 			break;
740 
741 		  case CMDDBGDEBUG:	/* set debug mode */
742 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
743 			tTflag(p);
744 			message("200 Debug set");
745 			break;
746 
747 # else /* not SMTPDEBUG */
748 		  case CMDDBGQSHOW:	/* show queues */
749 		  case CMDDBGDEBUG:	/* set debug mode */
750 # endif /* SMTPDEBUG */
751 		  case CMDLOGBOGUS:	/* bogus command */
752 # ifdef LOG
753 			if (LogLevel > 0)
754 				syslog(LOG_CRIT,
755 				    "\"%s\" command from %s (%s)",
756 				    c->cmdname, peerhostname,
757 				    anynet_ntoa(&RealHostAddr));
758 # endif
759 			/* FALL THROUGH */
760 
761 		  case CMDERROR:	/* unknown command */
762 			if (++badcommands > MAXBADCOMMANDS)
763 			{
764 				message("421 %s Too many bad commands; closing connection",
765 					MyHostName);
766 				goto doquit;
767 			}
768 
769 			message("500 Command unrecognized");
770 			break;
771 
772 		  default:
773 			errno = 0;
774 			syserr("500 smtp: unknown code %d", c->cmdcode);
775 			break;
776 		}
777 	}
778 }
779 /*
780 **  SKIPWORD -- skip a fixed word.
781 **
782 **	Parameters:
783 **		p -- place to start looking.
784 **		w -- word to skip.
785 **
786 **	Returns:
787 **		p following w.
788 **		NULL on error.
789 **
790 **	Side Effects:
791 **		clobbers the p data area.
792 */
793 
794 static char *
795 skipword(p, w)
796 	register char *p;
797 	char *w;
798 {
799 	register char *q;
800 	char *firstp = p;
801 
802 	/* find beginning of word */
803 	while (isascii(*p) && isspace(*p))
804 		p++;
805 	q = p;
806 
807 	/* find end of word */
808 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
809 		p++;
810 	while (isascii(*p) && isspace(*p))
811 		*p++ = '\0';
812 	if (*p != ':')
813 	{
814 	  syntax:
815 		message("501 Syntax error in parameters scanning \"%s\"",
816 			firstp);
817 		Errors++;
818 		return (NULL);
819 	}
820 	*p++ = '\0';
821 	while (isascii(*p) && isspace(*p))
822 		p++;
823 
824 	if (*p == '\0')
825 		goto syntax;
826 
827 	/* see if the input word matches desired word */
828 	if (strcasecmp(q, w))
829 		goto syntax;
830 
831 	return (p);
832 }
833 /*
834 **  PRINTVRFYADDR -- print an entry in the verify queue
835 **
836 **	Parameters:
837 **		a -- the address to print
838 **		last -- set if this is the last one.
839 **
840 **	Returns:
841 **		none.
842 **
843 **	Side Effects:
844 **		Prints the appropriate 250 codes.
845 */
846 
847 printvrfyaddr(a, last)
848 	register ADDRESS *a;
849 	bool last;
850 {
851 	char fmtbuf[20];
852 
853 	strcpy(fmtbuf, "250");
854 	fmtbuf[3] = last ? ' ' : '-';
855 
856 	if (a->q_fullname == NULL)
857 	{
858 		if (strchr(a->q_user, '@') == NULL)
859 			strcpy(&fmtbuf[4], "<%s@%s>");
860 		else
861 			strcpy(&fmtbuf[4], "<%s>");
862 		message(fmtbuf, a->q_user, MyHostName);
863 	}
864 	else
865 	{
866 		if (strchr(a->q_user, '@') == NULL)
867 			strcpy(&fmtbuf[4], "%s <%s@%s>");
868 		else
869 			strcpy(&fmtbuf[4], "%s <%s>");
870 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
871 	}
872 }
873 /*
874 **  HELP -- implement the HELP command.
875 **
876 **	Parameters:
877 **		topic -- the topic we want help for.
878 **
879 **	Returns:
880 **		none.
881 **
882 **	Side Effects:
883 **		outputs the help file to message output.
884 */
885 
886 help(topic)
887 	char *topic;
888 {
889 	register FILE *hf;
890 	int len;
891 	char buf[MAXLINE];
892 	bool noinfo;
893 
894 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
895 	{
896 		/* no help */
897 		errno = 0;
898 		message("502 HELP not implemented");
899 		return;
900 	}
901 
902 	if (topic == NULL || *topic == '\0')
903 		topic = "smtp";
904 	else
905 		makelower(topic);
906 
907 	len = strlen(topic);
908 	noinfo = TRUE;
909 
910 	while (fgets(buf, sizeof buf, hf) != NULL)
911 	{
912 		if (strncmp(buf, topic, len) == 0)
913 		{
914 			register char *p;
915 
916 			p = strchr(buf, '\t');
917 			if (p == NULL)
918 				p = buf;
919 			else
920 				p++;
921 			fixcrlf(p, TRUE);
922 			message("214-%s", p);
923 			noinfo = FALSE;
924 		}
925 	}
926 
927 	if (noinfo)
928 		message("504 HELP topic unknown");
929 	else
930 		message("214 End of HELP info");
931 	(void) fclose(hf);
932 }
933 /*
934 **  RUNINCHILD -- return twice -- once in the child, then in the parent again
935 **
936 **	Parameters:
937 **		label -- a string used in error messages
938 **
939 **	Returns:
940 **		zero in the child
941 **		one in the parent
942 **
943 **	Side Effects:
944 **		none.
945 */
946 
947 runinchild(label, e)
948 	char *label;
949 	register ENVELOPE *e;
950 {
951 	int childpid;
952 
953 	if (!OneXact)
954 	{
955 		childpid = dofork();
956 		if (childpid < 0)
957 		{
958 			syserr("%s: cannot fork", label);
959 			return (1);
960 		}
961 		if (childpid > 0)
962 		{
963 			auto int st;
964 
965 			/* parent -- wait for child to complete */
966 			setproctitle("server %s child wait", CurHostName);
967 			st = waitfor(childpid);
968 			if (st == -1)
969 				syserr("%s: lost child", label);
970 			else if (!WIFEXITED(st))
971 				syserr("%s: died on signal %d",
972 					label, st & 0177);
973 
974 			/* if we exited on a QUIT command, complete the process */
975 			if (WEXITSTATUS(st) == EX_QUIT)
976 			{
977 				disconnect(1, e);
978 				finis();
979 			}
980 
981 			return (1);
982 		}
983 		else
984 		{
985 			/* child */
986 			InChild = TRUE;
987 			QuickAbort = FALSE;
988 			clearenvelope(e, FALSE);
989 		}
990 	}
991 
992 	/* open alias database */
993 	initmaps(FALSE, e);
994 
995 	return (0);
996 }
997 
998 # endif /* SMTP */
999