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