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