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