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