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