"Fossies" - the Fresh Open Source Software Archive

Member "leafnode-1.12.0/configutil.c" (28 Dec 2021, 22255 Bytes) of package /linux/misc/leafnode-1.12.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "configutil.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.11.12_vs_1.12.0.

    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 - 2021 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
  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 */
  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
  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         pcre2_code_8 *re = gs_compile((unsigned char *)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 *
  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
  686 freeserver(struct server *a) {
  687     if (a->group_pcre) pcre2_code_free_8(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 */
  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
  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 
  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 
  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 
  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 }