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