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