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