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.64 (Berkeley) 03/31/95 (with SMTP)";
14 #else
15 static char sccsid[] = "@(#)srvrsmtp.c	8.64 (Berkeley) 03/31/95 (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 	char *peerhostname;		/* name of SMTP peer or "localhost" */
113 	auto char *delimptr;
114 	char *id;
115 	int nrcpts = 0;			/* number of RCPT commands */
116 	bool doublequeue;
117 	int badcommands = 0;		/* count of bad commands */
118 	char inp[MAXLINE];
119 	char cmdbuf[MAXLINE];
120 	extern char Version[];
121 	extern ENVELOPE BlankEnvelope;
122 
123 	if (fileno(OutChannel) != fileno(stdout))
124 	{
125 		/* arrange for debugging output to go to remote host */
126 		(void) dup2(fileno(OutChannel), fileno(stdout));
127 	}
128 	settime(e);
129 	peerhostname = RealHostName;
130 	if (peerhostname == NULL)
131 		peerhostname = "localhost";
132 	CurHostName = peerhostname;
133 	CurSmtpClient = macvalue('_', e);
134 	if (CurSmtpClient == NULL)
135 		CurSmtpClient = CurHostName;
136 
137 	setproctitle("server %s startup", CurSmtpClient);
138 	expand("\201e", inp, sizeof inp, e);
139 	if (BrokenSmtpPeers)
140 	{
141 		p = strchr(inp, '\n');
142 		if (p != NULL)
143 			*p = '\0';
144 		message("220 %s", inp);
145 	}
146 	else
147 	{
148 		char *q = inp;
149 
150 		while (q != NULL)
151 		{
152 			p = strchr(q, '\n');
153 			if (p != NULL)
154 				*p++ = '\0';
155 			message("220-%s", q);
156 			q = p;
157 		}
158 		message("220 ESMTP spoken here");
159 	}
160 	protocol = NULL;
161 	sendinghost = macvalue('s', e);
162 	gothello = FALSE;
163 	gotmail = FALSE;
164 	for (;;)
165 	{
166 		/* arrange for backout */
167 		if (setjmp(TopFrame) > 0)
168 		{
169 			/* if() nesting is necessary for Cray UNICOS */
170 			if (InChild)
171 			{
172 				QuickAbort = FALSE;
173 				SuprErrs = TRUE;
174 				finis();
175 			}
176 		}
177 		QuickAbort = FALSE;
178 		HoldErrs = FALSE;
179 		LogUsrErrs = FALSE;
180 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
181 
182 		/* setup for the read */
183 		e->e_to = NULL;
184 		Errors = 0;
185 		(void) fflush(stdout);
186 
187 		/* read the input line */
188 		SmtpPhase = "server cmd read";
189 		setproctitle("server %s cmd read", CurHostName);
190 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
191 				SmtpPhase);
192 
193 		/* handle errors */
194 		if (p == NULL)
195 		{
196 			/* end of file, just die */
197 			disconnect(1, e);
198 			message("421 %s Lost input channel from %s",
199 				MyHostName, CurSmtpClient);
200 #ifdef LOG
201 			if (LogLevel > (gotmail ? 1 : 19))
202 				syslog(LOG_NOTICE, "lost input channel from %s",
203 					CurSmtpClient);
204 #endif
205 			if (InChild)
206 				ExitStat = EX_QUIT;
207 			finis();
208 		}
209 
210 		/* clean up end of line */
211 		fixcrlf(inp, TRUE);
212 
213 		/* echo command to transcript */
214 		if (e->e_xfp != NULL)
215 			fprintf(e->e_xfp, "<<< %s\n", inp);
216 
217 		if (e->e_id == NULL)
218 			setproctitle("%s: %.80s", CurSmtpClient, inp);
219 		else
220 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
221 
222 		/* break off command */
223 		for (p = inp; isascii(*p) && isspace(*p); p++)
224 			continue;
225 		cmd = cmdbuf;
226 		while (*p != '\0' &&
227 		       !(isascii(*p) && isspace(*p)) &&
228 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
229 			*cmd++ = *p++;
230 		*cmd = '\0';
231 
232 		/* throw away leading whitespace */
233 		while (isascii(*p) && isspace(*p))
234 			p++;
235 
236 		/* decode command */
237 		for (c = CmdTab; c->cmdname != NULL; c++)
238 		{
239 			if (!strcasecmp(c->cmdname, cmdbuf))
240 				break;
241 		}
242 
243 		/* reset errors */
244 		errno = 0;
245 
246 		/* process command */
247 		switch (c->cmdcode)
248 		{
249 		  case CMDHELO:		/* hello -- introduce yourself */
250 		  case CMDEHLO:		/* extended hello */
251 			if (c->cmdcode == CMDEHLO)
252 			{
253 				protocol = "ESMTP";
254 				SmtpPhase = "server EHLO";
255 			}
256 			else
257 			{
258 				protocol = "SMTP";
259 				SmtpPhase = "server HELO";
260 			}
261 
262 			/* check for valid domain name (re 1123 5.2.5) */
263 			if (*p == '\0')
264 			{
265 				message("501 %s requires domain address",
266 					cmdbuf);
267 				break;
268 			}
269 			else
270 			{
271 				register char *q;
272 
273 				for (q = p; *q != '\0'; q++)
274 				{
275 					if (!isascii(*q))
276 						break;
277 					if (isalnum(*q))
278 						continue;
279 					if (strchr("[].-_#", *q) == NULL)
280 						break;
281 				}
282 				if (*q != '\0')
283 				{
284 					message("501 Invalid domain name");
285 					break;
286 				}
287 			}
288 
289 			sendinghost = newstr(p);
290 			gothello = TRUE;
291 			if (c->cmdcode != CMDEHLO)
292 			{
293 				/* print old message and be done with it */
294 				message("250 %s Hello %s, pleased to meet you",
295 					MyHostName, CurSmtpClient);
296 				break;
297 			}
298 
299 			/* print extended message and brag */
300 			message("250-%s Hello %s, pleased to meet you",
301 				MyHostName, CurSmtpClient);
302 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
303 				message("250-EXPN");
304 			message("250-8BITMIME");
305 			if (MaxMessageSize > 0)
306 				message("250-SIZE %ld", MaxMessageSize);
307 			else
308 				message("250-SIZE");
309 #ifdef DSN
310 			message("250-X-DSN-03 (Unpublished draft of 12 Mar 1995)");
311 #endif
312 			message("250 HELP");
313 			break;
314 
315 		  case CMDMAIL:		/* mail -- designate sender */
316 			SmtpPhase = "server MAIL";
317 
318 			/* check for validity of this command */
319 			if (!gothello)
320 			{
321 				/* set sending host to our known value */
322 				if (sendinghost == NULL)
323 					sendinghost = peerhostname;
324 
325 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
326 				{
327 					message("503 Polite people say HELO first");
328 					break;
329 				}
330 			}
331 			if (gotmail)
332 			{
333 				message("503 Sender already specified");
334 				if (InChild)
335 					finis();
336 				break;
337 			}
338 			if (InChild)
339 			{
340 				errno = 0;
341 				syserr("503 Nested MAIL command: MAIL %s", p);
342 				finis();
343 			}
344 
345 			/* fork a subprocess to process this command */
346 			if (runinchild("SMTP-MAIL", e) > 0)
347 				break;
348 			if (!gothello)
349 			{
350 				auth_warning(e,
351 					"Host %s didn't use HELO protocol",
352 					peerhostname);
353 			}
354 #ifdef PICKY_HELO_CHECK
355 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
356 			    (strcasecmp(peerhostname, "localhost") != 0 ||
357 			     strcasecmp(sendinghost, MyHostName) != 0))
358 			{
359 				auth_warning(e, "Host %s claimed to be %s",
360 					peerhostname, sendinghost);
361 			}
362 #endif
363 
364 			if (protocol == NULL)
365 				protocol = "SMTP";
366 			define('r', protocol, e);
367 			define('s', sendinghost, e);
368 			initsys(e);
369 			nrcpts = 0;
370 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
371 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
372 
373 			/* child -- go do the processing */
374 			p = skipword(p, "from");
375 			if (p == NULL)
376 				break;
377 			if (setjmp(TopFrame) > 0)
378 			{
379 				/* this failed -- undo work */
380 				if (InChild)
381 				{
382 					QuickAbort = FALSE;
383 					SuprErrs = TRUE;
384 					e->e_flags &= ~EF_FATALERRS;
385 					finis();
386 				}
387 				break;
388 			}
389 			QuickAbort = TRUE;
390 
391 			/* must parse sender first */
392 			delimptr = NULL;
393 			setsender(p, e, &delimptr, FALSE);
394 			p = delimptr;
395 			if (p != NULL && *p != '\0')
396 				*p++ = '\0';
397 
398 			/* check for possible spoofing */
399 			if (RealUid != 0 && OpMode == MD_SMTP &&
400 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
401 			    strcmp(e->e_from.q_user, RealUserName) != 0)
402 			{
403 				auth_warning(e, "%s owned process doing -bs",
404 					RealUserName);
405 			}
406 
407 			/* now parse ESMTP arguments */
408 			e->e_msgsize = 0;
409 			while (p != NULL && *p != '\0')
410 			{
411 				char *kp;
412 				char *vp = NULL;
413 
414 				/* locate the beginning of the keyword */
415 				while (isascii(*p) && isspace(*p))
416 					p++;
417 				if (*p == '\0')
418 					break;
419 				kp = p;
420 
421 				/* skip to the value portion */
422 				while (isascii(*p) && isalnum(*p) || *p == '-')
423 					p++;
424 				if (*p == '=')
425 				{
426 					*p++ = '\0';
427 					vp = p;
428 
429 					/* skip to the end of the value */
430 					while (*p != '\0' && *p != ' ' &&
431 					       !(isascii(*p) && iscntrl(*p)) &&
432 					       *p != '=')
433 						p++;
434 				}
435 
436 				if (*p != '\0')
437 					*p++ = '\0';
438 
439 				if (tTd(19, 1))
440 					printf("MAIL: got arg %s=\"%s\"\n", kp,
441 						vp == NULL ? "<null>" : vp);
442 
443 				mail_esmtp_args(kp, vp, e);
444 			}
445 
446 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
447 			{
448 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
449 					MaxMessageSize);
450 				/* NOTREACHED */
451 			}
452 
453 			if (!enoughspace(e->e_msgsize))
454 			{
455 				message("452 Insufficient disk space; try again later");
456 				break;
457 			}
458 			message("250 Sender ok");
459 			gotmail = TRUE;
460 			break;
461 
462 		  case CMDRCPT:		/* rcpt -- designate recipient */
463 			if (!gotmail)
464 			{
465 				usrerr("503 Need MAIL before RCPT");
466 				break;
467 			}
468 			SmtpPhase = "server RCPT";
469 			if (setjmp(TopFrame) > 0)
470 			{
471 				e->e_flags &= ~EF_FATALERRS;
472 				break;
473 			}
474 			QuickAbort = TRUE;
475 			LogUsrErrs = TRUE;
476 
477 			if (e->e_sendmode != SM_DELIVER)
478 				e->e_flags |= EF_VRFYONLY;
479 
480 			p = skipword(p, "to");
481 			if (p == NULL)
482 				break;
483 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
484 			if (a == NULL)
485 				break;
486 			p = delimptr;
487 
488 			/* now parse ESMTP arguments */
489 			while (p != NULL && *p != '\0')
490 			{
491 				char *kp;
492 				char *vp = NULL;
493 
494 				/* locate the beginning of the keyword */
495 				while (isascii(*p) && isspace(*p))
496 					p++;
497 				if (*p == '\0')
498 					break;
499 				kp = p;
500 
501 				/* skip to the value portion */
502 				while (isascii(*p) && isalnum(*p) || *p == '-')
503 					p++;
504 				if (*p == '=')
505 				{
506 					*p++ = '\0';
507 					vp = p;
508 
509 					/* skip to the end of the value */
510 					while (*p != '\0' && *p != ' ' &&
511 					       !(isascii(*p) && iscntrl(*p)) &&
512 					       *p != '=')
513 						p++;
514 				}
515 
516 				if (*p != '\0')
517 					*p++ = '\0';
518 
519 				if (tTd(19, 1))
520 					printf("RCPT: got arg %s=\"%s\"\n", kp,
521 						vp == NULL ? "<null>" : vp);
522 
523 				rcpt_esmtp_args(a, kp, vp, e);
524 
525 			}
526 
527 			/* save in recipient list after ESMTP mods */
528 			a = recipient(a, &e->e_sendqueue, 0, e);
529 
530 			if (Errors != 0)
531 				break;
532 
533 			/* no errors during parsing, but might be a duplicate */
534 			e->e_to = p;
535 			if (!bitset(QBADADDR, a->q_flags))
536 			{
537 				message("250 Recipient ok%s",
538 					bitset(QQUEUEUP, a->q_flags) ?
539 						" (will queue)" : "");
540 				nrcpts++;
541 			}
542 			else
543 			{
544 				/* punt -- should keep message in ADDRESS.... */
545 				message("550 Addressee unknown");
546 			}
547 			e->e_to = NULL;
548 			break;
549 
550 		  case CMDDATA:		/* data -- text of mail */
551 			SmtpPhase = "server DATA";
552 			if (!gotmail)
553 			{
554 				message("503 Need MAIL command");
555 				break;
556 			}
557 			else if (nrcpts <= 0)
558 			{
559 				message("503 Need RCPT (recipient)");
560 				break;
561 			}
562 
563 			/* check to see if we need to re-expand aliases */
564 			/* also reset QBADADDR on already-diagnosted addrs */
565 			doublequeue = FALSE;
566 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
567 			{
568 				if (bitset(QVERIFIED, a->q_flags))
569 				{
570 					/* need to re-expand aliases */
571 					doublequeue = TRUE;
572 				}
573 				if (bitset(QBADADDR, a->q_flags))
574 				{
575 					/* make this "go away" */
576 					a->q_flags |= QDONTSEND;
577 					a->q_flags &= ~QBADADDR;
578 				}
579 			}
580 
581 			/* collect the text of the message */
582 			SmtpPhase = "collect";
583 			buffer_errors();
584 			collect(InChannel, TRUE, doublequeue, NULL, e);
585 			flush_errors(TRUE);
586 			if (Errors != 0)
587 				goto abortmessage;
588 
589 			/* make sure we actually do delivery */
590 			e->e_flags &= ~EF_CLRQUEUE;
591 
592 			/* from now on, we have to operate silently */
593 			buffer_errors();
594 			e->e_errormode = EM_MAIL;
595 
596 			/*
597 			**  Arrange to send to everyone.
598 			**	If sending to multiple people, mail back
599 			**		errors rather than reporting directly.
600 			**	In any case, don't mail back errors for
601 			**		anything that has happened up to
602 			**		now (the other end will do this).
603 			**	Truncate our transcript -- the mail has gotten
604 			**		to us successfully, and if we have
605 			**		to mail this back, it will be easier
606 			**		on the reader.
607 			**	Then send to everyone.
608 			**	Finally give a reply code.  If an error has
609 			**		already been given, don't mail a
610 			**		message back.
611 			**	We goose error returns by clearing error bit.
612 			*/
613 
614 			SmtpPhase = "delivery";
615 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
616 			id = e->e_id;
617 
618 			if (doublequeue)
619 			{
620 				/* make sure it is in the queue */
621 				queueup(e, TRUE, FALSE);
622 				if (e->e_sendmode == SM_QUEUE)
623 					e->e_flags |= EF_KEEPQUEUE;
624 			}
625 			else
626 			{
627 				/* send to all recipients */
628 				sendall(e, SM_DEFAULT);
629 			}
630 			e->e_to = NULL;
631 
632 			/* issue success message */
633 			message("250 %s Message accepted for delivery", id);
634 
635 			/* if we just queued, poke it */
636 			if (doublequeue && e->e_sendmode != SM_QUEUE)
637 			{
638 				extern pid_t dowork();
639 
640 				unlockqueue(e);
641 				(void) dowork(id, TRUE, TRUE, e);
642 			}
643 
644   abortmessage:
645 			/* if in a child, pop back to our parent */
646 			if (InChild)
647 				finis();
648 
649 			/* clean up a bit */
650 			gotmail = FALSE;
651 			dropenvelope(e);
652 			CurEnv = e = newenvelope(e, CurEnv);
653 			e->e_flags = BlankEnvelope.e_flags;
654 			break;
655 
656 		  case CMDRSET:		/* rset -- reset state */
657 			message("250 Reset state");
658 
659 			/* arrange to ignore any current send list */
660 			e->e_sendqueue = NULL;
661 			e->e_flags |= EF_CLRQUEUE;
662 			if (InChild)
663 				finis();
664 
665 			/* clean up a bit */
666 			gotmail = FALSE;
667 			dropenvelope(e);
668 			CurEnv = e = newenvelope(e, CurEnv);
669 			break;
670 
671 		  case CMDVRFY:		/* vrfy -- verify address */
672 		  case CMDEXPN:		/* expn -- expand address */
673 			vrfy = c->cmdcode == CMDVRFY;
674 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
675 						PrivacyFlags))
676 			{
677 				if (vrfy)
678 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
679 				else
680 					message("502 Sorry, we do not allow this operation");
681 #ifdef LOG
682 				if (LogLevel > 5)
683 					syslog(LOG_INFO, "%s: %s [rejected]",
684 						CurSmtpClient, inp);
685 #endif
686 				break;
687 			}
688 			else if (!gothello &&
689 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
690 						PrivacyFlags))
691 			{
692 				message("503 I demand that you introduce yourself first");
693 				break;
694 			}
695 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
696 				break;
697 #ifdef LOG
698 			if (LogLevel > 5)
699 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
700 #endif
701 			vrfyqueue = NULL;
702 			QuickAbort = TRUE;
703 			if (vrfy)
704 				e->e_flags |= EF_VRFYONLY;
705 			while (*p != '\0' && isascii(*p) && isspace(*p))
706 				p++;
707 			if (*p == '\0')
708 			{
709 				message("501 Argument required");
710 				Errors++;
711 			}
712 			else
713 			{
714 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
715 			}
716 			if (Errors != 0)
717 			{
718 				if (InChild)
719 					finis();
720 				break;
721 			}
722 			if (vrfyqueue == NULL)
723 			{
724 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
725 			}
726 			while (vrfyqueue != NULL)
727 			{
728 				a = vrfyqueue;
729 				while ((a = a->q_next) != NULL &&
730 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
731 					continue;
732 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
733 					printvrfyaddr(vrfyqueue, a == NULL);
734 				vrfyqueue = vrfyqueue->q_next;
735 			}
736 			if (InChild)
737 				finis();
738 			break;
739 
740 		  case CMDHELP:		/* help -- give user info */
741 			help(p);
742 			break;
743 
744 		  case CMDNOOP:		/* noop -- do nothing */
745 			message("250 OK");
746 			break;
747 
748 		  case CMDQUIT:		/* quit -- leave mail */
749 			message("221 %s closing connection", MyHostName);
750 
751 doquit:
752 			/* arrange to ignore any current send list */
753 			e->e_sendqueue = NULL;
754 
755 			/* avoid future 050 messages */
756 			disconnect(1, e);
757 
758 			if (InChild)
759 				ExitStat = EX_QUIT;
760 			finis();
761 
762 		  case CMDVERB:		/* set verbose mode */
763 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
764 			{
765 				/* this would give out the same info */
766 				message("502 Verbose unavailable");
767 				break;
768 			}
769 			Verbose = TRUE;
770 			e->e_sendmode = SM_DELIVER;
771 			message("250 Verbose mode");
772 			break;
773 
774 		  case CMDONEX:		/* doing one transaction only */
775 			OneXact = TRUE;
776 			message("250 Only one transaction");
777 			break;
778 
779 # ifdef SMTPDEBUG
780 		  case CMDDBGQSHOW:	/* show queues */
781 			printf("Send Queue=");
782 			printaddr(e->e_sendqueue, TRUE);
783 			break;
784 
785 		  case CMDDBGDEBUG:	/* set debug mode */
786 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
787 			tTflag(p);
788 			message("200 Debug set");
789 			break;
790 
791 # else /* not SMTPDEBUG */
792 		  case CMDDBGQSHOW:	/* show queues */
793 		  case CMDDBGDEBUG:	/* set debug mode */
794 # endif /* SMTPDEBUG */
795 		  case CMDLOGBOGUS:	/* bogus command */
796 # ifdef LOG
797 			if (LogLevel > 0)
798 				syslog(LOG_CRIT,
799 				    "\"%s\" command from %s (%s)",
800 				    c->cmdname, peerhostname,
801 				    anynet_ntoa(&RealHostAddr));
802 # endif
803 			/* FALL THROUGH */
804 
805 		  case CMDERROR:	/* unknown command */
806 			if (++badcommands > MAXBADCOMMANDS)
807 			{
808 				message("421 %s Too many bad commands; closing connection",
809 					MyHostName);
810 				goto doquit;
811 			}
812 
813 			message("500 Command unrecognized");
814 			break;
815 
816 		  default:
817 			errno = 0;
818 			syserr("500 smtp: unknown code %d", c->cmdcode);
819 			break;
820 		}
821 	}
822 }
823 /*
824 **  SKIPWORD -- skip a fixed word.
825 **
826 **	Parameters:
827 **		p -- place to start looking.
828 **		w -- word to skip.
829 **
830 **	Returns:
831 **		p following w.
832 **		NULL on error.
833 **
834 **	Side Effects:
835 **		clobbers the p data area.
836 */
837 
838 static char *
839 skipword(p, w)
840 	register char *p;
841 	char *w;
842 {
843 	register char *q;
844 	char *firstp = p;
845 
846 	/* find beginning of word */
847 	while (isascii(*p) && isspace(*p))
848 		p++;
849 	q = p;
850 
851 	/* find end of word */
852 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
853 		p++;
854 	while (isascii(*p) && isspace(*p))
855 		*p++ = '\0';
856 	if (*p != ':')
857 	{
858 	  syntax:
859 		message("501 Syntax error in parameters scanning \"%s\"",
860 			firstp);
861 		Errors++;
862 		return (NULL);
863 	}
864 	*p++ = '\0';
865 	while (isascii(*p) && isspace(*p))
866 		p++;
867 
868 	if (*p == '\0')
869 		goto syntax;
870 
871 	/* see if the input word matches desired word */
872 	if (strcasecmp(q, w))
873 		goto syntax;
874 
875 	return (p);
876 }
877 /*
878 **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
879 **
880 **	Parameters:
881 **		kp -- the parameter key.
882 **		vp -- the value of that parameter.
883 **		e -- the envelope.
884 **
885 **	Returns:
886 **		none.
887 */
888 
889 mail_esmtp_args(kp, vp, e)
890 	char *kp;
891 	char *vp;
892 	ENVELOPE *e;
893 {
894 	if (strcasecmp(kp, "size") == 0)
895 	{
896 		if (vp == NULL)
897 		{
898 			usrerr("501 SIZE requires a value");
899 			/* NOTREACHED */
900 		}
901 # ifdef __STDC__
902 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
903 # else
904 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
905 # endif
906 	}
907 	else if (strcasecmp(kp, "body") == 0)
908 	{
909 		if (vp == NULL)
910 		{
911 			usrerr("501 BODY requires a value");
912 			/* NOTREACHED */
913 		}
914 		if (strcasecmp(vp, "8bitmime") == 0)
915 		{
916 			SevenBitInput = FALSE;
917 		}
918 		else if (strcasecmp(vp, "7bit") == 0)
919 		{
920 			SevenBitInput = TRUE;
921 		}
922 		else
923 		{
924 			usrerr("501 Unknown BODY type %s",
925 				vp);
926 			/* NOTREACHED */
927 		}
928 		e->e_bodytype = newstr(vp);
929 	}
930 	else if (strcasecmp(kp, "envid") == 0)
931 	{
932 		if (vp == NULL)
933 		{
934 			usrerr("501 ENVID requires a value");
935 			/* NOTREACHED */
936 		}
937 		if (!xtextok(vp))
938 		{
939 			usrerr("501 Syntax error in ENVID parameter value");
940 			/* NOTREACHED */
941 		}
942 		if (e->e_envid != NULL)
943 		{
944 			usrerr("501 Duplicate ENVID parameter");
945 			/* NOTREACHED */
946 		}
947 		e->e_envid = newstr(vp);
948 	}
949 	else if (strcasecmp(kp, "ret") == 0)
950 	{
951 		if (vp == NULL)
952 		{
953 			usrerr("501 RET requires a value");
954 			/* NOTREACHED */
955 		}
956 		if (bitset(EF_RET_PARAM, e->e_flags))
957 		{
958 			usrerr("501 Duplicate RET parameter");
959 			/* NOTREACHED */
960 		}
961 		e->e_flags |= EF_RET_PARAM;
962 		if (strcasecmp(vp, "hdrs") == 0)
963 			e->e_flags |= EF_NO_BODY_RETN;
964 		else if (strcasecmp(vp, "full") != 0)
965 		{
966 			usrerr("501 Bad argument \"%s\" to RET", vp);
967 			/* NOTREACHED */
968 		}
969 	}
970 	else
971 	{
972 		usrerr("501 %s parameter unrecognized", kp);
973 		/* NOTREACHED */
974 	}
975 }
976 /*
977 **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
978 **
979 **	Parameters:
980 **		a -- the address corresponding to the To: parameter.
981 **		kp -- the parameter key.
982 **		vp -- the value of that parameter.
983 **		e -- the envelope.
984 **
985 **	Returns:
986 **		none.
987 */
988 
989 rcpt_esmtp_args(a, kp, vp, e)
990 	ADDRESS *a;
991 	char *kp;
992 	char *vp;
993 	ENVELOPE *e;
994 {
995 	if (strcasecmp(kp, "notify") == 0)
996 	{
997 		char *p;
998 
999 		if (vp == NULL)
1000 		{
1001 			usrerr("501 NOTIFY requires a value");
1002 			/* NOTREACHED */
1003 		}
1004 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
1005 		a->q_flags |= QHASNOTIFY;
1006 		if (strcasecmp(vp, "never") == 0)
1007 			return;
1008 		for (p = vp; p != NULL; vp = p)
1009 		{
1010 			p = strchr(p, ',');
1011 			if (p != NULL)
1012 				*p++ = '\0';
1013 			if (strcasecmp(vp, "success") == 0)
1014 				a->q_flags |= QPINGONSUCCESS;
1015 			else if (strcasecmp(vp, "failure") == 0)
1016 				a->q_flags |= QPINGONFAILURE;
1017 			else if (strcasecmp(vp, "delay") == 0)
1018 				a->q_flags |= QPINGONDELAY;
1019 			else
1020 			{
1021 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
1022 					vp);
1023 				/* NOTREACHED */
1024 			}
1025 		}
1026 	}
1027 	else if (strcasecmp(kp, "orcpt") == 0)
1028 	{
1029 		if (vp == NULL)
1030 		{
1031 			usrerr("501 ORCPT requires a value");
1032 			/* NOTREACHED */
1033 		}
1034 		if (!xtextok(vp))
1035 		{
1036 			usrerr("501 Syntax error in ORCPT parameter value");
1037 			/* NOTREACHED */
1038 		}
1039 		if (a->q_orcpt != NULL)
1040 		{
1041 			usrerr("501 Duplicate ORCPT parameter");
1042 			/* NOTREACHED */
1043 		}
1044 		a->q_orcpt = newstr(vp);
1045 	}
1046 	else
1047 	{
1048 		usrerr("501 %s parameter unrecognized", kp);
1049 		/* NOTREACHED */
1050 	}
1051 }
1052 /*
1053 **  PRINTVRFYADDR -- print an entry in the verify queue
1054 **
1055 **	Parameters:
1056 **		a -- the address to print
1057 **		last -- set if this is the last one.
1058 **
1059 **	Returns:
1060 **		none.
1061 **
1062 **	Side Effects:
1063 **		Prints the appropriate 250 codes.
1064 */
1065 
1066 printvrfyaddr(a, last)
1067 	register ADDRESS *a;
1068 	bool last;
1069 {
1070 	char fmtbuf[20];
1071 
1072 	strcpy(fmtbuf, "250");
1073 	fmtbuf[3] = last ? ' ' : '-';
1074 
1075 	if (a->q_fullname == NULL)
1076 	{
1077 		if (strchr(a->q_user, '@') == NULL)
1078 			strcpy(&fmtbuf[4], "<%s@%s>");
1079 		else
1080 			strcpy(&fmtbuf[4], "<%s>");
1081 		message(fmtbuf, a->q_user, MyHostName);
1082 	}
1083 	else
1084 	{
1085 		if (strchr(a->q_user, '@') == NULL)
1086 			strcpy(&fmtbuf[4], "%s <%s@%s>");
1087 		else
1088 			strcpy(&fmtbuf[4], "%s <%s>");
1089 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
1090 	}
1091 }
1092 /*
1093 **  HELP -- implement the HELP command.
1094 **
1095 **	Parameters:
1096 **		topic -- the topic we want help for.
1097 **
1098 **	Returns:
1099 **		none.
1100 **
1101 **	Side Effects:
1102 **		outputs the help file to message output.
1103 */
1104 
1105 help(topic)
1106 	char *topic;
1107 {
1108 	register FILE *hf;
1109 	int len;
1110 	char buf[MAXLINE];
1111 	bool noinfo;
1112 
1113 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
1114 	{
1115 		/* no help */
1116 		errno = 0;
1117 		message("502 HELP not implemented");
1118 		return;
1119 	}
1120 
1121 	if (topic == NULL || *topic == '\0')
1122 		topic = "smtp";
1123 	else
1124 		makelower(topic);
1125 
1126 	len = strlen(topic);
1127 	noinfo = TRUE;
1128 
1129 	while (fgets(buf, sizeof buf, hf) != NULL)
1130 	{
1131 		if (strncmp(buf, topic, len) == 0)
1132 		{
1133 			register char *p;
1134 
1135 			p = strchr(buf, '\t');
1136 			if (p == NULL)
1137 				p = buf;
1138 			else
1139 				p++;
1140 			fixcrlf(p, TRUE);
1141 			message("214-%s", p);
1142 			noinfo = FALSE;
1143 		}
1144 	}
1145 
1146 	if (noinfo)
1147 		message("504 HELP topic unknown");
1148 	else
1149 		message("214 End of HELP info");
1150 	(void) fclose(hf);
1151 }
1152 /*
1153 **  RUNINCHILD -- return twice -- once in the child, then in the parent again
1154 **
1155 **	Parameters:
1156 **		label -- a string used in error messages
1157 **
1158 **	Returns:
1159 **		zero in the child
1160 **		one in the parent
1161 **
1162 **	Side Effects:
1163 **		none.
1164 */
1165 
1166 runinchild(label, e)
1167 	char *label;
1168 	register ENVELOPE *e;
1169 {
1170 	int childpid;
1171 
1172 	if (!OneXact)
1173 	{
1174 		childpid = dofork();
1175 		if (childpid < 0)
1176 		{
1177 			syserr("%s: cannot fork", label);
1178 			return (1);
1179 		}
1180 		if (childpid > 0)
1181 		{
1182 			auto int st;
1183 
1184 			/* parent -- wait for child to complete */
1185 			setproctitle("server %s child wait", CurHostName);
1186 			st = waitfor(childpid);
1187 			if (st == -1)
1188 				syserr("%s: lost child", label);
1189 			else if (!WIFEXITED(st))
1190 				syserr("%s: died on signal %d",
1191 					label, st & 0177);
1192 
1193 			/* if we exited on a QUIT command, complete the process */
1194 			if (WEXITSTATUS(st) == EX_QUIT)
1195 			{
1196 				disconnect(1, e);
1197 				finis();
1198 			}
1199 
1200 			return (1);
1201 		}
1202 		else
1203 		{
1204 			/* child */
1205 			InChild = TRUE;
1206 			QuickAbort = FALSE;
1207 			clearenvelope(e, FALSE);
1208 		}
1209 	}
1210 
1211 	/* open alias database */
1212 	initmaps(FALSE, e);
1213 
1214 	return (0);
1215 }
1216 
1217 # endif /* SMTP */
1218