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