|
|
1.1 root 1: # include "sendmail.h"
2:
3: SCCSID(@(#)readcf.c 4.2 8/28/83);
4:
5: /*
6: ** READCF -- read control file.
7: **
8: ** This routine reads the control file and builds the internal
9: ** form.
10: **
11: ** The file is formatted as a sequence of lines, each taken
12: ** atomically. The first character of each line describes how
13: ** the line is to be interpreted. The lines are:
14: ** Dxval Define macro x to have value val.
15: ** Cxword Put word into class x.
16: ** Fxfile [fmt] Read file for lines to put into
17: ** class x. Use scanf string 'fmt'
18: ** or "%s" if not present. Fmt should
19: ** only produce one string-valued result.
20: ** Hname: value Define header with field-name 'name'
21: ** and value as specified; this will be
22: ** macro expanded immediately before
23: ** use.
24: ** Sn Use rewriting set n.
25: ** Rlhs rhs Rewrite addresses that match lhs to
26: ** be rhs.
27: ** Mn p f s r a Define mailer. n - internal name,
28: ** p - pathname, f - flags, s - rewriting
29: ** ruleset for sender, s - rewriting ruleset
30: ** for recipients, a - argument vector.
31: ** Oxvalue Set option x to value.
32: ** Pname=value Set precedence name to value.
33: **
34: ** Parameters:
35: ** cfname -- control file name.
36: ** safe -- set if this is a system configuration file.
37: ** Non-system configuration files can not do
38: ** certain things (e.g., leave the SUID bit on
39: ** when executing mailers).
40: **
41: ** Returns:
42: ** none.
43: **
44: ** Side Effects:
45: ** Builds several internal tables.
46: */
47:
48: readcf(cfname, safe)
49: char *cfname;
50: bool safe;
51: {
52: FILE *cf;
53: int ruleset = 0;
54: char *q;
55: char **pv;
56: struct rewrite *rwp = NULL;
57: char buf[MAXLINE];
58: register char *p;
59: extern char **prescan();
60: extern char **copyplist();
61: char exbuf[MAXLINE];
62: extern char *fgetfolded();
63: extern char *munchstring();
64:
65: cf = fopen(cfname, "r");
66: if (cf == NULL)
67: {
68: syserr("cannot open %s", cfname);
69: exit(EX_OSFILE);
70: }
71:
72: FileName = cfname;
73: LineNumber = 0;
74: while (fgetfolded(buf, sizeof buf, cf) != NULL)
75: {
76: switch (buf[0])
77: {
78: case '\0':
79: case '#': /* comment */
80: break;
81:
82: case 'R': /* rewriting rule */
83: for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
84: continue;
85:
86: if (*p == '\0')
87: {
88: syserr("invalid rewrite line \"%s\"", buf);
89: break;
90: }
91:
92: /* allocate space for the rule header */
93: if (rwp == NULL)
94: {
95: RewriteRules[ruleset] = rwp =
96: (struct rewrite *) xalloc(sizeof *rwp);
97: }
98: else
99: {
100: rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
101: rwp = rwp->r_next;
102: }
103: rwp->r_next = NULL;
104:
105: /* expand and save the LHS */
106: *p = '\0';
107: expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
108: rwp->r_lhs = prescan(exbuf, '\t');
109: if (rwp->r_lhs != NULL)
110: rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
111:
112: /* expand and save the RHS */
113: while (*++p == '\t')
114: continue;
115: q = p;
116: while (*p != '\0' && *p != '\t')
117: p++;
118: *p = '\0';
119: expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
120: rwp->r_rhs = prescan(exbuf, '\t');
121: if (rwp->r_rhs != NULL)
122: rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
123: break;
124:
125: case 'S': /* select rewriting set */
126: ruleset = atoi(&buf[1]);
127: if (ruleset >= MAXRWSETS || ruleset < 0)
128: {
129: syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
130: ruleset = 0;
131: }
132: rwp = NULL;
133: break;
134:
135: case 'D': /* macro definition */
136: define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
137: break;
138:
139: case 'H': /* required header line */
140: (void) chompheader(&buf[1], TRUE);
141: break;
142:
143: case 'C': /* word class */
144: case 'F': /* word class from file */
145: /* read list of words from argument or file */
146: if (buf[0] == 'F')
147: {
148: /* read from file */
149: for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
150: continue;
151: if (*p == '\0')
152: p = "%s";
153: else
154: {
155: *p = '\0';
156: while (isspace(*++p))
157: continue;
158: }
159: fileclass(buf[1], &buf[2], p);
160: break;
161: }
162:
163: /* scan the list of words and set class for all */
164: for (p = &buf[2]; *p != '\0'; )
165: {
166: register char *wd;
167: char delim;
168:
169: while (*p != '\0' && isspace(*p))
170: p++;
171: wd = p;
172: while (*p != '\0' && !isspace(*p))
173: p++;
174: delim = *p;
175: *p = '\0';
176: if (wd[0] != '\0')
177: setclass(buf[1], wd);
178: *p = delim;
179: }
180: break;
181:
182: case 'M': /* define mailer */
183: makemailer(&buf[1], safe);
184: break;
185:
186: case 'O': /* set option */
187: setoption(buf[1], &buf[2], safe, FALSE);
188: break;
189:
190: case 'P': /* set precedence */
191: if (NumPriorities >= MAXPRIORITIES)
192: {
193: toomany('P', MAXPRIORITIES);
194: break;
195: }
196: for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
197: continue;
198: if (*p == '\0')
199: goto badline;
200: *p = '\0';
201: Priorities[NumPriorities].pri_name = newstr(&buf[1]);
202: Priorities[NumPriorities].pri_val = atoi(++p);
203: NumPriorities++;
204: break;
205:
206: case 'T': /* trusted user(s) */
207: p = &buf[1];
208: while (*p != '\0')
209: {
210: while (isspace(*p))
211: p++;
212: q = p;
213: while (*p != '\0' && !isspace(*p))
214: p++;
215: if (*p != '\0')
216: *p++ = '\0';
217: if (*q == '\0')
218: continue;
219: for (pv = TrustedUsers; *pv != NULL; pv++)
220: continue;
221: if (pv >= &TrustedUsers[MAXTRUST])
222: {
223: toomany('T', MAXTRUST);
224: break;
225: }
226: *pv = newstr(q);
227: }
228: break;
229:
230: default:
231: badline:
232: syserr("unknown control line \"%s\"", buf);
233: }
234: }
235: FileName = NULL;
236: }
237: /*
238: ** TOOMANY -- signal too many of some option
239: **
240: ** Parameters:
241: ** id -- the id of the error line
242: ** maxcnt -- the maximum possible values
243: **
244: ** Returns:
245: ** none.
246: **
247: ** Side Effects:
248: ** gives a syserr.
249: */
250:
251: toomany(id, maxcnt)
252: char id;
253: int maxcnt;
254: {
255: syserr("too many %c lines, %d max", id, maxcnt);
256: }
257: /*
258: ** FILECLASS -- read members of a class from a file
259: **
260: ** Parameters:
261: ** class -- class to define.
262: ** filename -- name of file to read.
263: ** fmt -- scanf string to use for match.
264: **
265: ** Returns:
266: ** none
267: **
268: ** Side Effects:
269: **
270: ** puts all lines in filename that match a scanf into
271: ** the named class.
272: */
273:
274: fileclass(class, filename, fmt)
275: int class;
276: char *filename;
277: char *fmt;
278: {
279: register FILE *f;
280: char buf[MAXLINE];
281:
282: f = fopen(filename, "r");
283: if (f == NULL)
284: {
285: syserr("cannot open %s", filename);
286: return;
287: }
288:
289: while (fgets(buf, sizeof buf, f) != NULL)
290: {
291: register STAB *s;
292: char wordbuf[MAXNAME+1];
293:
294: if (sscanf(buf, fmt, wordbuf) != 1)
295: continue;
296: s = stab(wordbuf, ST_CLASS, ST_ENTER);
297: setbitn(class, s->s_class);
298: }
299:
300: (void) fclose(f);
301: }
302: /*
303: ** MAKEMAILER -- define a new mailer.
304: **
305: ** Parameters:
306: ** line -- description of mailer. This is in labeled
307: ** fields. The fields are:
308: ** P -- the path to the mailer
309: ** F -- the flags associated with the mailer
310: ** A -- the argv for this mailer
311: ** S -- the sender rewriting set
312: ** R -- the recipient rewriting set
313: ** E -- the eol string
314: ** The first word is the canonical name of the mailer.
315: ** safe -- set if this is a safe configuration file.
316: **
317: ** Returns:
318: ** none.
319: **
320: ** Side Effects:
321: ** enters the mailer into the mailer table.
322: */
323:
324: makemailer(line, safe)
325: char *line;
326: bool safe;
327: {
328: register char *p;
329: register struct mailer *m;
330: register STAB *s;
331: int i;
332: char fcode;
333: extern int NextMailer;
334: extern char **makeargv();
335: extern char *munchstring();
336: extern char *DelimChar;
337: extern long atol();
338:
339: /* allocate a mailer and set up defaults */
340: m = (struct mailer *) xalloc(sizeof *m);
341: bzero((char *) m, sizeof *m);
342: m->m_mno = NextMailer;
343: m->m_eol = "\n";
344:
345: /* collect the mailer name */
346: for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
347: continue;
348: if (*p != '\0')
349: *p++ = '\0';
350: m->m_name = newstr(line);
351:
352: /* now scan through and assign info from the fields */
353: while (*p != '\0')
354: {
355: while (*p != '\0' && (*p == ',' || isspace(*p)))
356: p++;
357:
358: /* p now points to field code */
359: fcode = *p;
360: while (*p != '\0' && *p != '=' && *p != ',')
361: p++;
362: if (*p++ != '=')
363: {
364: syserr("`=' expected");
365: return;
366: }
367: while (isspace(*p))
368: p++;
369:
370: /* p now points to the field body */
371: p = munchstring(p);
372:
373: /* install the field into the mailer struct */
374: switch (fcode)
375: {
376: case 'P': /* pathname */
377: m->m_mailer = newstr(p);
378: break;
379:
380: case 'F': /* flags */
381: for (; *p != '\0'; p++)
382: setbitn(*p, m->m_flags);
383: if (!safe)
384: clrbitn(M_RESTR, m->m_flags);
385: break;
386:
387: case 'S': /* sender rewriting ruleset */
388: case 'R': /* recipient rewriting ruleset */
389: i = atoi(p);
390: if (i < 0 || i >= MAXRWSETS)
391: {
392: syserr("invalid rewrite set, %d max", MAXRWSETS);
393: return;
394: }
395: if (fcode == 'S')
396: m->m_s_rwset = i;
397: else
398: m->m_r_rwset = i;
399: break;
400:
401: case 'E': /* end of line string */
402: m->m_eol = newstr(p);
403: break;
404:
405: case 'A': /* argument vector */
406: m->m_argv = makeargv(p);
407: break;
408:
409: case 'M': /* maximum message size */
410: m->m_maxsize = atol(p);
411: break;
412: }
413:
414: p = DelimChar;
415: }
416:
417: /* now store the mailer away */
418: if (NextMailer >= MAXMAILERS)
419: {
420: syserr("too many mailers defined (%d max)", MAXMAILERS);
421: return;
422: }
423: Mailer[NextMailer++] = m;
424: s = stab(m->m_name, ST_MAILER, ST_ENTER);
425: s->s_mailer = m;
426: }
427: /*
428: ** MUNCHSTRING -- translate a string into internal form.
429: **
430: ** Parameters:
431: ** p -- the string to munch.
432: **
433: ** Returns:
434: ** the munched string.
435: **
436: ** Side Effects:
437: ** Sets "DelimChar" to point to the string that caused us
438: ** to stop.
439: */
440:
441: char *
442: munchstring(p)
443: register char *p;
444: {
445: register char *q;
446: bool backslash = FALSE;
447: bool quotemode = FALSE;
448: static char buf[MAXLINE];
449: extern char *DelimChar;
450:
451: for (q = buf; *p != '\0'; p++)
452: {
453: if (backslash)
454: {
455: /* everything is roughly literal */
456: backslash = FALSE;
457: switch (*p)
458: {
459: case 'r': /* carriage return */
460: *q++ = '\r';
461: continue;
462:
463: case 'n': /* newline */
464: *q++ = '\n';
465: continue;
466:
467: case 'f': /* form feed */
468: *q++ = '\f';
469: continue;
470:
471: case 'b': /* backspace */
472: *q++ = '\b';
473: continue;
474: }
475: *q++ = *p;
476: }
477: else
478: {
479: if (*p == '\\')
480: backslash = TRUE;
481: else if (*p == '"')
482: quotemode = !quotemode;
483: else if (quotemode || *p != ',')
484: *q++ = *p;
485: else
486: break;
487: }
488: }
489:
490: DelimChar = p;
491: *q++ = '\0';
492: return (buf);
493: }
494: /*
495: ** MAKEARGV -- break up a string into words
496: **
497: ** Parameters:
498: ** p -- the string to break up.
499: **
500: ** Returns:
501: ** a char **argv (dynamically allocated)
502: **
503: ** Side Effects:
504: ** munges p.
505: */
506:
507: char **
508: makeargv(p)
509: register char *p;
510: {
511: char *q;
512: int i;
513: char **avp;
514: char *argv[MAXPV + 1];
515:
516: /* take apart the words */
517: i = 0;
518: while (*p != '\0' && i < MAXPV)
519: {
520: q = p;
521: while (*p != '\0' && !isspace(*p))
522: p++;
523: while (isspace(*p))
524: *p++ = '\0';
525: argv[i++] = newstr(q);
526: }
527: argv[i++] = NULL;
528:
529: /* now make a copy of the argv */
530: avp = (char **) xalloc(sizeof *avp * i);
531: bmove((char *) argv, (char *) avp, sizeof *avp * i);
532:
533: return (avp);
534: }
535: /*
536: ** PRINTRULES -- print rewrite rules (for debugging)
537: **
538: ** Parameters:
539: ** none.
540: **
541: ** Returns:
542: ** none.
543: **
544: ** Side Effects:
545: ** prints rewrite rules.
546: */
547:
548: # ifdef DEBUG
549:
550: printrules()
551: {
552: register struct rewrite *rwp;
553: register int ruleset;
554:
555: for (ruleset = 0; ruleset < 10; ruleset++)
556: {
557: if (RewriteRules[ruleset] == NULL)
558: continue;
559: printf("\n----Rule Set %d:", ruleset);
560:
561: for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
562: {
563: printf("\nLHS:");
564: printav(rwp->r_lhs);
565: printf("RHS:");
566: printav(rwp->r_rhs);
567: }
568: }
569: }
570:
571: # endif DEBUG
572: /*
573: ** SETOPTION -- set global processing option
574: **
575: ** Parameters:
576: ** opt -- option name.
577: ** val -- option value (as a text string).
578: ** safe -- if set, this came from a system configuration file.
579: ** sticky -- if set, don't let other setoptions override
580: ** this value.
581: **
582: ** Returns:
583: ** none.
584: **
585: ** Side Effects:
586: ** Sets options as implied by the arguments.
587: */
588:
589: static BITMAP StickyOpt; /* set if option is stuck */
590: extern char *WizWord; /* the stored wizard password */
591:
592: setoption(opt, val, safe, sticky)
593: char opt;
594: char *val;
595: bool safe;
596: bool sticky;
597: {
598: extern bool atobool();
599: extern time_t convtime();
600: extern int QueueLA;
601: extern int RefuseLA;
602:
603: # ifdef DEBUG
604: if (tTd(37, 1))
605: printf("setoption %c=%s", opt, val);
606: # endif DEBUG
607:
608: /*
609: ** See if this option is preset for us.
610: */
611:
612: if (bitnset(opt, StickyOpt))
613: {
614: # ifdef DEBUG
615: if (tTd(37, 1))
616: printf(" (ignored)\n");
617: # endif DEBUG
618: return;
619: }
620: #ifdef DEBUG
621: else if (tTd(37, 1))
622: printf("\n");
623: #endif DEBUG
624: if (sticky)
625: setbitn(opt, StickyOpt);
626:
627: if (getruid() == 0)
628: safe = TRUE;
629:
630: switch (opt)
631: {
632: case 'A': /* set default alias file */
633: if (val[0] == '\0')
634: AliasFile = "aliases";
635: else
636: AliasFile = newstr(val);
637: break;
638:
639: case 'a': /* look for "@:@" in alias file */
640: SafeAlias = atobool(val);
641: break;
642:
643: case 'c': /* don't connect to "expensive" mailers */
644: NoConnect = atobool(val);
645: break;
646:
647: case 'd': /* delivery mode */
648: switch (*val)
649: {
650: case '\0':
651: SendMode = SM_DELIVER;
652: break;
653:
654: case SM_QUEUE: /* queue only */
655: #ifndef QUEUE
656: syserr("need QUEUE to set -odqueue");
657: #endif QUEUE
658: /* fall through..... */
659:
660: case SM_DELIVER: /* do everything */
661: case SM_FORK: /* fork after verification */
662: SendMode = *val;
663: break;
664:
665: default:
666: syserr("Unknown delivery mode %c", *val);
667: exit(EX_USAGE);
668: }
669: break;
670:
671: case 'D': /* rebuild alias database as needed */
672: AutoRebuild = atobool(val);
673: break;
674:
675: case 'e': /* set error processing mode */
676: switch (*val)
677: {
678: case EM_QUIET: /* be silent about it */
679: case EM_MAIL: /* mail back */
680: case EM_BERKNET: /* do berknet error processing */
681: case EM_WRITE: /* write back (or mail) */
682: HoldErrs = TRUE;
683: /* fall through... */
684:
685: case EM_PRINT: /* print errors normally (default) */
686: ErrorMode = *val;
687: break;
688: }
689: break;
690:
691: case 'F': /* file mode */
692: FileMode = atooct(val);
693: break;
694:
695: case 'f': /* save Unix-style From lines on front */
696: SaveFrom = atobool(val);
697: break;
698:
699: case 'g': /* default gid */
700: if (safe)
701: DefGid = atoi(val);
702: break;
703:
704: case 'H': /* help file */
705: if (val[0] == '\0')
706: HelpFile = "sendmail.hf";
707: else
708: HelpFile = newstr(val);
709: break;
710:
711: case 'i': /* ignore dot lines in message */
712: IgnrDot = atobool(val);
713: break;
714:
715: case 'L': /* log level */
716: LogLevel = atoi(val);
717: break;
718:
719: case 'M': /* define macro */
720: define(val[0], newstr(&val[1]), CurEnv);
721: break;
722:
723: case 'm': /* send to me too */
724: MeToo = atobool(val);
725: break;
726:
727: case 'o': /* assume old style headers */
728: if (atobool(val))
729: CurEnv->e_flags |= EF_OLDSTYLE;
730: else
731: CurEnv->e_flags &= ~EF_OLDSTYLE;
732: break;
733:
734: case 'Q': /* queue directory */
735: if (val[0] == '\0')
736: QueueDir = "mqueue";
737: else
738: QueueDir = newstr(val);
739: break;
740:
741: case 'r': /* read timeout */
742: ReadTimeout = convtime(val);
743: break;
744:
745: case 'S': /* status file */
746: if (val[0] == '\0')
747: StatFile = "sendmail.st";
748: else
749: StatFile = newstr(val);
750: break;
751:
752: case 's': /* be super safe, even if expensive */
753: SuperSafe = atobool(val);
754: break;
755:
756: case 'T': /* queue timeout */
757: TimeOut = convtime(val);
758: break;
759:
760: case 't': /* time zone name */
761: # ifdef V6
762: StdTimezone = newstr(val);
763: DstTimezone = index(StdTimeZone, ',');
764: if (DstTimezone == NULL)
765: syserr("bad time zone spec");
766: else
767: *DstTimezone++ = '\0';
768: # endif V6
769: break;
770:
771: case 'u': /* set default uid */
772: if (safe)
773: DefUid = atoi(val);
774: break;
775:
776: case 'v': /* run in verbose mode */
777: Verbose = atobool(val);
778: break;
779:
780: # ifdef DEBUG
781: case 'W': /* set the wizards password */
782: if (safe)
783: WizWord = newstr(val);
784: break;
785: # endif DEBUG
786:
787: case 'x': /* load avg at which to auto-queue msgs */
788: QueueLA = atoi(val);
789: break;
790:
791: case 'X': /* load avg at which to auto-reject connections */
792: RefuseLA = atoi(val);
793: break;
794:
795: default:
796: break;
797: }
798: return;
799: }
800: /*
801: ** SETCLASS -- set a word into a class
802: **
803: ** Parameters:
804: ** class -- the class to put the word in.
805: ** word -- the word to enter
806: **
807: ** Returns:
808: ** none.
809: **
810: ** Side Effects:
811: ** puts the word into the symbol table.
812: */
813:
814: setclass(class, word)
815: int class;
816: char *word;
817: {
818: register STAB *s;
819:
820: s = stab(word, ST_CLASS, ST_ENTER);
821: setbitn(class, s->s_class);
822: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.