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