1 # include "sendmail.h" 2 3 SCCSID(@(#)readcf.c 3.27 06/19/82); 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 r a Define mailer. n - internal name, 28 ** p - pathname, f - flags, r - rewriting 29 ** rule for sender, a - argument vector. 30 ** 31 ** Parameters: 32 ** cfname -- control file name. 33 ** safe -- set if this is a system configuration file. 34 ** Non-system configuration files can not do 35 ** certain things (e.g., leave the SUID bit on 36 ** when executing mailers). 37 ** 38 ** Returns: 39 ** none. 40 ** 41 ** Side Effects: 42 ** Builds several internal tables. 43 */ 44 45 struct rewrite *RewriteRules[10]; 46 47 48 readcf(cfname, safe) 49 char *cfname; 50 bool safe; 51 { 52 FILE *cf; 53 char buf[MAXLINE]; 54 register char *p; 55 struct rewrite *rwp = NULL; 56 extern char **prescan(); 57 extern char **copyplist(); 58 int class; 59 int ruleset = 0; 60 char exbuf[MAXLINE]; 61 char *q; 62 63 cf = fopen(cfname, "r"); 64 if (cf == NULL) 65 { 66 syserr("cannot open %s", cfname); 67 exit(EX_OSFILE); 68 } 69 70 while (fgets(buf, sizeof buf, cf) != NULL) 71 { 72 p = rindex(buf, '\n'); 73 if (p != NULL) 74 *p = '\0'; 75 76 switch (buf[0]) 77 { 78 case '\n': 79 case '\0': 80 case ' ': 81 case '\t': 82 case '#': /* comment */ 83 break; 84 85 case 'R': /* rewriting rule */ 86 for (p = &buf[1]; *p != '\0' && *p != '\t'; p++) 87 continue; 88 89 if (*p == '\0') 90 { 91 syserr("invalid rewrite line \"%s\"", buf); 92 break; 93 } 94 95 /* allocate space for the rule header */ 96 if (rwp == NULL) 97 { 98 RewriteRules[ruleset] = rwp = 99 (struct rewrite *) xalloc(sizeof *rwp); 100 } 101 else 102 { 103 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 104 rwp = rwp->r_next; 105 } 106 rwp->r_next = NULL; 107 108 /* expand and save the LHS */ 109 *p = '\0'; 110 expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv); 111 rwp->r_lhs = prescan(exbuf, '\t'); 112 if (rwp->r_lhs != NULL) 113 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 114 115 /* expand and save the RHS */ 116 while (*++p == '\t') 117 continue; 118 q = p; 119 while (*p != '\0' && *p != '\t') 120 p++; 121 *p = '\0'; 122 expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv); 123 rwp->r_rhs = prescan(exbuf, '\t'); 124 if (rwp->r_rhs != NULL) 125 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 126 break; 127 128 case 'S': /* select rewriting set */ 129 ruleset = atoi(&buf[1]); 130 rwp = NULL; 131 break; 132 133 case 'D': /* macro definition */ 134 define(buf[1], newstr(&buf[2])); 135 break; 136 137 case 'H': /* required header line */ 138 (void) chompheader(&buf[1], TRUE); 139 break; 140 141 case 'C': /* word class */ 142 case 'F': /* word class from file */ 143 class = buf[1]; 144 if (!isalpha(class)) 145 goto badline; 146 if (isupper(class)) 147 class -= 'A'; 148 else 149 class -= 'a'; 150 151 /* read list of words from argument or file */ 152 if (buf[0] == 'F') 153 { 154 /* read from file */ 155 for (p = &buf[2]; *p != '\0' && !isspace(*p); p++) 156 continue; 157 if (*p == '\0') 158 p = "%s"; 159 else 160 { 161 *p = '\0'; 162 while (isspace(*++p)) 163 continue; 164 } 165 fileclass(class, &buf[2], p); 166 break; 167 } 168 169 /* scan the list of words and set class for all */ 170 for (p = &buf[2]; *p != '\0'; ) 171 { 172 register char *wd; 173 char delim; 174 register STAB *s; 175 176 while (*p != '\0' && isspace(*p)) 177 p++; 178 wd = p; 179 while (*p != '\0' && !isspace(*p)) 180 p++; 181 delim = *p; 182 *p = '\0'; 183 if (wd[0] != '\0') 184 { 185 s = stab(wd, ST_CLASS, ST_ENTER); 186 s->s_class |= 1L << class; 187 } 188 *p = delim; 189 } 190 break; 191 192 case 'M': /* define mailer */ 193 makemailer(&buf[1], safe); 194 break; 195 196 default: 197 badline: 198 syserr("unknown control line \"%s\"", buf); 199 } 200 } 201 } 202 /* 203 ** FILECLASS -- read members of a class from a file 204 ** 205 ** Parameters: 206 ** class -- class to define. 207 ** filename -- name of file to read. 208 ** fmt -- scanf string to use for match. 209 ** 210 ** Returns: 211 ** none 212 ** 213 ** Side Effects: 214 ** 215 ** puts all lines in filename that match a scanf into 216 ** the named class. 217 */ 218 219 fileclass(class, filename, fmt) 220 int class; 221 char *filename; 222 char *fmt; 223 { 224 register FILE *f; 225 char buf[MAXLINE]; 226 227 f = fopen(filename, "r"); 228 if (f == NULL) 229 { 230 syserr("cannot open %s", filename); 231 return; 232 } 233 234 while (fgets(buf, sizeof buf, f) != NULL) 235 { 236 register STAB *s; 237 char wordbuf[MAXNAME+1]; 238 239 if (sscanf(buf, fmt, wordbuf) != 1) 240 continue; 241 s = stab(wordbuf, ST_CLASS, ST_ENTER); 242 s->s_class |= 1L << class; 243 } 244 245 (void) fclose(f); 246 } 247 /* 248 ** MAKEMAILER -- define a new mailer. 249 ** 250 ** Parameters: 251 ** line -- description of mailer. This is in tokens 252 ** separated by white space. The fields are: 253 ** * the name of the mailer, as refered to 254 ** in the rewriting rules. 255 ** * the pathname of the program to fork to 256 ** execute it. 257 ** * the options needed by this program. 258 ** * the macro string needed to translate 259 ** a local "from" name to one that can be 260 ** returned to this machine. 261 ** * the argument vector (a series of parameters). 262 ** safe -- set if this is a safe configuration file. 263 ** 264 ** Returns: 265 ** none. 266 ** 267 ** Side Effects: 268 ** enters the mailer into the mailer table. 269 */ 270 271 # define SETWORD \ 272 { \ 273 while (*p != '\0' && isspace(*p)) \ 274 p++; \ 275 q = p; \ 276 while (*p != '\0' && !isspace(*p)) \ 277 p++; \ 278 if (*p != '\0') \ 279 *p++ = '\0'; \ 280 } 281 282 makemailer(line, safe) 283 char *line; 284 bool safe; 285 { 286 register char *p; 287 register char *q; 288 char *mname; 289 char *mpath; 290 u_long mopts; 291 extern u_long mfencode(); 292 char *mfrom; 293 register struct mailer *m; 294 char *margv[MAXPV + 1]; 295 register int i; 296 extern int NextMailer; 297 STAB *s; 298 299 if (NextMailer >= MAXMAILERS) 300 { 301 syserr("Too many mailers defined"); 302 return; 303 } 304 305 /* collect initial information */ 306 p = line; 307 SETWORD; 308 mname = q; 309 SETWORD; 310 mpath = q; 311 SETWORD; 312 mopts = mfencode(q); 313 if (!safe) 314 mopts &= ~M_RESTR; 315 SETWORD; 316 mfrom = q; 317 318 if (*p == '\0') 319 { 320 syserr("invalid M line in configuration file"); 321 return; 322 } 323 324 /* allocate a mailer */ 325 m = (struct mailer *) xalloc(sizeof *m); 326 m->m_name = newstr(mname); 327 m->m_mailer = newstr(mpath); 328 m->m_flags = mopts; 329 m->m_from = newstr(mfrom); 330 m->m_badstat = EX_UNAVAILABLE; 331 m->m_mno = NextMailer; 332 Mailer[NextMailer++] = m; 333 334 /* collect the argument vector */ 335 for (i = 0; i < MAXPV - 1 && *p != '\0'; i++) 336 { 337 SETWORD; 338 margv[i] = newstr(q); 339 } 340 margv[i++] = NULL; 341 342 /* save the argv */ 343 m->m_argv = (char **) xalloc(sizeof margv[0] * i); 344 bmove((char *) margv, (char *) m->m_argv, sizeof margv[0] * i); 345 s = stab(m->m_name, ST_MAILER, ST_ENTER); 346 s->s_mailer = m; 347 } 348 /* 349 ** PRINTRULES -- print rewrite rules (for debugging) 350 ** 351 ** Parameters: 352 ** none. 353 ** 354 ** Returns: 355 ** none. 356 ** 357 ** Side Effects: 358 ** prints rewrite rules. 359 */ 360 361 # ifdef DEBUG 362 363 printrules() 364 { 365 register struct rewrite *rwp; 366 register int ruleset; 367 368 for (ruleset = 0; ruleset < 10; ruleset++) 369 { 370 if (RewriteRules[ruleset] == NULL) 371 continue; 372 printf("\n----Rule Set %d:\n", ruleset); 373 374 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 375 { 376 register char **av; 377 378 printf("\n"); 379 for (av = rwp->r_lhs; *av != NULL; av++) 380 { 381 xputs(*av); 382 putchar('_'); 383 } 384 printf("\n\t"); 385 for (av = rwp->r_rhs; *av != NULL; av++) 386 { 387 xputs(*av); 388 putchar('_'); 389 } 390 printf("\n"); 391 } 392 } 393 } 394 395 # endif DEBUG 396 /* 397 ** MFENCODE -- crack mailer options 398 ** 399 ** These options modify the functioning of the mailer 400 ** from the configuration table. 401 ** 402 ** Parameters: 403 ** p -- pointer to vector of options. 404 ** 405 ** Returns: 406 ** option list in binary. 407 ** 408 ** Side Effects: 409 ** none. 410 */ 411 412 struct optlist 413 { 414 char opt_name; /* external name of option */ 415 u_long opt_value; /* internal name of option */ 416 }; 417 struct optlist OptList[] = 418 { 419 'f', M_FOPT, 420 'r', M_ROPT, 421 'q', M_QUIET, 422 'S', M_RESTR, 423 'n', M_NHDR, 424 'l', M_LOCAL, 425 's', M_STRIPQ, 426 'm', M_MUSER, 427 'F', M_NEEDFROM, 428 'D', M_NEEDDATE, 429 'M', M_MSGID, 430 'u', M_USR_UPPER, 431 'h', M_HST_UPPER, 432 'x', M_FULLNAME, 433 'A', M_ARPAFMT, 434 'U', M_UGLYUUCP, 435 'e', M_EXPENSIVE, 436 'R', M_RELRCPT, 437 'X', M_FULLSMTP, 438 '\0', 0 439 }; 440 441 u_long 442 mfencode(p) 443 register char *p; 444 { 445 register struct optlist *o; 446 register u_long opts = 0; 447 448 while (*p != '\0') 449 { 450 for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++) 451 continue; 452 if (o->opt_name == '\0') 453 syserr("bad mailer option %c", *p); 454 opts |= o->opt_value; 455 p++; 456 } 457 return (opts); 458 } 459 /* 460 ** MFDECODE -- decode mailer flags into external form. 461 ** 462 ** Parameters: 463 ** flags -- value of flags to decode. 464 ** f -- file to write them onto. 465 ** 466 ** Returns: 467 ** none. 468 ** 469 ** Side Effects: 470 ** none. 471 */ 472 473 mfdecode(flags, f) 474 u_long flags; 475 FILE *f; 476 { 477 register struct optlist *o; 478 479 putc('?', f); 480 for (o = OptList; o->opt_name != '\0'; o++) 481 { 482 if ((o->opt_value & flags) == o->opt_value) 483 { 484 flags &= ~o->opt_value; 485 putc(o->opt_name, f); 486 } 487 } 488 putc('?', f); 489 } 490