1 /*
2 libutil -- read config file
3 
4 Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
5 Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
6 22646949.
7 Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
8 and Randolf Skerka <Randolf.Skerka@gmx.de>.
9 Copyright of the modifications 1997.
10 Modified by Kent Robotti <robotti@erols.com>. Copyright of the
11 modifications 1998.
12 Modified by Markus Enzenberger <enz@cip.physik.uni-muenchen.de>.
13 Copyright of the modifications 1998.
14 Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
15 Copyright of the modifications 1998, 1999.
16 Modified and copyright of the modifications 2002 by Ralf Wildenhues
17 <ralf.wildenhues@gmx.de>.
18 Modified and copyright of the modifications 2001 - 2003 by Matthias Andree
19 <matthias.andree@gmx.de>.
20 
21 See file COPYING for restrictions on the use of this software.
22 */
23 
24 #include "leafnode.h"
25 #include "validatefqdn.h"
26 #include "ln_log.h"
27 #include "strlcpy.h"
28 
29 #include <ctype.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <limits.h>
33 #include <netdb.h>
34 #include <netinet/in.h>
35 #ifndef __LCLINT__
36 #include <arpa/inet.h>
37 #endif
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <unistd.h>
43 #include <sys/stat.h>
44 #include <sys/resource.h>
45 
46 #define COREFILESIZE 1024*1024*64
47 #define TOKENSIZE 4096
48 
49 #include "groupselect.h"
50 
51 #ifndef min
52 #define min(a,b) ((a < b) ? (a) : (b))
53 #endif
54 
55 /*
56  * misc. global variables, documented in leafnode.h
57  */
58 time_t expire = 0;
59 int expiredays = 0;
60 struct expire_entry *expire_base;
61 unsigned long artlimit = 0;
62 unsigned long initiallimit = 0;
63 long crosspostlimit = 0;
64 int create_all_links = 0;
65 int delaybody = 0;
66 int db_situ = 0;
67 int debugmode = 0;		/* if 1, log lots of stuff via syslog */
68 int maxage = 10;
69 int article_despite_filter = 0;
70 long maxlines = 0;
71 long minlines = 0;
72 unsigned long maxbytes = 0;
73 static int linebuffer = 0;	/* if 1, make stdout and stderr explicitly
74 				   line buffered, GNU libc makes them fully buffered
75 				   if redirected to files */
76 int timeout_long = 7;
77 int timeout_short = 2;
78 int timeout_active = 90;
79 int timeout_client = 15*60;	/* when newsreader is idle for this many seconds, disconnect */
80 int timeout_fetchnews = 5*60;	/* wait at most this many seconds for server replies in fetchnews */
81 int clamp_maxage = 1;           /* if 1, maxage will be lowered to
82 				 * groupexpire or expire if the
83 				 * applicable parameter is lower than
84 				 * maxage, to prevent duplicate fetches
85 				 * after a premature exit of
86 				 * fetchnews. */
87 int allowstrangers = 0;
88 char *filterfile;
89 struct server *servers = NULL;
90 int allow_8bit_headers = 0;
91 char *newsadmin;
92 unsigned long timeout_lock = 5UL;
93 
94 /** parse the line in \a l, breaking it into param and value at the "="
95  * delimiter. The right-hand side can be quoted with double quotes,
96  * inside these a backslash escapes a quote that is part of the string.
97  * \return success
98  */
99 static int parse_line(
100 	/*@unique@*/ char *l /** input, will be modified */,
101 	/*@out@*/ char *param /** output, left-hand side */,
102 	/*@out@*/ char *value /** output, right-hand side */);
103 
104 /* parse a line, destructively */
105 static int
parse_line(char * l,char * param,char * value)106 parse_line(char *l, char *param, char *value)
107 {
108     char *p, *start;
109     size_t le, len;
110     enum modes { plain, quoted } mode = plain;
111 
112     p = l;
113     /* skip leading spaces, read parameter */
114     SKIPLWS(p);
115     le = strcspn(p, "=#");
116     /* strip trailing space */
117     while(le && strchr(" \t", p[le-1])) le--;
118     len = min(le, TOKENSIZE - 1);
119     if (!len) return 0;
120     memcpy(param, p, len);
121     param[len] = '\0';
122     p += le;
123 
124     SKIPLWS(p);
125     if (*p++ != '=')
126 	return 0;
127     SKIPLWS(p);
128 
129     /* strip trailing blanks from input */
130     le = strlen(p);
131     while (le--) {
132 	if (p[le] == ' ' || p[le] == '\t')
133 	    p[le] = '\0';
134 	else
135 	    break;
136     }
137 
138     start = value;
139     /* read value */
140     for (le = 0 ; le < TOKENSIZE - 1 ; le ++) {
141 	char c = *p++;
142 	if (mode == plain) {
143 	    if (c == '#' || c == '\0') {
144 		/* strip trailing blanks */
145 		while(value > start && strchr(" \t", value[-1])) value--;
146 		break;
147 	    }
148 	    if (c == '"') { mode = quoted; continue; }
149 	    *value++ = c;
150 	} else if (mode == quoted) {
151 	    if (c == '\\') {
152 		if (*p) {
153 		    *value++ = *p++; continue;
154 		} else
155 		    return 0;
156 	    }
157 	    if (c == '\0') return 0;
158 	    if (c == '"') break;
159 	    *value++ = c;
160 	} else {
161 	    abort();
162 	}
163     }
164     *value = '\0';
165     return 1;
166 }
167 
168 /*
169 05/25/97 - T. Sweeney - Modified to read user name and password for AUTHINFO.
170                         Security questionable as password is stored in
171                         plaintext in insecure file.
172 1999-07-15 - Matthias Andree
173              Set p and q defaults to 0
174 */
175 
176 /* parses value into timeout_lock, returns 0 for success, -1 for error */
read_timeout_lock(const char * value,const char * source)177 static int read_timeout_lock(
178 	const char *value, /* input */
179 	const char *source /* where did value come from */) {
180     char *t;
181     unsigned long u;
182 
183     errno = 0;
184     u = strtoul(value, &t, 10);
185     if ((u != 0 || errno == 0)
186 	    && t > value
187 	    && (!*t || isspace((unsigned char)*t)))
188     {
189 	timeout_lock = u;
190 	if (debugmode) {
191 	    if (timeout_lock) {
192 		syslog(LOG_DEBUG,
193 			"%s: waiting %lu second%s for lockfile",
194 			source, timeout_lock, PLURAL(timeout_lock));
195 	    } else {
196 		syslog(LOG_DEBUG,
197 			"%s: waiting indefinitely for lockfile", source);
198 	    }
199 	}
200 	return 0;
201     } else {
202 	ln_log(LNLOG_SERR, LNLOG_CTOP, "error: cannot parse lockfile value \"%s\" from %s", value, source);
203 	return -1;
204     }
205 }
206 
207 int
readconfig(int logtostderr)208 readconfig(int logtostderr)
209 {
210     struct server *p = 0, *q = 0;
211     struct rlimit corelimit;
212     struct expire_entry *ent = NULL, *prev = NULL;
213     FILE *f;
214     char *l;
215     char *param, *value;
216     char s[SIZE_s + 1];
217     unsigned long curline = 0;
218 
219     artlimit = 0;
220     param = critmalloc(TOKENSIZE, "allocating space for parsing");
221     value = critmalloc(TOKENSIZE, "allocating space for parsing");
222 
223     xsnprintf(s, SIZE_s, "%s/config", sysconfdir);
224     if ((f = fopen(s, "r")) == NULL) {
225 	syslog(LOG_ERR, "cannot open %s", s);
226 	free(param);
227 	free(value);
228 	return 0;
229     }
230     while ((l = getaline(f))) {
231 	++curline;
232 	if (parse_line(l, param, value)) {
233 	    if (strcmp("username", param) == 0) {
234 		if (p) {
235 		    if (p->username != NULL)
236 			free(p->username);
237 		    p->username = critstrdup(value, "readconfig");
238 		    if (debugmode)
239 			syslog(LOG_DEBUG, "config: found username for %s",
240 			       p->name);
241 		} else
242 		    syslog(LOG_ERR, "config: no server for username %s", value);
243 	    } else if (strcmp("password", param) == 0) {
244 		if (p) {
245 		    if (p->password != NULL)
246 			free(p->password);
247 		    p->password = critstrdup(value, "readconfig");
248 		    if (debugmode)
249 			syslog(LOG_DEBUG, "config: found password for %s",
250 			       p->name);
251 		} else
252 		    syslog(LOG_ERR, "config: no server for password");
253 	    } else if (strcmp("timeout", param) == 0) {
254 		if (p) {
255 		    p->timeout = atoi(value);
256 		    if (debugmode)
257 			syslog(LOG_DEBUG, "config: timeout is %d second%s",
258 			       p->timeout, PLURAL(p->timeout));
259 		} else
260 		    syslog(LOG_ERR, "config: no server for timeout");
261 	    } else if (strcmp("allowSTRANGERS", param) == 0) {
262 		if (value && strlen(value)) {
263 		    if (atoi(value) == 42)
264 			allowstrangers = 1;
265 		    if (debugmode)
266 			syslog(LOG_DEBUG,
267 				"config: allowstrangers is %s",
268 				allowstrangers ? "set" : "unset");
269 		}
270 	    } else if (strcmp("create_all_links", param) == 0) {
271 		if (value && strlen(value)) {
272 		    create_all_links = atoi(value);
273 		    if (create_all_links && debugmode)
274 			syslog(LOG_DEBUG,
275 				"config: link articles in all groups");
276 		}
277 	    } else if (strcmp("expire", param) == 0) {
278 		int i = atoi(value);
279 		if (i >= (INT_MAX / SECONDS_PER_DAY))
280 		    i = (INT_MAX / SECONDS_PER_DAY) - 1;
281 		if (i <= 0) {
282 		    ln_log(LNLOG_SERR, LNLOG_CTOP, "config: expire must be positive, not %d, abort", i);
283 		    exit(1);
284 		}
285 		expiredays = i;
286 		expire = time(NULL) - (time_t) (SECONDS_PER_DAY * i);
287 		if (debugmode)
288 		    syslog(LOG_DEBUG, "config: expire is %d day%s", i, PLURAL(i));
289 	    } else if (strcmp("newsadmin", param) == 0) {
290 		if (debugmode)
291 		    syslog(LOG_DEBUG, "config: newsadmin is %s", value);
292 		newsadmin = critstrdup(value, "readconfig");
293 	    } else if (strcmp("filterfile", param) == 0) {
294 		if (debugmode)
295 		    syslog(LOG_DEBUG, "config: filterfile is %s", value);
296 		filterfile = critstrdup(value, "readconfig");
297 	    } else if ((strcmp("hostname", param) == 0) ||
298 		    (strcmp("fqdn", param) == 0)) {
299 		if (debugmode)
300 		    syslog(LOG_DEBUG, "config: hostname is %s", value);
301 		(void)xstrlcpy(fqdn, value, sizeof(fqdn));
302 	    } else if ((strcmp("maxcrosspost", param) == 0) ||
303 		       (strcmp("maxgroups", param) == 0)) {
304 		/* maxgroups is for compatibility with leafnode+ */
305 		crosspostlimit = strtol(value, NULL, 10);
306 		if (debugmode)
307 		    syslog(LOG_DEBUG, "config: crosspostlimit is %ld group%s",
308 			   crosspostlimit, PLURAL(crosspostlimit));
309 	    } else if (strcmp("article_despite_filter", param) == 0) {
310 		article_despite_filter = atoi(value) ? 1 : 0;
311 		if (debugmode)
312 		    syslog(LOG_DEBUG, "config: article_despite_filter is %s",
313 			   article_despite_filter ? "TRUE" : "FALSE");
314 	    } else if (strcmp("clamp_maxage", param) == 0) {
315 		clamp_maxage = atoi(value);
316 		if (debugmode)
317 		    syslog(LOG_DEBUG, "config: clamp_maxage is %s",
318 			   clamp_maxage ? "TRUE" : "FALSE");
319 	    } else if (strcmp("maxlines", param) == 0) {
320 		maxlines = strtol(value, NULL, 10);
321 		if (debugmode)
322 		    syslog(LOG_DEBUG, "config: postings have max. %ld line%s",
323 			   maxlines, PLURAL(maxlines));
324 	    } else if (strcmp("minlines", param) == 0) {
325 		minlines = strtol(value, NULL, 10);
326 		if (debugmode)
327 		    syslog(LOG_DEBUG, "config: postings have min. %ld line%s",
328 			   minlines, PLURAL(minlines));
329 	    } else if (strcmp("maxbytes", param) == 0) {
330 		maxbytes = strtoul(value, NULL, 10);
331 		if (debugmode)
332 		    syslog(LOG_DEBUG,
333 			   "config: postings have max. %lu byte%s",
334 			   maxbytes, PLURAL(maxbytes));
335 	    } else if (strcmp("linebuffer", param) == 0) {
336 		linebuffer = atoi(value);
337 		if (debugmode)
338 		    syslog(LOG_DEBUG, "config: linebuffer is %d", linebuffer);
339 	    } else if (strcmp("allow_8bit_headers", param) == 0) {
340 		allow_8bit_headers = atoi(value);
341 		if (debugmode)
342 		    syslog(LOG_DEBUG, "config: allow_8bit_headers is %d",
343 			    allow_8bit_headers);
344 	    } else if (strcmp("debugmode", param) == 0) {
345 		int d;
346 		d = atoi(value);
347 		debugmode = d > debugmode ? d : debugmode;
348 		if (debugmode)
349 		    syslog(LOG_DEBUG, "config: debugmode is %d", debugmode);
350 	    } else if (strcmp("delaybody_in_situ", param) == 0) {
351 		db_situ = atoi(value);
352 		if (debugmode)
353 		    syslog(LOG_DEBUG, "config: delaybody_in_situ is %d (default 0)",
354 			   db_situ);
355 	    } else if (strcmp("delaybody", param) == 0) {
356 		delaybody = atoi(value);
357 		if (debugmode)
358 		    syslog(LOG_DEBUG, "config: delaybody is %d (default 0)",
359 			   delaybody);
360 	    } else if (strcmp("timeout_short", param) == 0) {
361 		timeout_short = atoi(value);
362 		if (debugmode)
363 		    syslog(LOG_DEBUG, "config: timeout_short is %d day%s",
364 			   timeout_short, PLURAL(timeout_short));
365 	    } else if (strcmp("timeout_long", param) == 0) {
366 		timeout_long = atoi(value);
367 		if (debugmode)
368 		    syslog(LOG_DEBUG, "config: timeout_long is %d day%s",
369 			   timeout_long, PLURAL(timeout_long));
370 	    } else if (strcmp("timeout_active", param) == 0) {
371 		timeout_active = atoi(value);
372 		if (debugmode)
373 		    syslog(LOG_DEBUG, "config: timeout_active is %d day%s",
374 			   timeout_active, PLURAL(timeout_active));
375 	    } else if (strcmp("timeout_client", param) == 0) {
376 		timeout_client = atoi(value);
377 		if (debugmode)
378 		    syslog(LOG_DEBUG, "config: timeout_client is %d second%s",
379 			   timeout_client, PLURAL(timeout_client));
380 	    } else if (strcmp("timeout_fetchnews", param) == 0) {
381 		timeout_fetchnews = atoi(value);
382 		if (debugmode)
383 		    syslog(LOG_DEBUG, "config: timeout_fetchnews is %d second%s",
384 			   timeout_fetchnews, PLURAL(timeout_fetchnews));
385 	    } else if (strcmp("timeout_lock", param) == 0) {
386 		read_timeout_lock(value, "config");
387 	    } else if (strncmp("groupexpire", param, 11) == 0) {
388 		char *m;
389 		m = param;
390 		while (*m && !(isspace((unsigned char)*m)))
391 		    m++;
392 		while (isspace((unsigned char)*m))
393 		    m++;
394 		if (m && *m) {
395 		    time_t e, i = (time_t) atol(value);
396 		    if (i >= (INT_MAX / SECONDS_PER_DAY))
397 			i = (INT_MAX / SECONDS_PER_DAY) - 1;
398 		    if (debugmode) {
399 			if ((long)i < 0)
400 			    syslog(LOG_DEBUG,
401 			           "config: groupexpire for %s is %ld (never)",
402 				   m, (long)i);
403 			else if (i == 0) {
404 			    fprintf(stderr,
405 			       "config: groupexpire for %s is 0, which is treated as \"use the default expire\"\n", m);
406 			    syslog(LOG_INFO,
407 			       "config: groupexpire for %s is 0, which is treated as \"use the default expire\"",
408 			       m);
409 			} else
410 			    syslog(LOG_DEBUG,
411 			           "config: groupexpire for %s is %ld day%s",
412 			           m, (long)i, PLURAL(i));
413 		    }
414 		    e = time(NULL) - (time_t) (SECONDS_PER_DAY * i);
415 		    ent = (struct expire_entry *)
416 			critmalloc(sizeof(struct expire_entry), "readconfig");
417 		    ent->group = critstrdup(m, "readconfig");
418 		    ent->days = i;
419 		    ent->xtime = e;
420 		    ent->next = prev;
421 		    prev = ent;
422 		}
423 	    } else if ((strcmp("maxage", param) == 0) ||
424 		       (strcmp("maxold", param) == 0)) {
425 		/* maxold is for compatibility with leafnode+ */
426 		maxage = atoi(value);
427 #if LONG_MAX / 86400 <= INT_MAX
428 		if (maxage > LONG_MAX / 86400)
429 		{
430 		    maxage = 24854; /* 32-bit: LONG_MAX / 86400 - 1 */
431 		    ln_log(LNLOG_SWARNING, LNLOG_CTOP,
432 			    "warning: config: maxage cannot exceed %d, "
433 			    "please fix %s", maxage, s);
434 		}
435 #endif
436 		if (debugmode)
437 		    syslog(LOG_DEBUG, "config: maxage is %d", maxage);
438 	    } else if (strcmp("maxfetch", param) == 0) {
439 		artlimit = strtoul(value, NULL, 10);
440 		if (debugmode)
441 		    syslog(LOG_DEBUG, "config: maxfetch is %lu", artlimit);
442 	    } else if (strcmp("port", param) == 0) {
443 		unsigned long pp = strtoul(value, NULL, 10);
444 		if (p) {
445 		    if (pp == 0 || pp > 65535) {
446 			syslog(LOG_ERR,
447 			       "config: invalid port number for nntpport %s",
448 			       value);
449 		    } else {
450 			p->port = (unsigned int)pp;
451 			if (debugmode)
452 			    syslog(LOG_DEBUG, "config: nntpport is %u",
453 				   p->port);
454 		    }
455 		} else {
456 		    syslog(LOG_ERR, "config: no server for nntpport %s", value);
457 		}
458 	    } else if (strcmp("noactive", param) == 0) {
459 		long tmp;
460 		errno = 0;
461 		tmp = strtol(value,NULL,10);
462 		if (errno) {
463 		    syslog(LOG_ERR, "config: invalid value \"%s\" for noactive", value);
464 		} else {
465 		    if (p) {
466 			p->updateactive = !tmp;
467 			if (debugmode)
468 			    syslog(LOG_DEBUG, "config: %s active file updates for %s",
469 				    p->updateactive ? "enabled" : "no", p->name);
470 		    } else {
471 			syslog(LOG_ERR, "config: no server for noactive = %s",
472 				value);
473 		    }
474 		}
475 	    } else if (strcmp("noxover", param) == 0) {
476 		if (p) {
477 		    p->noxover = TRUE;
478 		    if (debugmode)
479 			syslog(LOG_DEBUG, "config: no XOVER for %s",
480 			       p->name);
481 		} else
482 		    syslog(LOG_ERR, "config: no server for noxover = %s", value);
483 	    } else if (strcmp("nodesc", param) == 0) {
484 		if (p) {
485 		    p->descriptions = !atoi(value);
486 		    if (debugmode)
487 			syslog(LOG_DEBUG, "config: %s LIST NEWSGROUPS for %s",
488 			       p->descriptions ? "enabled" : "no", p->name);
489 		} else
490 		    syslog(LOG_ERR, "config: no server for nodesc = %s", value);
491 	    } else if (strcmp("nopost", param) == 0) {
492 		if (p) {
493 		    p->nopost = atoi(value);
494 		    if (debugmode)
495 			syslog(LOG_DEBUG, "config: nopost for %s is %d",
496 			       p->name, p->nopost);
497 		} else
498 		    syslog(LOG_ERR, "config: no server for nopost = %s", value);
499 	    } else if (strcmp("post_anygroup", param) == 0) {
500 		if (p) {
501 		    p->post_anygroup = atoi(value);
502 		    if (debugmode)
503 			syslog(LOG_DEBUG, "config: post_anygroup for %s is %d",
504 			       p->name, p->nopost);
505 		} else
506 		    syslog(LOG_ERR, "config: no server for post_anygroup = %s", value);
507 	    } else if (strcmp("noread", param) == 0) {
508 		if (p) {
509 		    p->noread = atoi(value);
510 		    if (debugmode)
511 			syslog(LOG_DEBUG, "config: noread for %s is %d",
512 			       p->name, p->noread);
513 		} else
514 		    syslog(LOG_ERR, "config: no server for noread = %s", value);
515 	    } else if (strcmp("only_groups_match_all", param) == 0) {
516 		if (p) {
517 		    p->only_groups_match_all = atoi(value);
518 		    if (debugmode)
519 			syslog(LOG_DEBUG, "config: only_groups_match_all for %s is %d",
520 			       p->name, p->only_groups_match_all);
521 		} else
522 		    syslog(LOG_ERR, "config: no server for only_groups_match_all = %s", value);
523 	    } else if (strcmp("initialfetch", param) == 0) {
524 		initiallimit = strtoul(value, NULL, 10);
525 		if (debugmode)
526 		    syslog(LOG_DEBUG, "config: initialfetch is %lu",
527 			   initiallimit);
528 	    } else if ((strcmp("server", param) == 0) ||
529 		       (strcmp("supplement", param) == 0)) {
530 		if (debugmode)
531 		    syslog(LOG_DEBUG, "config: server is %s", value);
532 		p = (struct server *)critmalloc(sizeof(struct server),
533 						    "allocating space for server");
534 		p->name = critstrdup(value, "readconfig");
535 		p->descriptions = TRUE;
536 		p->next = NULL;
537 		p->timeout = 30;	/* default 30 seconds */
538 		p->port = 0;
539 		p->username = NULL;
540 		p->password = NULL;
541 		p->nopost = 0;
542 		p->noread = 0;
543 		p->noxover = 0;
544 		p->post_anygroup = 0;
545 		p->updateactive = TRUE;
546 		p->group_pcre = NULL;
547 		p->only_groups_match_all = 0;
548 		if (servers == NULL)
549 		    servers = p;
550 		else
551 		    q->next = p;
552 		q = p;
553 	    } else if (0 == strcmp("only_groups_pcre", param)) {
554 		pcre *re = gs_compile(value);
555 		if (!re) exit(2);
556 		if (p) {
557 		    p->group_pcre = re;
558 		    if (debugmode)
559 			syslog(LOG_DEBUG, "config: only_groups_pcre for %s is %s",
560 			       p->name, value);
561 		} else {
562 		    free(re);
563 		    syslog(LOG_ERR, "config: no server for nopost = %s", value);
564 		}
565 	    } else {
566 		ln_log(LNLOG_SERR, LNLOG_CTOP,
567 			"config: unknown line %lu: \"%s = %s\"", curline,
568 			param, value);
569 	    }
570 	} else {
571 	    size_t i;
572 	    if ((i = strspn(l, " \t")) < strlen(l) && l[i] != '#') {
573 		ln_log(LNLOG_SERR, LNLOG_CTOP,
574 			"config: malformatted line %lu: \"%s\"", curline,
575 			l);
576 	    }
577 	}
578     }
579     if (maxage != 0 && maxage > expiredays && clamp_maxage == 0) {
580 	ln_log(LNLOG_SERR, LNLOG_CTOP,
581 		"config: maxage (%d) > expire (%d). This can cause duplicate download. Please fix your configuration, maxage must not be greater than expire.",
582 		maxage, expiredays);
583 	exit(1);
584     }
585     debug = debugmode;
586 
587     if (!newsadmin) {
588 	const char t[] = NEWS_USER;
589 	newsadmin = critmalloc(strlen(fqdn) + strlen(t) + 2, "readconfig");
590 	strcpy(newsadmin, t); /* RATS: ignore */
591 	strcat(newsadmin, "@");
592 	strcat(newsadmin, fqdn); /* RATS: ignore */
593     }
594 
595     expire_base = ent;
596     fclose(f);
597     free(param);
598     free(value);
599 
600     if (servers == NULL) {
601 	syslog(LOG_ERR, "no server declaration in config file");
602 	return 0;
603     }
604     if (!expire)
605 	syslog(LOG_ERR, "no expire declaration in config file");
606 
607     /* check for duplicate server configurations */
608     {
609 	unsigned short port = 0;
610 	struct servent *sp = getservbyname("nntp", "tcp");
611 	if (sp) port = ntohs(sp->s_port);
612 
613 	for (p = servers; p ; p=p->next) {
614 	    for (q = p->next ; q ; q=q->next) {
615 		unsigned short pp = p->port, qp = q->port;
616 		if (!pp) pp = port;
617 		if (!qp) qp = port;
618 		if (!pp || !qp) {
619 		    syslog(LOG_ERR, "Cannot resolve service \"nntp\" protocol \"tcp\".");
620 		    fprintf(stderr, "Cannot resolve service \"nntp\" protocol \"tcp\".\n");
621 		    return 0;
622 		}
623 		if (pp != qp) continue;
624 		if (strcasecmp(p->name, q->name)) continue;
625 		syslog(LOG_ERR, "Duplicate definition for server %s port %u", p->name, (unsigned int)pp);
626 		fprintf(stderr, "Duplicate definition for server %s port %u\n", p->name, (unsigned int)pp);
627 		return 0;
628 	    }
629 	}
630     }
631 
632     if (debugmode > 1) {
633 	getrlimit(RLIMIT_CORE, &corelimit);
634 	corelimit.rlim_cur = COREFILESIZE;
635 	if (setrlimit(RLIMIT_CORE, &corelimit) < 0 && debugmode)
636 	    syslog(LOG_DEBUG, "Changing core file size failed: %m");
637 	corelimit.rlim_cur = 0;
638 	getrlimit(RLIMIT_CORE, &corelimit);
639 	if (debugmode)
640 	    syslog(LOG_DEBUG, "Core file size: %d", (int)corelimit.rlim_cur);
641     }
642 
643     l = getenv("LN_LOCK_TIMEOUT");
644     if (l && *l)
645 	read_timeout_lock(l, "LN_LOCK_TIMEOUT");
646 
647     if (linebuffer) {
648 	fflush(stdout);
649 	setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
650 	fflush(stderr);
651 	setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
652     }
653 
654     validatefqdn(logtostderr);
655 
656     if (debugmode && verbose) {
657 	puts("");
658 	puts("WARNING:  Make sure that syslog.conf captures news.debug logging");
659 	puts("--------  and obtain your debug output from syslog.");
660 	puts("WARNING:  The screen output below is not sufficient. Check syslog!");
661 	puts("");
662 	sleep(3);
663     }
664     return 1;
665 }
666 
667 /*
668 1997-05-27 - T. Sweeney - Find a group in the expireinfo linked list and return
669                           its expire time. Otherwise, return zero.
670 */
671 static struct expire_entry *
lookup_expireent(char * group)672 lookup_expireent(char *group)
673 {
674     struct expire_entry *a;
675 
676     a = expire_base;
677     while (a != NULL) {
678 	if (ngmatch(a->group, group) == 0)
679 	    return a;
680 	a = a->next;
681     }
682     return NULL;
683 }
684 
685 static void
freeserver(struct server * a)686 freeserver(struct server *a) {
687     if (a->group_pcre) pcre_free(a->group_pcre);
688     if (a->name) free(a->name);
689     if (a->username) free(a->username);
690     if (a->password) free(a->password);
691     free(a);
692 }
693 
694 void /* exported for exclusive use in nntpd.c */
freeservers(void)695 freeservers(void) {
696     struct server *i = servers, *n;
697 
698     while(i != NULL) {
699 	n = i->next;
700 	freeserver(i);
701 	i = n;
702     }
703     servers = NULL;
704 }
705 
706 
707 void
freeexpire(void)708 freeexpire(void)
709 {
710     struct expire_entry *a, *b;
711 
712     a = expire_base;
713     while(a)
714     {
715 	b = a->next;
716 	free(a->group);
717 	free(a);
718 	a = b;
719     }
720 }
721 
freeconfig(void)722 void freeconfig(void) {
723     freeservers();
724     if (newsadmin)
725 	free(newsadmin);
726     freefilter();
727     if (filterfile)
728 	free(filterfile);
729     freegetaline();
730     freeexpire();
731     (void)lookup(LOOKUP_FREE);
732     freelastreply();
733 }
734 
lookup_expire(char * group)735 time_t lookup_expire(char *group)
736 {
737     struct expire_entry *e;
738     e = lookup_expireent(group);
739     if (e) return e->xtime;
740     return 0;
741 }
742 
lookup_expiredays(char * group)743 int lookup_expiredays(char *group)
744 {
745     struct expire_entry *e;
746     e = lookup_expireent(group);
747     if (e) return e->days;
748     return 0;
749 }
750