try to eliminate some of the CurEnv globals
[unix-history] / usr / src / usr.sbin / sendmail / src / srvrsmtp.c
CommitLineData
b2a81223 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
bee79b64
KB
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
417f7a11 6 * %sccs.include.redist.c%
bee79b64
KB
7 */
8
9# include "sendmail.h"
b2a81223 10
bee79b64
KB
11#ifndef lint
12#ifdef SMTP
b5958391 13static char sccsid[] = "@(#)srvrsmtp.c 5.34 (Berkeley) %G% (with SMTP)";
bee79b64 14#else
b5958391 15static char sccsid[] = "@(#)srvrsmtp.c 5.34 (Berkeley) %G% (without SMTP)";
bee79b64
KB
16#endif
17#endif /* not lint */
b2a81223 18
e6f08ab1 19# include <errno.h>
671d4b5c 20# include <signal.h>
6b861048 21
bee79b64 22# ifdef SMTP
d727056e 23
6b861048
EA
24/*
25** SMTP -- run the SMTP protocol.
26**
27** Parameters:
28** none.
29**
30** Returns:
31** never.
32**
33** Side Effects:
34** Reads commands from the input channel and processes
35** them.
36*/
37
38struct cmd
39{
40 char *cmdname; /* command name */
41 int cmdcode; /* internal code, see below */
42};
43
44/* values for cmdcode */
45# define CMDERROR 0 /* bad command */
46# define CMDMAIL 1 /* mail -- designate sender */
4a4ebe09 47# define CMDRCPT 2 /* rcpt -- designate recipient */
6b861048 48# define CMDDATA 3 /* data -- send message text */
7d7fdf93 49# define CMDHOPS 4 /* hops -- specify hop count */
e6f08ab1
EA
50# define CMDRSET 4 /* rset -- reset state */
51# define CMDVRFY 5 /* vrfy -- verify address */
52# define CMDHELP 6 /* help -- give usage info */
53# define CMDNOOP 7 /* noop -- do nothing */
54# define CMDQUIT 8 /* quit -- close connection and die */
55# define CMDHELO 9 /* helo -- be polite */
ab4889ea
MK
56# define CMDONEX 10 /* onex -- sending one transaction only */
57# define CMDVERB 11 /* verb -- go into verbose mode */
58/* debugging-only commands, only enabled if SMTPDEBUG is defined */
59# define CMDDBGQSHOW 12 /* showq -- show send queue */
60# define CMDDBGDEBUG 13 /* debug -- set debug mode */
6b861048
EA
61
62static struct cmd CmdTab[] =
63{
64 "mail", CMDMAIL,
4a4ebe09 65 "rcpt", CMDRCPT,
6b861048 66 "data", CMDDATA,
6b861048
EA
67 "rset", CMDRSET,
68 "vrfy", CMDVRFY,
abae7b2d 69 "expn", CMDVRFY,
633a2e02 70 "expn", CMDVRFY,
6b861048
EA
71 "help", CMDHELP,
72 "noop", CMDNOOP,
73 "quit", CMDQUIT,
4a4ebe09 74 "helo", CMDHELO,
e8ad767d 75 "verb", CMDVERB,
8fe4fb9b 76 "onex", CMDONEX,
7d7fdf93 77 "hops", CMDHOPS,
ab4889ea
MK
78 /*
79 * remaining commands are here only
80 * to trap and log attempts to use them
81 */
e6f08ab1 82 "showq", CMDDBGQSHOW,
ad20d67b 83 "debug", CMDDBGDEBUG,
6b861048
EA
84 NULL, CMDERROR,
85};
86
e6f08ab1 87bool InChild = FALSE; /* true if running in a subprocess */
7338e3d4 88bool OneXact = FALSE; /* one xaction only this run */
1972fb40 89
e6f08ab1 90#define EX_QUIT 22 /* special code for QUIT command */
e8ad767d 91
6b861048
EA
92smtp()
93{
6b861048 94 register char *p;
e8ad767d 95 register struct cmd *c;
6b861048 96 char *cmd;
0df908a9 97 static char *skipword();
6b861048 98 bool hasmail; /* mail command received */
abae7b2d
EA
99 extern ADDRESS *sendto();
100 ADDRESS *a;
6b861048 101
abae7b2d 102 hasmail = FALSE;
1f1cc003
EA
103 if (OutChannel != stdout)
104 {
105 /* arrange for debugging output to go to remote host */
106 (void) close(1);
107 (void) dup(fileno(OutChannel));
108 }
1b932f66 109 settime();
356262e6 110 if (RealHostName != NULL)
57c97d4a
EA
111 {
112 CurHostName = RealHostName;
113 setproctitle("srvrsmtp %s", CurHostName);
114 }
115 else
116 {
117 /* this must be us!! */
118 CurHostName = MyHostName;
119 }
a73ae8ac 120 expand("\001e", inp, &inp[sizeof inp], CurEnv);
378e8da7 121 message("220", inp);
2e3062fe 122 SmtpPhase = "startup";
7564b51f 123 sendinghost = NULL;
6b861048
EA
124 for (;;)
125 {
d344c0b7
EA
126 /* arrange for backout */
127 if (setjmp(TopFrame) > 0 && InChild)
128 finis();
129 QuickAbort = FALSE;
130 HoldErrs = FALSE;
f61c3c40 131 LogUsrErrs = FALSE;
d344c0b7 132
37eaaadb 133 /* setup for the read */
2654b031 134 CurEnv->e_to = NULL;
34d37b7d 135 Errors = 0;
09eb49d8 136 (void) fflush(stdout);
37eaaadb 137
37eaaadb 138 /* read the input line */
2439b900 139 p = sfgets(inp, sizeof inp, InChannel);
37eaaadb 140
2439b900 141 /* handle errors */
37eaaadb 142 if (p == NULL)
6b861048
EA
143 {
144 /* end of file, just die */
ab4889ea 145 message("421", "%s Lost input channel from %s",
57c97d4a 146 MyHostName, CurHostName);
6b861048
EA
147 finis();
148 }
149
150 /* clean up end of line */
2768afe3 151 fixcrlf(inp, TRUE);
6b861048 152
49086753 153 /* echo command to transcript */
912acb74
EA
154 if (CurEnv->e_xfp != NULL)
155 fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
49086753 156
6b861048
EA
157 /* break off command */
158 for (p = inp; isspace(*p); p++)
159 continue;
160 cmd = p;
a0225d08
EA
161 for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
162 *cmd++ = *p++;
163 *cmd = '\0';
6b861048 164
a1a07282
EA
165 /* throw away leading whitespace */
166 while (isspace(*p))
167 p++;
168
6b861048
EA
169 /* decode command */
170 for (c = CmdTab; c->cmdname != NULL; c++)
171 {
ed73ef1d 172 if (!strcasecmp(c->cmdname, cmdbuf))
6b861048
EA
173 break;
174 }
175
5ae51cd5
EA
176 /* reset errors */
177 errno = 0;
178
6b861048
EA
179 /* process command */
180 switch (c->cmdcode)
181 {
4a4ebe09 182 case CMDHELO: /* hello -- introduce yourself */
2e3062fe 183 SmtpPhase = "HELO";
57c97d4a 184 setproctitle("%s: %s", CurHostName, inp);
ed73ef1d 185 if (!strcasecmp(p, MyHostName))
f7e74083 186 {
ab4889ea
MK
187 /*
188 * didn't know about alias,
189 * or connected to an echo server
190 */
af80352d
EA
191 message("553", "%s config error: mail loops back to myself",
192 MyHostName);
f7e74083
EA
193 break;
194 }
ed73ef1d 195 if (RealHostName != NULL && strcasecmp(p, RealHostName))
1972fb40 196 {
a0225d08 197 char hostbuf[MAXNAME];
1972fb40 198
a0225d08 199 (void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
7564b51f 200 sendinghost = newstr(hostbuf);
1972fb40
EA
201 }
202 else
7564b51f 203 sendinghost = newstr(p);
abae7b2d 204 message("250", "%s Hello %s, pleased to meet you", HostName, p);
4a4ebe09
EA
205 break;
206
6b861048 207 case CMDMAIL: /* mail -- designate sender */
2e3062fe
EA
208 SmtpPhase = "MAIL";
209
0c6a0070
EA
210 /* force a sending host even if no HELO given */
211 if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
7564b51f 212 sendinghost = RealHostName;
0c6a0070 213
8fe4fb9b 214 /* check for validity of this command */
2768afe3
EA
215 if (hasmail)
216 {
217 message("503", "Sender already specified");
218 break;
219 }
e6f08ab1
EA
220 if (InChild)
221 {
ab4889ea 222 errno = 0;
e6f08ab1
EA
223 syserr("Nested MAIL command");
224 exit(0);
225 }
226
227 /* fork a subprocess to process this command */
228 if (runinchild("SMTP-MAIL") > 0)
229 break;
7564b51f 230 define('s', sendinghost, CurEnv);
dfff22e9 231 define('r', "SMTP", CurEnv);
e6f08ab1 232 initsys();
4d344ce6 233 setproctitle("%s %s: %s", CurEnv->e_id,
57c97d4a 234 CurHostName, inp);
e6f08ab1
EA
235
236 /* child -- go do the processing */
6b861048
EA
237 p = skipword(p, "from");
238 if (p == NULL)
239 break;
b5958391 240 setsender(p, CurEnv);
34d37b7d 241 if (Errors == 0)
6b861048
EA
242 {
243 message("250", "Sender ok");
244 hasmail = TRUE;
245 }
e6f08ab1
EA
246 else if (InChild)
247 finis();
6b861048
EA
248 break;
249
4a4ebe09 250 case CMDRCPT: /* rcpt -- designate recipient */
2e3062fe 251 SmtpPhase = "RCPT";
4d344ce6 252 setproctitle("%s %s: %s", CurEnv->e_id,
57c97d4a 253 CurHostName, inp);
d344c0b7 254 if (setjmp(TopFrame) > 0)
9bfb75c1
EA
255 {
256 CurEnv->e_flags &= ~EF_FATALERRS;
d344c0b7 257 break;
9bfb75c1 258 }
d344c0b7 259 QuickAbort = TRUE;
f61c3c40 260 LogUsrErrs = TRUE;
6b861048
EA
261 p = skipword(p, "to");
262 if (p == NULL)
263 break;
abae7b2d
EA
264 a = sendto(p, 1, (ADDRESS *) NULL, 0);
265# ifdef DEBUG
266 if (Debug > 1)
267 printaddr(a, TRUE);
268# endif DEBUG
d344c0b7
EA
269 if (Errors != 0)
270 break;
271
272 /* no errors during parsing, but might be a duplicate */
273 CurEnv->e_to = p;
274 if (!bitset(QBADADDR, a->q_flags))
275 message("250", "Recipient ok");
276 else
6b861048 277 {
d344c0b7
EA
278 /* punt -- should keep message in ADDRESS.... */
279 message("550", "Addressee unknown");
6b861048 280 }
d344c0b7 281 CurEnv->e_to = NULL;
6b861048
EA
282 break;
283
284 case CMDDATA: /* data -- text of mail */
2e3062fe 285 SmtpPhase = "DATA";
6b861048 286 if (!hasmail)
4a4ebe09 287 {
6b861048 288 message("503", "Need MAIL command");
4a4ebe09
EA
289 break;
290 }
2e3062fe 291 else if (CurEnv->e_nrcpts <= 0)
6b861048 292 {
4a4ebe09
EA
293 message("503", "Need RCPT (recipient)");
294 break;
6b861048 295 }
4a4ebe09
EA
296
297 /* collect the text of the message */
2e3062fe 298 SmtpPhase = "collect";
4d344ce6 299 setproctitle("%s %s: %s", CurEnv->e_id,
57c97d4a 300 CurHostName, inp);
4a4ebe09
EA
301 collect(TRUE);
302 if (Errors != 0)
303 break;
304
8eedb496
EA
305 /*
306 ** Arrange to send to everyone.
307 ** If sending to multiple people, mail back
308 ** errors rather than reporting directly.
309 ** In any case, don't mail back errors for
310 ** anything that has happened up to
311 ** now (the other end will do this).
bcf74f25
EA
312 ** Truncate our transcript -- the mail has gotten
313 ** to us successfully, and if we have
314 ** to mail this back, it will be easier
315 ** on the reader.
8eedb496
EA
316 ** Then send to everyone.
317 ** Finally give a reply code. If an error has
318 ** already been given, don't mail a
319 ** message back.
e6f08ab1 320 ** We goose error returns by clearing error bit.
8eedb496
EA
321 */
322
2e3062fe
EA
323 SmtpPhase = "delivery";
324 if (CurEnv->e_nrcpts != 1)
7338e3d4
EA
325 {
326 HoldErrs = TRUE;
83f674d8 327 ErrorMode = EM_MAIL;
7338e3d4 328 }
e6f08ab1 329 CurEnv->e_flags &= ~EF_FATALERRS;
bcf74f25 330 CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
4a4ebe09
EA
331
332 /* send to all recipients */
f7e74083 333 sendall(CurEnv, SM_DEFAULT);
2654b031 334 CurEnv->e_to = NULL;
4a4ebe09 335
666b39ad
EA
336 /* save statistics */
337 markstats(CurEnv, (ADDRESS *) NULL);
338
8eedb496
EA
339 /* issue success if appropriate and reset */
340 if (Errors == 0 || HoldErrs)
75f95954 341 message("250", "Ok");
8eedb496 342 else
e6f08ab1
EA
343 CurEnv->e_flags &= ~EF_FATALERRS;
344
345 /* if in a child, pop back to our parent */
346 if (InChild)
347 finis();
2e3062fe
EA
348
349 /* clean up a bit */
350 hasmail = 0;
2e3062fe
EA
351 dropenvelope(CurEnv);
352 CurEnv = newenvelope(CurEnv);
353 CurEnv->e_flags = BlankEnvelope.e_flags;
6b861048
EA
354 break;
355
356 case CMDRSET: /* rset -- reset state */
357 message("250", "Reset state");
e6f08ab1
EA
358 if (InChild)
359 finis();
360 break;
6b861048
EA
361
362 case CMDVRFY: /* vrfy -- verify address */
e6f08ab1
EA
363 if (runinchild("SMTP-VRFY") > 0)
364 break;
57c97d4a 365 setproctitle("%s: %s", CurHostName, inp);
abae7b2d 366 paddrtree(a);
6b861048
EA
367 break;
368
369 case CMDHELP: /* help -- give user info */
34d37b7d 370 help(p);
6b861048
EA
371 break;
372
373 case CMDNOOP: /* noop -- do nothing */
374 message("200", "OK");
375 break;
376
377 case CMDQUIT: /* quit -- leave mail */
57c97d4a 378 message("221", "%s closing connection", MyHostName);
e6f08ab1
EA
379 if (InChild)
380 ExitStat = EX_QUIT;
6b861048
EA
381 finis();
382
e8ad767d
EA
383 case CMDVERB: /* set verbose mode */
384 Verbose = TRUE;
28852808 385 SendMode = SM_DELIVER;
e8ad767d
EA
386 message("200", "Verbose mode");
387 break;
388
8fe4fb9b 389 case CMDONEX: /* doing one transaction only */
7338e3d4 390 OneXact = TRUE;
8fe4fb9b
EA
391 message("200", "Only one transaction");
392 break;
393
ab4889ea 394# ifdef SMTPDEBUG
e6f08ab1 395 case CMDDBGQSHOW: /* show queues */
2654b031
EA
396 printf("Send Queue=");
397 printaddr(CurEnv->e_sendqueue, TRUE);
d4f42161 398 break;
09eb49d8
EA
399
400 case CMDDBGDEBUG: /* set debug mode */
9678c96d
EA
401 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
402 tTflag(p);
403 message("200", "Debug set");
09eb49d8
EA
404 break;
405
ab4889ea
MK
406# else /* not SMTPDEBUG */
407
408 case CMDDBGQSHOW: /* show queues */
409 case CMDDBGDEBUG: /* set debug mode */
2e15a2d8
MK
410# ifdef LOG
411 if (RealHostName != NULL && LogLevel > 0)
ab4889ea
MK
412 syslog(LOG_NOTICE,
413 "\"%s\" command from %s (%s)\n",
414 c->cmdname, RealHostName,
415 inet_ntoa(RealHostAddr.sin_addr));
2e15a2d8 416# endif
ab4889ea
MK
417 /* FALL THROUGH */
418# endif /* SMTPDEBUG */
d4f42161 419
6b861048
EA
420 case CMDERROR: /* unknown command */
421 message("500", "Command unrecognized");
422 break;
423
424 default:
ab4889ea 425 errno = 0;
6b861048
EA
426 syserr("smtp: unknown code %d", c->cmdcode);
427 break;
428 }
429 }
430}
431\f/*
432** SKIPWORD -- skip a fixed word.
433**
434** Parameters:
435** p -- place to start looking.
436** w -- word to skip.
437**
438** Returns:
439** p following w.
440** NULL on error.
441**
442** Side Effects:
443** clobbers the p data area.
444*/
445
446static char *
447skipword(p, w)
448 register char *p;
449 char *w;
450{
451 register char *q;
6b861048
EA
452
453 /* find beginning of word */
454 while (isspace(*p))
455 p++;
456 q = p;
457
458 /* find end of word */
459 while (*p != '\0' && *p != ':' && !isspace(*p))
460 p++;
461 while (isspace(*p))
462 *p++ = '\0';
463 if (*p != ':')
464 {
465 syntax:
466 message("501", "Syntax error");
467 Errors++;
468 return (NULL);
469 }
470 *p++ = '\0';
471 while (isspace(*p))
472 p++;
473
474 /* see if the input word matches desired word */
ed73ef1d 475 if (strcasecmp(q, w))
6b861048
EA
476 goto syntax;
477
478 return (p);
479}
34d37b7d
EA
480\f/*
481** HELP -- implement the HELP command.
482**
483** Parameters:
484** topic -- the topic we want help for.
485**
486** Returns:
487** none.
488**
489** Side Effects:
490** outputs the help file to message output.
491*/
492
493help(topic)
494 char *topic;
495{
496 register FILE *hf;
497 int len;
498 char buf[MAXLINE];
499 bool noinfo;
500
c1e24818 501 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
34d37b7d
EA
502 {
503 /* no help */
1b932f66 504 errno = 0;
34d37b7d
EA
505 message("502", "HELP not implemented");
506 return;
507 }
508
e97afd4a
EA
509 if (topic == NULL || *topic == '\0')
510 topic = "smtp";
511 else
512 makelower(topic);
513
34d37b7d 514 len = strlen(topic);
34d37b7d
EA
515 noinfo = TRUE;
516
517 while (fgets(buf, sizeof buf, hf) != NULL)
518 {
519 if (strncmp(buf, topic, len) == 0)
520 {
521 register char *p;
522
523 p = index(buf, '\t');
524 if (p == NULL)
525 p = buf;
526 else
527 p++;
528 fixcrlf(p, TRUE);
529 message("214-", p);
530 noinfo = FALSE;
531 }
532 }
533
534 if (noinfo)
535 message("504", "HELP topic unknown");
536 else
537 message("214", "End of HELP info");
ed45aae1 538 (void) fclose(hf);
34d37b7d 539}
abae7b2d 540\f/*
e6f08ab1
EA
541** RUNINCHILD -- return twice -- once in the child, then in the parent again
542**
543** Parameters:
544** label -- a string used in error messages
545**
546** Returns:
547** zero in the child
548** one in the parent
549**
550** Side Effects:
551** none.
552*/
553
554runinchild(label)
555 char *label;
556{
557 int childpid;
558
c1a66acf 559 if (!OneXact)
e6f08ab1 560 {
c1a66acf
EA
561 childpid = dofork();
562 if (childpid < 0)
563 {
564 syserr("%s: cannot fork", label);
565 return (1);
566 }
567 if (childpid > 0)
568 {
569 auto int st;
e6f08ab1 570
c1a66acf
EA
571 /* parent -- wait for child to complete */
572 st = waitfor(childpid);
573 if (st == -1)
574 syserr("%s: lost child", label);
e6f08ab1 575
c1a66acf
EA
576 /* if we exited on a QUIT command, complete the process */
577 if (st == (EX_QUIT << 8))
578 finis();
e6f08ab1 579
c1a66acf
EA
580 return (1);
581 }
582 else
583 {
584 /* child */
585 InChild = TRUE;
57c97d4a 586 QuickAbort = FALSE;
66ba9834 587 clearenvelope(CurEnv, FALSE);
c1a66acf 588 }
e6f08ab1 589 }
55f0da62 590
c1a66acf
EA
591 /* open alias database */
592 initaliases(AliasFile, FALSE);
593
594 return (0);
e6f08ab1
EA
595}
596\f/*
abae7b2d
EA
597** PADDRTREE -- print address tree
598**
599** Used by VRFY and EXPD to dump the tree of addresses produced.
600**
601** Parameters:
602** a -- address of root.
603**
604** Returns:
605** none.
606**
607** Side Effects:
608** prints the tree in a nice order.
609*/
610
611paddrtree(a)
612 register ADDRESS *a;
613{
614 static ADDRESS *prev;
615 static int lev;
616
617 if (a == NULL)
618 return;
619 lev++;
620 if (!bitset(QDONTSEND, a->q_flags))
621 {
622 if (prev != NULL)
623 {
624 if (prev->q_fullname != NULL)
625 message("250-", "%s <%s>", prev->q_fullname, prev->q_paddr);
626 else
627 message("250-", "<%s>", prev->q_paddr);
628 }
629 prev = a;
630 }
631 paddrtree(a->q_child);
632 paddrtree(a->q_sibling);
633 if (--lev <= 0)
634 {
635 if (prev != NULL)
636 {
637 /* last one */
638 if (prev->q_fullname != NULL)
639 message("250", "%s <%s>", prev->q_fullname, prev->q_paddr);
640 else
641 message("250", "<%s>", prev->q_paddr);
642 prev = NULL;
643 }
644 else
645 message("550", "User unknown");
646 }
647}
884a20cb
EA
648
649# endif SMTP