ignore -t in -bd mode
[unix-history] / usr / src / usr.sbin / sendmail / src / main.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1983, 1995 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#ifndef lint
10static char copyright[] =
11"@(#) Copyright (c) 1988, 1993\n\
12 The Regents of the University of California. All rights reserved.\n";
13#endif /* not lint */
14
15#ifndef lint
16static char sccsid[] = "@(#)main.c 8.123 (Berkeley) %G%";
17#endif /* not lint */
18
19#define _DEFINE
20
21#include "sendmail.h"
22#if NAMED_BIND
23#include <resolv.h>
24#endif
25
26# ifdef lint
27char edata, end;
28# endif /* lint */
29
30/*
31** SENDMAIL -- Post mail to a set of destinations.
32**
33** This is the basic mail router. All user mail programs should
34** call this routine to actually deliver mail. Sendmail in
35** turn calls a bunch of mail servers that do the real work of
36** delivering the mail.
37**
38** Sendmail is driven by tables read in from /usr/lib/sendmail.cf
39** (read by readcf.c). Some more static configuration info,
40** including some code that you may want to tailor for your
41** installation, is in conf.c. You may also want to touch
42** daemon.c (if you have some other IPC mechanism), acct.c
43** (to change your accounting), names.c (to adjust the name
44** server mechanism).
45**
46** Usage:
47** /usr/lib/sendmail [flags] addr ...
48**
49** See the associated documentation for details.
50**
51** Author:
52** Eric Allman, UCB/INGRES (until 10/81)
53** Britton-Lee, Inc., purveyors of fine
54** database computers (from 11/81)
55** Now back at UCB at the Mammoth project.
56** The support of the INGRES Project and Britton-Lee is
57** gratefully acknowledged. Britton-Lee in
58** particular had absolutely nothing to gain from
59** my involvement in this project.
60*/
61
62
63int NextMailer; /* "free" index into Mailer struct */
64char *FullName; /* sender's full name */
65ENVELOPE BlankEnvelope; /* a "blank" envelope */
66ENVELOPE MainEnvelope; /* the envelope around the basic letter */
67ADDRESS NullAddress = /* a null address */
68 { "", "", NULL, "" };
69char *CommandLineArgs; /* command line args for pid file */
70bool Warn_Q_option = FALSE; /* warn about Q option use */
71char **SaveArgv; /* argument vector for re-execing */
72
73/*
74** Pointers for setproctitle.
75** This allows "ps" listings to give more useful information.
76*/
77
78char **Argv = NULL; /* pointer to argument vector */
79char *LastArgv = NULL; /* end of argv */
80
81static void obsolete();
82
83#ifdef DAEMON
84#ifndef SMTP
85ERROR %%%% Cannot have daemon mode without SMTP %%%% ERROR
86#endif /* SMTP */
87#endif /* DAEMON */
88
89#define MAXCONFIGLEVEL 6 /* highest config version level known */
90
91int
92main(argc, argv, envp)
93 int argc;
94 char **argv;
95 char **envp;
96{
97 register char *p;
98 char **av;
99 char *locname;
100 extern char Version[];
101 char *ep, *from;
102 typedef int (*fnptr)();
103 STAB *st;
104 register int i;
105 int j;
106 bool queuemode = FALSE; /* process queue requests */
107 bool safecf = TRUE;
108 bool warn_C_flag = FALSE;
109 char warn_f_flag = '\0';
110 static bool reenter = FALSE;
111 struct passwd *pw;
112 struct stat stb;
113 struct hostent *hp;
114 char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
115 static char rnamebuf[MAXNAME]; /* holds RealUserName */
116 extern int DtableSize;
117 extern int optind;
118 extern time_t convtime();
119 extern void intsig();
120 extern struct hostent *myhostname();
121 extern char *arpadate();
122 extern char *getauthinfo();
123 extern char *getcfname();
124 extern char *optarg;
125 extern char **environ;
126 extern void sigusr1();
127 extern void sighup();
128 extern void initmacros __P((ENVELOPE *));
129
130 /*
131 ** Check to see if we reentered.
132 ** This would normally happen if e_putheader or e_putbody
133 ** were NULL when invoked.
134 */
135
136 if (reenter)
137 {
138 syserr("main: reentered!");
139 abort();
140 }
141 reenter = TRUE;
142 extern ADDRESS *recipient();
143 bool canrename;
144
145 /* do machine-dependent initializations */
146 init_md(argc, argv);
147
148#ifdef SIGUSR1
149 /* arrange to dump state on user-1 signal */
150 setsignal(SIGUSR1, sigusr1);
151#endif
152
153 /* in 4.4BSD, the table can be huge; impose a reasonable limit */
154 DtableSize = getdtsize();
155 if (DtableSize > 256)
156 DtableSize = 256;
157
158 /*
159 ** Be sure we have enough file descriptors.
160 ** But also be sure that 0, 1, & 2 are open.
161 */
162
163 i = open("/dev/null", O_RDWR, 0);
164 if (fstat(STDIN_FILENO, &stb) < 0 && errno != EOPNOTSUPP)
165 (void) dup2(i, STDIN_FILENO);
166 if (fstat(STDOUT_FILENO, &stb) < 0 && errno != EOPNOTSUPP)
167 (void) dup2(i, STDOUT_FILENO);
168 if (fstat(STDERR_FILENO, &stb) < 0 && errno != EOPNOTSUPP)
169 (void) dup2(i, STDERR_FILENO);
170 if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
171 (void) close(i);
172
173 i = DtableSize;
174 while (--i > 0)
175 {
176 if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
177 (void) close(i);
178 }
179 errno = 0;
180
181#ifdef LOG
182# ifdef LOG_MAIL
183 openlog("sendmail", LOG_PID, LOG_MAIL);
184# else
185 openlog("sendmail", LOG_PID);
186# endif
187#endif
188
189 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
190
191 /* set up the blank envelope */
192 BlankEnvelope.e_puthdr = putheader;
193 BlankEnvelope.e_putbody = putbody;
194 BlankEnvelope.e_xfp = NULL;
195 STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
196 STRUCTCOPY(BlankEnvelope, MainEnvelope);
197 CurEnv = &MainEnvelope;
198
199 /*
200 ** Set default values for variables.
201 ** These cannot be in initialized data space.
202 */
203
204 setdefaults(&BlankEnvelope);
205
206 RealUid = getuid();
207 RealGid = getgid();
208
209 pw = sm_getpwuid(RealUid);
210 if (pw != NULL)
211 (void) strcpy(rnamebuf, pw->pw_name);
212 else
213 (void) sprintf(rnamebuf, "Unknown UID %d", RealUid);
214 RealUserName = rnamebuf;
215
216 /* save command line arguments */
217 i = 0;
218 for (av = argv; *av != NULL; )
219 i += strlen(*av++) + 1;
220 SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
221 CommandLineArgs = xalloc(i);
222 p = CommandLineArgs;
223 for (av = argv, i = 0; *av != NULL; )
224 {
225 SaveArgv[i++] = newstr(*av);
226 if (av != argv)
227 *p++ = ' ';
228 strcpy(p, *av++);
229 p += strlen(p);
230 }
231 SaveArgv[i] = NULL;
232
233 /* Handle any non-getoptable constructions. */
234 obsolete(argv);
235
236 /*
237 ** Do a quick prescan of the argument list.
238 */
239
240#if defined(__osf__) || defined(_AIX3)
241# define OPTIONS "B:b:C:cd:e:F:f:h:IimnO:o:p:q:r:sTtvX:x"
242#endif
243#if defined(ultrix)
244# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mnO:o:p:q:r:sTtvX:"
245#endif
246#if defined(sony_news)
247# define OPTIONS "B:b:C:cd:E:e:F:f:h:IiJ:mnO:o:p:q:r:sTtvX:"
248#endif
249#ifndef OPTIONS
250# define OPTIONS "B:b:C:cd:e:F:f:h:IimnO:o:p:q:r:sTtvX:"
251#endif
252 while ((j = getopt(argc, argv, OPTIONS)) != EOF)
253 {
254 switch (j)
255 {
256 case 'd':
257 tTflag(optarg);
258 setbuf(stdout, (char *) NULL);
259 break;
260 }
261 }
262
263 if (tTd(0, 1))
264 {
265 int ll;
266 extern char *CompileOptions[];
267
268 printf("Version %s\nCompiled with:\t", Version);
269 av = CompileOptions;
270 ll = 7;
271 while (*av != NULL)
272 {
273 if (ll + strlen(*av) > 63)
274 {
275 putchar('\n');
276 ll = 0;
277 }
278 if (ll == 0)
279 {
280 putchar('\t');
281 putchar('\t');
282 }
283 putchar(' ');
284 printf("%s", *av);
285 ll += strlen(*av++) + 1;
286 }
287 putchar('\n');
288 }
289 if (tTd(0, 10))
290 {
291 int ll;
292 extern char *OsCompileOptions[];
293
294 printf("OS Defines:\t", Version);
295 av = OsCompileOptions;
296 ll = 7;
297 while (*av != NULL)
298 {
299 if (ll + strlen(*av) > 63)
300 {
301 putchar('\n');
302 ll = 0;
303 }
304 if (ll == 0)
305 {
306 putchar('\t');
307 putchar('\t');
308 }
309 putchar(' ');
310 printf("%s", *av);
311 ll += strlen(*av++) + 1;
312 }
313 putchar('\n');
314#ifdef _PATH_UNIX
315 printf("Unix path:\t %s\n", _PATH_UNIX);
316#endif
317 printf("Config file:\t %s\n", getcfname());
318 printf("Proc Id file:\t %s\n", PidFile);
319 }
320
321 InChannel = stdin;
322 OutChannel = stdout;
323
324 /*
325 ** Move the environment so setproctitle can use the space at
326 ** the top of memory.
327 */
328
329 for (i = 0; envp[i] != NULL; i++)
330 continue;
331 environ = (char **) xalloc(sizeof (char *) * i);
332 for (i = 0; envp[i] != NULL; i++)
333 environ[i] = newstr(envp[i]);
334 environ[i] = NULL;
335
336 /* and prime the child environment */
337 setuserenv("AGENT", "sendmail");
338
339 /*
340 ** Save start and extent of argv for setproctitle.
341 */
342
343 Argv = argv;
344 if (i > 0)
345 LastArgv = envp[i - 1] + strlen(envp[i - 1]);
346 else
347 LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
348
349 if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
350 (void) setsignal(SIGINT, intsig);
351 (void) setsignal(SIGTERM, intsig);
352 (void) setsignal(SIGPIPE, SIG_IGN);
353 OldUmask = umask(022);
354 OpMode = MD_DELIVER;
355 FullName = getenv("NAME");
356
357#if NAMED_BIND
358 if (tTd(8, 8))
359 {
360 res_init();
361 {
362 res_init();
363 _res.options |= RES_DEBUG;
364 }
365 }
366#endif
367
368 errno = 0;
369 from = NULL;
370
371 /* initialize some macros, etc. */
372 initmacros(CurEnv);
373 init_vendor_macros(CurEnv);
374
375 /* version */
376 define('v', Version, CurEnv);
377
378 /* hostname */
379 hp = myhostname(jbuf, sizeof jbuf);
380 if (jbuf[0] != '\0')
381 {
382 struct utsname utsname;
383
384 if (tTd(0, 4))
385 printf("canonical name: %s\n", jbuf);
386 define('w', newstr(jbuf), CurEnv); /* must be new string */
387 define('j', newstr(jbuf), CurEnv);
388 setclass('w', jbuf);
389
390 p = strchr(jbuf, '.');
391 if (p != NULL)
392 {
393 if (p[1] != '\0')
394 {
395 define('m', newstr(&p[1]), CurEnv);
396 }
397 while (p != NULL && strchr(&p[1], '.') != NULL)
398 {
399 *p = '\0';
400 setclass('w', jbuf);
401 *p++ = '.';
402 p = strchr(p, '.');
403 }
404 }
405
406 if (uname(&utsname) >= 0)
407 p = utsname.nodename;
408 else
409 {
410 if (tTd(0, 22))
411 printf("uname failed (%s)\n", errstring(errno));
412 makelower(jbuf);
413 p = jbuf;
414 }
415 if (tTd(0, 4))
416 printf(" UUCP nodename: %s\n", p);
417 p = newstr(p);
418 define('k', p, CurEnv);
419 setclass('k', p);
420 setclass('w', p);
421 }
422 if (hp != NULL)
423 {
424 for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
425 {
426 if (tTd(0, 4))
427 printf("\ta.k.a.: %s\n", *av);
428 setclass('w', *av);
429 }
430 if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
431 {
432 register int i;
433
434 for (i = 0; hp->h_addr_list[i] != NULL; i++)
435 {
436 char ipbuf[100];
437
438 sprintf(ipbuf, "[%s]",
439 inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
440 if (tTd(0, 4))
441 printf("\ta.k.a.: %s\n", ipbuf);
442 setclass('w', ipbuf);
443 }
444 }
445 }
446
447 /* probe interfaces and locate any additional names */
448 load_if_names();
449
450 /* current time */
451 define('b', arpadate((char *) NULL), CurEnv);
452
453 /*
454 ** Find our real host name for future logging.
455 */
456
457 p = getauthinfo(STDIN_FILENO);
458 define('_', p, CurEnv);
459
460 /*
461 ** Crack argv.
462 */
463
464 av = argv;
465 p = strrchr(*av, '/');
466 if (p++ == NULL)
467 p = *av;
468 if (strcmp(p, "newaliases") == 0)
469 OpMode = MD_INITALIAS;
470 else if (strcmp(p, "mailq") == 0)
471 OpMode = MD_PRINT;
472 else if (strcmp(p, "smtpd") == 0)
473 OpMode = MD_DAEMON;
474
475 optind = 1;
476 while ((j = getopt(argc, argv, OPTIONS)) != EOF)
477 {
478 switch (j)
479 {
480 case 'b': /* operations mode */
481 switch (j = *optarg)
482 {
483 case MD_DAEMON:
484# ifdef DAEMON
485 if (RealUid != 0) {
486 usrerr("Permission denied");
487 exit (EX_USAGE);
488 }
489 (void) unsetenv("HOSTALIASES");
490# else
491 usrerr("Daemon mode not implemented");
492 ExitStat = EX_USAGE;
493 break;
494# endif /* DAEMON */
495 case MD_SMTP:
496# ifndef SMTP
497 usrerr("I don't speak SMTP");
498 ExitStat = EX_USAGE;
499 break;
500# endif /* SMTP */
501 case MD_DELIVER:
502 case MD_VERIFY:
503 case MD_TEST:
504 case MD_INITALIAS:
505 case MD_PRINT:
506 case MD_ARPAFTP:
507 OpMode = j;
508 break;
509
510 case MD_FREEZE:
511 usrerr("Frozen configurations unsupported");
512 ExitStat = EX_USAGE;
513 break;
514
515 default:
516 usrerr("Invalid operation mode %c", j);
517 ExitStat = EX_USAGE;
518 break;
519 }
520 break;
521
522 case 'B': /* body type */
523 if (strcasecmp(optarg, "7bit") == 0 ||
524 strcasecmp(optarg, "8bitmime") == 0)
525 CurEnv->e_bodytype = newstr(optarg);
526 else
527 usrerr("Illegal body type %s", optarg);
528 break;
529
530 case 'C': /* select configuration file (already done) */
531 if (RealUid != 0)
532 warn_C_flag = TRUE;
533 ConfFile = optarg;
534 (void) setgid(RealGid);
535 (void) setuid(RealUid);
536 safecf = FALSE;
537 break;
538
539 case 'd': /* debugging -- already done */
540 break;
541
542 case 'f': /* from address */
543 case 'r': /* obsolete -f flag */
544 if (from != NULL)
545 {
546 usrerr("More than one \"from\" person");
547 ExitStat = EX_USAGE;
548 break;
549 }
550 from = newstr(denlstring(optarg, TRUE, TRUE));
551 if (strcmp(RealUserName, from) != 0)
552 warn_f_flag = j;
553 break;
554
555 case 'F': /* set full name */
556 FullName = newstr(optarg);
557 break;
558
559 case 'h': /* hop count */
560 CurEnv->e_hopcount = strtol(optarg, &ep, 10);
561 if (*ep)
562 {
563 usrerr("Bad hop count (%s)", optarg);
564 ExitStat = EX_USAGE;
565 break;
566 }
567 break;
568
569 case 'n': /* don't alias */
570 NoAlias = TRUE;
571 break;
572
573 case 'o': /* set option */
574 setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
575 break;
576
577 case 'O': /* set option (long form) */
578 setoption(' ', optarg, FALSE, TRUE, CurEnv);
579 break;
580
581 case 'p': /* set protocol */
582 p = strchr(optarg, ':');
583 if (p != NULL)
584 {
585 *p++ = '\0';
586 if (*p != '\0')
587 {
588 ep = xalloc(strlen(p) + 1);
589 cleanstrcpy(ep, p, MAXNAME);
590 define('s', ep, CurEnv);
591 }
592 }
593 if (*optarg != '\0')
594 {
595 ep = xalloc(strlen(optarg) + 1);
596 cleanstrcpy(ep, optarg, MAXNAME);
597 define('r', ep, CurEnv);
598 }
599 break;
600
601 case 'q': /* run queue files at intervals */
602# ifdef QUEUE
603 (void) unsetenv("HOSTALIASES");
604 FullName = NULL;
605 queuemode = TRUE;
606 switch (optarg[0])
607 {
608 case 'I':
609 QueueLimitId = newstr(&optarg[1]);
610 break;
611
612 case 'R':
613 QueueLimitRecipient = newstr(&optarg[1]);
614 break;
615
616 case 'S':
617 QueueLimitSender = newstr(&optarg[1]);
618 break;
619
620 default:
621 QueueIntvl = convtime(optarg, 'm');
622 break;
623 }
624# else /* QUEUE */
625 usrerr("I don't know about queues");
626 ExitStat = EX_USAGE;
627# endif /* QUEUE */
628 break;
629
630 case 't': /* read recipients from message */
631 GrabTo = TRUE;
632 break;
633
634 case 'X': /* traffic log file */
635 setgid(RealGid);
636 setuid(RealUid);
637 TrafficLogFile = fopen(optarg, "a");
638 if (TrafficLogFile == NULL)
639 {
640 syserr("cannot open %s", optarg);
641 break;
642 }
643#ifdef HASSETVBUF
644 setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
645#else
646 setlinebuf(TrafficLogFile);
647#endif
648 break;
649
650 /* compatibility flags */
651 case 'c': /* connect to non-local mailers */
652 case 'i': /* don't let dot stop me */
653 case 'm': /* send to me too */
654 case 'T': /* set timeout interval */
655 case 'v': /* give blow-by-blow description */
656 setoption(j, "T", FALSE, TRUE, CurEnv);
657 break;
658
659 case 'e': /* error message disposition */
660 case 'M': /* define macro */
661 setoption(j, optarg, FALSE, TRUE, CurEnv);
662 break;
663
664 case 's': /* save From lines in headers */
665 setoption('f', "T", FALSE, TRUE, CurEnv);
666 break;
667
668# ifdef DBM
669 case 'I': /* initialize alias DBM file */
670 OpMode = MD_INITALIAS;
671 break;
672# endif /* DBM */
673
674# if defined(__osf__) || defined(_AIX3)
675 case 'x': /* random flag that OSF/1 & AIX mailx passes */
676 break;
677# endif
678# if defined(sony_news)
679 case 'E':
680 case 'J': /* ignore flags for Japanese code conversion
681 impremented on Sony NEWS */
682 break;
683# endif
684
685 default:
686 ExitStat = EX_USAGE;
687 finis();
688 break;
689 }
690 }
691 av += optind;
692
693 /*
694 ** Do basic initialization.
695 ** Read system control file.
696 ** Extract special fields for local use.
697 */
698
699#ifdef XDEBUG
700 checkfd012("before readcf");
701#endif
702 vendor_pre_defaults(CurEnv);
703 readcf(getcfname(), safecf, CurEnv);
704 vendor_post_defaults(CurEnv);
705
706 /* set up the $=m class now, after .cf has a chance to redefine $m */
707 expand("\201m", jbuf, sizeof jbuf, CurEnv);
708 setclass('m', jbuf);
709
710 if (tTd(0, 1))
711 {
712 printf("SYSTEM IDENTITY (after readcf):");
713 printf("\n\t (short domain name) $w = ");
714 xputs(macvalue('w', CurEnv));
715 printf("\n\t(canonical domain name) $j = ");
716 xputs(macvalue('j', CurEnv));
717 printf("\n\t (subdomain name) $m = ");
718 xputs(macvalue('m', CurEnv));
719 printf("\n\t (node name) $k = ");
720 xputs(macvalue('k', CurEnv));
721 printf("\n");
722 }
723
724 /*
725 ** Initialize name server if it is going to be used.
726 */
727
728#if NAMED_BIND
729 if (!bitset(RES_INIT, _res.options))
730 res_init();
731#endif
732
733 /*
734 ** Initialize name server if it is going to be used.
735 */
736
737#if NAMED_BIND
738 if (UseNameServer && !bitset(RES_INIT, _res.options))
739 res_init();
740#endif
741
742 /*
743 ** Process authorization warnings from command line.
744 */
745
746 if (warn_C_flag)
747 auth_warning(CurEnv, "Processed by %s with -C %s",
748 RealUserName, ConfFile);
749 if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
750 auth_warning(CurEnv, "%s set sender to %s using -%c",
751 RealUserName, from, warn_f_flag);
752 if (Warn_Q_option)
753 auth_warning(CurEnv, "Processed from queue %s", QueueDir);
754
755 /* supress error printing if errors mailed back or whatever */
756 if (CurEnv->e_errormode != EM_PRINT)
757 HoldErrs = TRUE;
758
759 /* Enforce use of local time (null string overrides this) */
760 if (TimeZoneSpec == NULL)
761 unsetenv("TZ");
762 else if (TimeZoneSpec[0] != '\0')
763 setuserenv("TZ", TimeZoneSpec);
764 tzset();
765
766 if (ConfigLevel > MAXCONFIGLEVEL)
767 {
768 syserr("Warning: .cf version level (%d) exceeds program functionality (%d)",
769 ConfigLevel, MAXCONFIGLEVEL);
770 }
771
772 if (MeToo)
773 BlankEnvelope.e_flags |= EF_METOO;
774
775 switch (OpMode)
776 {
777 case MD_DAEMON:
778 /* remove things that don't make sense in daemon mode */
779 FullName = NULL;
780 GrabTo = FALSE;
781
782 /* arrange to restart on hangup signal */
783 setsignal(SIGHUP, sighup);
784 break;
785
786 case MD_INITALIAS:
787 Verbose = TRUE;
788 /* fall through... */
789
790 default:
791 /* arrange to exit cleanly on hangup signal */
792 setsignal(SIGHUP, intsig);
793 break;
794 }
795
796 /* full names can't have newlines */
797 if (FullName != NULL && strchr(FullName, '\n') != NULL)
798 FullName = newstr(denlstring(FullName, TRUE, TRUE));
799
800 /* do heuristic mode adjustment */
801 if (Verbose)
802 {
803 /* turn off noconnect option */
804 setoption('c', "F", TRUE, FALSE, CurEnv);
805
806 /* turn on interactive delivery */
807 setoption('d', "", TRUE, FALSE, CurEnv);
808 }
809
810 if (ConfigLevel < 3)
811 {
812 UseErrorsTo = TRUE;
813 }
814
815 /* our name for SMTP codes */
816 expand("\201j", jbuf, sizeof jbuf, CurEnv);
817 MyHostName = jbuf;
818 if (strchr(jbuf, '.') == NULL)
819 message("WARNING: local host name (%s) is not qualified; fix $j in config file",
820 jbuf);
821
822 /* make certain that this name is part of the $=w class */
823 setclass('w', MyHostName);
824
825 /* the indices of built-in mailers */
826 st = stab("local", ST_MAILER, ST_FIND);
827 if (st == NULL)
828 syserr("No local mailer defined");
829 else
830 LocalMailer = st->s_mailer;
831
832 st = stab("prog", ST_MAILER, ST_FIND);
833 if (st == NULL)
834 syserr("No prog mailer defined");
835 else
836 {
837 ProgMailer = st->s_mailer;
838 clrbitn(M_MUSER, ProgMailer->m_flags);
839 }
840
841 st = stab("*file*", ST_MAILER, ST_FIND);
842 if (st == NULL)
843 syserr("No *file* mailer defined");
844 else
845 {
846 FileMailer = st->s_mailer;
847 clrbitn(M_MUSER, FileMailer->m_flags);
848 }
849
850 st = stab("*include*", ST_MAILER, ST_FIND);
851 if (st == NULL)
852 syserr("No *include* mailer defined");
853 else
854 InclMailer = st->s_mailer;
855
856 if (ConfigLevel < 6)
857 {
858 /* heuristic tweaking of local mailer for back compat */
859 if (LocalMailer != NULL)
860 {
861 setbitn(M_ALIASABLE, LocalMailer->m_flags);
862 setbitn(M_HASPWENT, LocalMailer->m_flags);
863 setbitn(M_TRYRULESET5, LocalMailer->m_flags);
864 setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
865 setbitn(M_CHECKPROG, LocalMailer->m_flags);
866 setbitn(M_CHECKFILE, LocalMailer->m_flags);
867 setbitn(M_CHECKUDB, LocalMailer->m_flags);
868 }
869 if (ProgMailer != NULL)
870 setbitn(M_RUNASRCPT, ProgMailer->m_flags);
871 if (FileMailer != NULL)
872 setbitn(M_RUNASRCPT, FileMailer->m_flags);
873
874 /* propogate some envariables into children */
875 setuserenv("ISP", NULL);
876 setuserenv("SYSTYPE", NULL);
877 }
878
879 /* MIME Content-Types that cannot be transfer encoded */
880 setclass('n', "multipart/signed");
881
882 /* MIME Content-Transfer-Encodings that can be encoded */
883 setclass('e', "7bit");
884 setclass('e', "8bit");
885 setclass('e', "binary");
886
887 /* operate in queue directory */
888 if (OpMode != MD_TEST && chdir(QueueDir) < 0)
889 {
890 syserr("cannot chdir(%s)", QueueDir);
891 ExitStat = EX_SOFTWARE;
892 }
893
894# ifdef QUEUE
895 if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
896 {
897 struct stat stbuf;
898
899 /* check to see if we own the queue directory */
900 if (stat(".", &stbuf) < 0)
901 syserr("main: cannot stat %s", QueueDir);
902 if (stbuf.st_uid != RealUid)
903 {
904 /* nope, really a botch */
905 usrerr("You do not have permission to process the queue");
906 exit (EX_NOPERM);
907 }
908 }
909# endif /* QUEUE */
910
911 /* if we've had errors so far, exit now */
912 if (ExitStat != EX_OK && OpMode != MD_TEST)
913 {
914 setuid(RealUid);
915 exit(ExitStat);
916 }
917
918#ifdef XDEBUG
919 checkfd012("before main() initmaps");
920#endif
921
922 /*
923 ** Do operation-mode-dependent initialization.
924 */
925
926 switch (OpMode)
927 {
928 case MD_PRINT:
929 /* print the queue */
930#ifdef QUEUE
931 dropenvelope(CurEnv);
932 printqueue();
933 setuid(RealUid);
934 exit(EX_OK);
935#else /* QUEUE */
936 usrerr("No queue to print");
937 finis();
938#endif /* QUEUE */
939
940 case MD_INITALIAS:
941 /* initialize alias database */
942 initmaps(TRUE, CurEnv);
943 setuid(RealUid);
944 exit(EX_OK);
945
946 case MD_DAEMON:
947 case MD_SMTP:
948 /* don't open alias database -- done in srvrsmtp */
949 break;
950
951 default:
952 /* open the alias database */
953 initmaps(FALSE, CurEnv);
954 break;
955 }
956
957 if (tTd(0, 15))
958 {
959 /* print configuration table (or at least part of it) */
960 if (tTd(0, 90))
961 printrules();
962 for (i = 0; i < MAXMAILERS; i++)
963 {
964 if (Mailer[i] != NULL)
965 printmailer(Mailer[i]);
966 }
967 }
968
969 /*
970 ** Switch to the main envelope.
971 */
972
973 CurEnv = newenvelope(&MainEnvelope, CurEnv);
974 MainEnvelope.e_flags = BlankEnvelope.e_flags;
975
976 /*
977 ** If test mode, read addresses from stdin and process.
978 */
979
980 if (OpMode == MD_TEST)
981 {
982 char buf[MAXLINE];
983
984 if (isatty(fileno(stdin)))
985 Verbose = TRUE;
986
987 if (Verbose)
988 {
989 printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
990 printf("Enter <ruleset> <address>\n");
991 }
992 for (;;)
993 {
994 extern void testmodeline __P((char *, ENVELOPE *));
995
996 if (Verbose)
997 printf("> ");
998 (void) fflush(stdout);
999 if (fgets(buf, sizeof buf, stdin) == NULL)
1000 finis();
1001 p = strchr(buf, '\n');
1002 if (p != NULL)
1003 *p = '\0';
1004 if (!Verbose)
1005 printf("> %s\n", buf);
1006 testmodeline(buf, CurEnv);
1007 }
1008 }
1009
1010# ifdef QUEUE
1011 /*
1012 ** If collecting stuff from the queue, go start doing that.
1013 */
1014
1015 if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
1016 {
1017 runqueue(FALSE);
1018 finis();
1019 }
1020# endif /* QUEUE */
1021
1022 /*
1023 ** If a daemon, wait for a request.
1024 ** getrequests will always return in a child.
1025 ** If we should also be processing the queue, start
1026 ** doing it in background.
1027 ** We check for any errors that might have happened
1028 ** during startup.
1029 */
1030
1031 if (OpMode == MD_DAEMON || QueueIntvl != 0)
1032 {
1033 char dtype[200];
1034
1035 if (!tTd(52, 100))
1036 {
1037 /* put us in background */
1038 i = fork();
1039 if (i < 0)
1040 syserr("daemon: cannot fork");
1041 if (i != 0)
1042 exit(0);
1043
1044 /* disconnect from our controlling tty */
1045 disconnect(2, CurEnv);
1046 }
1047
1048 dtype[0] = '\0';
1049 if (OpMode == MD_DAEMON)
1050 strcat(dtype, "+SMTP");
1051 if (QueueIntvl != 0)
1052 {
1053 strcat(dtype, "+queueing@");
1054 strcat(dtype, pintvl(QueueIntvl, TRUE));
1055 }
1056 if (tTd(0, 1))
1057 strcat(dtype, "+debugging");
1058
1059#ifdef LOG
1060 syslog(LOG_INFO, "starting daemon (%s): %s", Version, dtype + 1);
1061#endif
1062#ifdef XLA
1063 xla_create_file();
1064#endif
1065
1066# ifdef QUEUE
1067 if (queuemode)
1068 {
1069 runqueue(TRUE);
1070 if (OpMode != MD_DAEMON)
1071 for (;;)
1072 pause();
1073 }
1074# endif /* QUEUE */
1075 dropenvelope(CurEnv);
1076
1077#ifdef DAEMON
1078 getrequests();
1079
1080 /* at this point we are in a child: reset state */
1081 (void) newenvelope(CurEnv, CurEnv);
1082
1083 /*
1084 ** Get authentication data
1085 */
1086
1087 p = getauthinfo(fileno(InChannel));
1088 define('_', p, CurEnv);
1089
1090#endif /* DAEMON */
1091 }
1092
1093# ifdef SMTP
1094 /*
1095 ** If running SMTP protocol, start collecting and executing
1096 ** commands. This will never return.
1097 */
1098
1099 if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1100 smtp(CurEnv);
1101# endif /* SMTP */
1102
1103 if (OpMode == MD_VERIFY)
1104 {
1105 CurEnv->e_sendmode = SM_VERIFY;
1106 CurEnv->e_errormode = EM_QUIET;
1107 PostMasterCopy = NULL;
1108 }
1109 else
1110 {
1111 /* interactive -- all errors are global */
1112 CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
1113 }
1114
1115 /*
1116 ** Do basic system initialization and set the sender
1117 */
1118
1119 initsys(CurEnv);
1120 setsender(from, CurEnv, NULL, FALSE);
1121 if (macvalue('s', CurEnv) == NULL)
1122 define('s', RealHostName, CurEnv);
1123
1124 if (*av == NULL && !GrabTo)
1125 {
1126 CurEnv->e_flags |= EF_GLOBALERRS;
1127 usrerr("Recipient names must be specified");
1128
1129 /* collect body for UUCP return */
1130 if (OpMode != MD_VERIFY)
1131 collect(InChannel, FALSE, FALSE, NULL, CurEnv);
1132 finis();
1133 }
1134
1135 /*
1136 ** Scan argv and deliver the message to everyone.
1137 */
1138
1139 sendtoargv(av, CurEnv);
1140
1141 /* if we have had errors sofar, arrange a meaningful exit stat */
1142 if (Errors > 0 && ExitStat == EX_OK)
1143 ExitStat = EX_USAGE;
1144
1145 /*
1146 ** Read the input mail.
1147 */
1148
1149 CurEnv->e_to = NULL;
1150 if (OpMode != MD_VERIFY || GrabTo)
1151 {
1152 CurEnv->e_flags |= EF_GLOBALERRS;
1153 collect(InChannel, FALSE, FALSE, NULL, CurEnv);
1154 }
1155 errno = 0;
1156
1157 if (tTd(1, 1))
1158 printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
1159
1160 /*
1161 ** Actually send everything.
1162 ** If verifying, just ack.
1163 */
1164
1165 CurEnv->e_from.q_flags |= QDONTSEND;
1166 if (tTd(1, 5))
1167 {
1168 printf("main: QDONTSEND ");
1169 printaddr(&CurEnv->e_from, FALSE);
1170 }
1171 CurEnv->e_to = NULL;
1172 sendall(CurEnv, SM_DEFAULT);
1173
1174 /*
1175 ** All done.
1176 ** Don't send return error message if in VERIFY mode.
1177 */
1178
1179 finis();
1180}
1181\f/*
1182** FINIS -- Clean up and exit.
1183**
1184** Parameters:
1185** none
1186**
1187** Returns:
1188** never
1189**
1190** Side Effects:
1191** exits sendmail
1192*/
1193
1194void
1195finis()
1196{
1197 if (tTd(2, 1))
1198 {
1199 extern void printenvflags();
1200
1201 printf("\n====finis: stat %d e_id=%s e_flags=",
1202 ExitStat,
1203 CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
1204 printenvflags(CurEnv);
1205 }
1206 if (tTd(2, 9))
1207 printopenfds(FALSE);
1208
1209 /* clean up temp files */
1210 CurEnv->e_to = NULL;
1211 dropenvelope(CurEnv);
1212
1213 /* flush any cached connections */
1214 mci_flush(TRUE, NULL);
1215
1216# ifdef XLA
1217 /* clean up extended load average stuff */
1218 xla_all_end();
1219# endif
1220
1221 /* and exit */
1222# ifdef LOG
1223 if (LogLevel > 78)
1224 syslog(LOG_DEBUG, "finis, pid=%d", getpid());
1225# endif /* LOG */
1226 if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
1227 ExitStat = EX_OK;
1228
1229 /* reset uid for process accounting */
1230 setuid(RealUid);
1231
1232 exit(ExitStat);
1233}
1234\f/*
1235** INTSIG -- clean up on interrupt
1236**
1237** This just arranges to exit. It pessimises in that it
1238** may resend a message.
1239**
1240** Parameters:
1241** none.
1242**
1243** Returns:
1244** none.
1245**
1246** Side Effects:
1247** Unlocks the current job.
1248*/
1249
1250void
1251intsig()
1252{
1253 FileName = NULL;
1254 unlockqueue(CurEnv);
1255#ifdef XLA
1256 xla_all_end();
1257#endif
1258
1259 /* reset uid for process accounting */
1260 setuid(RealUid);
1261
1262 exit(EX_OK);
1263}
1264\f/*
1265** INITMACROS -- initialize the macro system
1266**
1267** This just involves defining some macros that are actually
1268** used internally as metasymbols to be themselves.
1269**
1270** Parameters:
1271** none.
1272**
1273** Returns:
1274** none.
1275**
1276** Side Effects:
1277** initializes several macros to be themselves.
1278*/
1279
1280struct metamac MetaMacros[] =
1281{
1282 /* LHS pattern matching characters */
1283 '*', MATCHZANY, '+', MATCHANY, '-', MATCHONE,
1284 '=', MATCHCLASS, '~', MATCHNCLASS,
1285
1286 /* these are RHS metasymbols */
1287 '#', CANONNET, '@', CANONHOST, ':', CANONUSER,
1288 '>', CALLSUBR,
1289 '{', MATCHLOOKUP, '}', MATCHELOOKUP,
1290
1291 /* the conditional operations */
1292 '?', CONDIF, '|', CONDELSE, '.', CONDFI,
1293
1294 /* the hostname lookup characters */
1295 '[', HOSTBEGIN, ']', HOSTEND,
1296 '(', LOOKUPBEGIN, ')', LOOKUPEND,
1297
1298 /* miscellaneous control characters */
1299 '&', MACRODEXPAND,
1300
1301 '\0'
1302};
1303
1304void
1305initmacros(e)
1306 register ENVELOPE *e;
1307{
1308 register struct metamac *m;
1309 char buf[5];
1310 register int c;
1311
1312 for (m = MetaMacros; m->metaname != '\0'; m++)
1313 {
1314 buf[0] = m->metaval;
1315 buf[1] = '\0';
1316 define(m->metaname, newstr(buf), e);
1317 }
1318 buf[0] = MATCHREPL;
1319 buf[2] = '\0';
1320 for (c = '0'; c <= '9'; c++)
1321 {
1322 buf[1] = c;
1323 define(c, newstr(buf), e);
1324 }
1325
1326 /* set defaults for some macros sendmail will use later */
1327 define('e', "\201j Sendmail \201v ready at \201b", e);
1328 define('l', "From \201g \201d", e);
1329 define('n', "MAILER-DAEMON", e);
1330 define('o', ".:@[]", e);
1331 define('q', "<\201g>", e);
1332}
1333\f/*
1334** DISCONNECT -- remove our connection with any foreground process
1335**
1336** Parameters:
1337** droplev -- how "deeply" we should drop the line.
1338** 0 -- ignore signals, mail back errors, make sure
1339** output goes to stdout.
1340** 1 -- also, make stdout go to transcript.
1341** 2 -- also, disconnect from controlling terminal
1342** (only for daemon mode).
1343** e -- the current envelope.
1344**
1345** Returns:
1346** none
1347**
1348** Side Effects:
1349** Trys to insure that we are immune to vagaries of
1350** the controlling tty.
1351*/
1352
1353void
1354disconnect(droplev, e)
1355 int droplev;
1356 register ENVELOPE *e;
1357{
1358 int fd;
1359
1360 if (tTd(52, 1))
1361 printf("disconnect: In %d Out %d, e=%x\n",
1362 fileno(InChannel), fileno(OutChannel), e);
1363 if (tTd(52, 100))
1364 {
1365 printf("don't\n");
1366 return;
1367 }
1368
1369 /* be sure we don't get nasty signals */
1370 (void) setsignal(SIGINT, SIG_IGN);
1371 (void) setsignal(SIGQUIT, SIG_IGN);
1372
1373 /* we can't communicate with our caller, so.... */
1374 HoldErrs = TRUE;
1375 CurEnv->e_errormode = EM_MAIL;
1376 Verbose = FALSE;
1377 DisConnected = TRUE;
1378
1379 /* all input from /dev/null */
1380 if (InChannel != stdin)
1381 {
1382 (void) fclose(InChannel);
1383 InChannel = stdin;
1384 }
1385 (void) freopen("/dev/null", "r", stdin);
1386
1387 /* output to the transcript */
1388 if (OutChannel != stdout)
1389 {
1390 (void) fclose(OutChannel);
1391 OutChannel = stdout;
1392 }
1393 if (droplev > 0)
1394 {
1395 if (e->e_xfp == NULL)
1396 fd = open("/dev/null", O_WRONLY, 0666);
1397 else
1398 fd = fileno(e->e_xfp);
1399 (void) fflush(stdout);
1400 dup2(fd, STDOUT_FILENO);
1401 dup2(fd, STDERR_FILENO);
1402 if (e->e_xfp == NULL)
1403 close(fd);
1404 }
1405
1406 /* drop our controlling TTY completely if possible */
1407 if (droplev > 1)
1408 {
1409 (void) setsid();
1410 errno = 0;
1411 }
1412
1413#ifdef XDEBUG
1414 checkfd012("disconnect");
1415#endif
1416
1417# ifdef LOG
1418 if (LogLevel > 71)
1419 syslog(LOG_DEBUG, "in background, pid=%d", getpid());
1420# endif /* LOG */
1421
1422 errno = 0;
1423}
1424
1425static void
1426obsolete(argv)
1427 char *argv[];
1428{
1429 register char *ap;
1430 register char *op;
1431
1432 while ((ap = *++argv) != NULL)
1433 {
1434 /* Return if "--" or not an option of any form. */
1435 if (ap[0] != '-' || ap[1] == '-')
1436 return;
1437
1438 /* skip over options that do have a value */
1439 op = strchr(OPTIONS, ap[1]);
1440 if (op != NULL && *++op == ':' && ap[2] == '\0' &&
1441 ap[1] != 'd' &&
1442#if defined(sony_news)
1443 ap[1] != 'E' && ap[1] != 'J' &&
1444#endif
1445 argv[1] != NULL && argv[1][0] != '-')
1446 {
1447 argv++;
1448 continue;
1449 }
1450
1451 /* If -C doesn't have an argument, use sendmail.cf. */
1452#define __DEFPATH "sendmail.cf"
1453 if (ap[1] == 'C' && ap[2] == '\0')
1454 {
1455 *argv = xalloc(sizeof(__DEFPATH) + 2);
1456 argv[0][0] = '-';
1457 argv[0][1] = 'C';
1458 (void)strcpy(&argv[0][2], __DEFPATH);
1459 }
1460
1461 /* If -q doesn't have an argument, run it once. */
1462 if (ap[1] == 'q' && ap[2] == '\0')
1463 *argv = "-q0";
1464
1465 /* if -d doesn't have an argument, use 0-99.1 */
1466 if (ap[1] == 'd' && ap[2] == '\0')
1467 *argv = "-d0-99.1";
1468
1469# if defined(sony_news)
1470 /* if -E doesn't have an argument, use -EC */
1471 if (ap[1] == 'E' && ap[2] == '\0')
1472 *argv = "-EC";
1473
1474 /* if -J doesn't have an argument, use -JJ */
1475 if (ap[1] == 'J' && ap[2] == '\0')
1476 *argv = "-JJ";
1477# endif
1478 }
1479}
1480\f/*
1481** AUTH_WARNING -- specify authorization warning
1482**
1483** Parameters:
1484** e -- the current envelope.
1485** msg -- the text of the message.
1486** args -- arguments to the message.
1487**
1488** Returns:
1489** none.
1490*/
1491
1492void
1493#ifdef __STDC__
1494auth_warning(register ENVELOPE *e, const char *msg, ...)
1495#else
1496auth_warning(e, msg, va_alist)
1497 register ENVELOPE *e;
1498 const char *msg;
1499 va_dcl
1500#endif
1501{
1502 char buf[MAXLINE];
1503 VA_LOCAL_DECL
1504
1505 if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
1506 {
1507 register char *p;
1508 static char hostbuf[48];
1509 extern struct hostent *myhostname();
1510
1511 if (hostbuf[0] == '\0')
1512 (void) myhostname(hostbuf, sizeof hostbuf);
1513
1514 (void) sprintf(buf, "%s: ", hostbuf);
1515 p = &buf[strlen(buf)];
1516 VA_START(msg);
1517 vsprintf(p, msg, ap);
1518 VA_END;
1519 addheader("X-Authentication-Warning", buf, &e->e_header);
1520#if LOG
1521 if (LogLevel > 3)
1522 syslog(LOG_INFO, "%s: Authentication-Warning: %s",
1523 e->e_id == NULL ? "[NOQUEUE]" : e->e_id, buf);
1524#endif
1525 }
1526}
1527\f/*
1528** SETUSERENV -- set an environment in the propogated environment
1529**
1530** Parameters:
1531** envar -- the name of the environment variable.
1532** value -- the value to which it should be set. If
1533** null, this is extracted from the incoming
1534** environment. If that is not set, the call
1535** to setuserenv is ignored.
1536**
1537** Returns:
1538** none.
1539*/
1540
1541void
1542setuserenv(envar, value)
1543 const char *envar;
1544 const char *value;
1545{
1546 int i;
1547 char **evp = UserEnviron;
1548 char *p;
1549
1550 if (value == NULL)
1551 {
1552 value = getenv(envar);
1553 if (value == NULL)
1554 return;
1555 }
1556
1557 i = strlen(envar);
1558 p = (char *) xalloc(strlen(value) + i + 2);
1559 strcpy(p, envar);
1560 p[i++] = '=';
1561 strcpy(&p[i], value);
1562
1563 while (*evp != NULL && strncmp(*evp, p, i) != 0)
1564 evp++;
1565 if (*evp != NULL)
1566 {
1567 *evp++ = p;
1568 }
1569 else if (evp < &UserEnviron[MAXUSERENVIRON])
1570 {
1571 *evp++ = p;
1572 *evp = NULL;
1573 }
1574
1575 /* make sure it is in our environment as well */
1576 if (putenv(p) < 0)
1577 syserr("setuserenv: putenv(%s) failed", p);
1578}
1579\f/*
1580** DUMPSTATE -- dump state
1581**
1582** For debugging.
1583*/
1584
1585void
1586dumpstate(when)
1587 char *when;
1588{
1589#ifdef LOG
1590 register char *j = macvalue('j', CurEnv);
1591
1592 syslog(LOG_DEBUG, "--- dumping state on %s: $j = %s ---",
1593 when,
1594 j == NULL ? "<NULL>" : j);
1595 if (j != NULL)
1596 {
1597 if (!wordinclass(j, 'w'))
1598 syslog(LOG_DEBUG, "*** $j not in $=w ***");
1599 }
1600 syslog(LOG_DEBUG, "--- open file descriptors: ---");
1601 printopenfds(TRUE);
1602 syslog(LOG_DEBUG, "--- connection cache: ---");
1603 mci_dump_all(TRUE);
1604 if (RewriteRules[89] != NULL)
1605 {
1606 int stat;
1607 register char **pvp;
1608 char *pv[MAXATOM + 1];
1609
1610 pv[0] = NULL;
1611 stat = rewrite(pv, 89, 0, CurEnv);
1612 syslog(LOG_DEBUG, "--- ruleset 89 returns stat %d, pv: ---",
1613 stat);
1614 for (pvp = pv; *pvp != NULL; pvp++)
1615 syslog(LOG_DEBUG, "%s", *pvp);
1616 }
1617 syslog(LOG_DEBUG, "--- end of state dump ---");
1618#endif
1619}
1620
1621
1622void
1623sigusr1()
1624{
1625 dumpstate("user signal");
1626}
1627
1628
1629void
1630sighup()
1631{
1632#ifdef LOG
1633 if (LogLevel > 3)
1634 syslog(LOG_INFO, "restarting %s on signal", SaveArgv[0]);
1635#endif
1636 execv(SaveArgv[0], (ARGV_T) SaveArgv);
1637#ifdef LOG
1638 if (LogLevel > 0)
1639 syslog(LOG_ALERT, "could not exec %s: %m", SaveArgv[0]);
1640#endif
1641 exit(EX_OSFILE);
1642}
1643\f/*
1644** TESTMODELINE -- process a test mode input line
1645**
1646** Parameters:
1647** line -- the input line.
1648** e -- the current environment.
1649*/
1650
1651void
1652testmodeline(line, e)
1653 char *line;
1654 ENVELOPE *e;
1655{
1656 register char *p;
1657 char *q;
1658 auto char *delimptr;
1659 int mid;
1660 ADDRESS a;
1661 extern bool invalidaddr();
1662 extern char *crackaddr();
1663
1664 switch (line[0])
1665 {
1666 case '#':
1667 return;
1668
1669 case '.': /* config-style settings */
1670 switch (line[1])
1671 {
1672 case 'D':
1673 mid = macid(&line[2], &delimptr);
1674 if (mid != '\0')
1675 define(mid, newstr(delimptr), e);
1676 break;
1677
1678 case 'C':
1679 setclass(line[2], &line[3]);
1680 break;
1681
1682 case 'S': /* dump rule set */
1683 {
1684 int rs;
1685 struct rewrite *rw;
1686 STAB *s;
1687
1688 if (line[2] == '\0')
1689 return;
1690 s = stab(&line[2], ST_RULESET, ST_FIND);
1691 if (s == NULL)
1692 {
1693 if (!isdigit(line[2]))
1694 return;
1695 rs = atoi(line+2);
1696 }
1697 else
1698 rs = s->s_ruleset;
1699 if (rs < 0 || rs > MAXRWSETS)
1700 return;
1701 if ((rw = RewriteRules[rs]) == NULL)
1702 return;
1703 do
1704 {
1705 char **s;
1706 putchar('R');
1707 s = rw->r_lhs;
1708 while (*s != NULL)
1709 {
1710 xputs(*s++);
1711 putchar(' ');
1712 }
1713 putchar('\t');
1714 putchar('\t');
1715 s = rw->r_rhs;
1716 while (*s != NULL)
1717 {
1718 xputs(*s++);
1719 putchar(' ');
1720 }
1721 putchar('\n');
1722 } while (rw = rw->r_next);
1723 }
1724 break;
1725
1726 default:
1727 printf("Unknown config command %s", line);
1728 break;
1729 }
1730 return;
1731
1732 case '-': /* set command-line-like opts */
1733 switch (line[1])
1734 {
1735 case 'd':
1736 tTflag(&line[2]);
1737 break;
1738
1739 default:
1740 printf("Unknown \"-\" command %s", line);
1741 break;
1742 }
1743 return;
1744
1745 case '$':
1746 mid = macid(&line[1], NULL);
1747 if (mid == '\0')
1748 return;
1749 p = macvalue(mid, e);
1750 if (p == NULL)
1751 printf("Undefined\n");
1752 else
1753 {
1754 xputs(p);
1755 printf("\n");
1756 }
1757 return;
1758
1759 case '/': /* miscellaneous commands */
1760 p = &line[strlen(line)];
1761 while (--p >= line && isascii(*p) && isspace(*p))
1762 *p = '\0';
1763 p = strpbrk(line, " \t");
1764 if (p != NULL)
1765 {
1766 while (isascii(*p) && isspace(*p))
1767 *p++ = '\0';
1768 }
1769 else
1770 p = "";
1771 if (strcasecmp(&line[1], "mx") == 0)
1772 {
1773#if NAMED_BIND
1774 /* look up MX records */
1775 int nmx;
1776 int i;
1777 auto int rcode;
1778 char *mxhosts[MAXMXHOSTS + 1];
1779
1780 nmx = getmxrr(p, mxhosts, FALSE, &rcode);
1781 printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
1782 for (i = 0; i < nmx; i++)
1783 printf("\t%s\n", mxhosts[i]);
1784#else
1785 printf("No MX code compiled in\n");
1786#endif
1787 }
1788 else if (strcasecmp(&line[1], "try") == 0)
1789 {
1790 q = crackaddr(p);
1791 printf("Cracked address = ");
1792 xputs(q);
1793 printf("\n");
1794 if (parseaddr(p, &a, RF_COPYNONE, '\0', NULL, e) == NULL)
1795 printf("Cannot parse\n");
1796 else if (a.q_host != NULL && a.q_host[0] != '\0')
1797 printf("mailer %s, host %s, user %s\n",
1798 a.q_mailer->m_name, a.q_host, a.q_user);
1799 else
1800 printf("mailer %s, user %s\n",
1801 a.q_mailer->m_name, a.q_user);
1802 }
1803 else
1804 {
1805 printf("Unknown test command %s\n", line);
1806 }
1807 return;
1808 }
1809
1810 for (p = line; isascii(*p) && isspace(*p); p++)
1811 continue;
1812 q = p;
1813 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1814 p++;
1815 if (*p == '\0')
1816 {
1817 printf("No address!\n");
1818 return;
1819 }
1820 *p = '\0';
1821 if (invalidaddr(p + 1, NULL))
1822 return;
1823 do
1824 {
1825 register char **pvp;
1826 char pvpbuf[PSBUFSIZE];
1827
1828 pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
1829 &delimptr, NULL);
1830 if (pvp == NULL)
1831 continue;
1832 p = q;
1833 while (*p != '\0')
1834 {
1835 int stat;
1836
1837 stat = rewrite(pvp, atoi(p), 0, e);
1838 if (stat != EX_OK)
1839 printf("== Ruleset %s status %d\n",
1840 p, stat);
1841 while (*p != '\0' && *p++ != ',')
1842 continue;
1843 }
1844 } while (*(p = delimptr) != '\0');
1845}