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