Logo Search packages:      
Sourcecode: leafnode version File versions

configutil.c

/*
libutil -- read config file

Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
22646949.
Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
and Randolf Skerka <Randolf.Skerka@gmx.de>.
Copyright of the modifications 1997.
Modified by Kent Robotti <robotti@erols.com>. Copyright of the
modifications 1998.
Modified by Markus Enzenberger <enz@cip.physik.uni-muenchen.de>.
Copyright of the modifications 1998.
Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
Copyright of the modifications 1998, 1999.
Modified and copyright of the modifications 2002 by Ralf Wildenhues
<ralf.wildenhues@gmx.de>.
Modified and copyright of the modifications 2001 - 2003 by Matthias Andree
<matthias.andree@web.de>.

See file COPYING for restrictions on the use of this software.
*/

#include "leafnode.h"
#include "validatefqdn.h"
#include "ln_log.h"
#include "strlcpy.h"

#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#ifndef __LCLINT__
#include <arpa/inet.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/resource.h>

#define COREFILESIZE 1024*1024*64
#define TOKENSIZE 4096

#include "groupselect.h"

#ifndef min
#define min(a,b) ((a < b) ? (a) : (b))
#endif

/*
 * misc. global variables, documented in leafnode.h
 */
time_t expire = 0;
int expiredays = 0;
struct expire_entry *expire_base;
unsigned long artlimit = 0;
unsigned long initiallimit = 0;
long crosspostlimit = 0;
int create_all_links = 0;
int delaybody = 0;
int db_situ = 0;
int debugmode = 0;            /* if 1, log lots of stuff via syslog */
int maxage = 10;
int article_despite_filter = 0;
long maxlines = 0;
long minlines = 0;
unsigned long maxbytes = 0;
static int linebuffer = 0;    /* if 1, make stdout and stderr explicitly 
                           line buffered, GNU libc makes them fully buffered
                           if redirected to files */
int timeout_long = 7;
int timeout_short = 2;
int timeout_active = 90;
int timeout_client = 15*60;   /* when newsreader is idle for this many seconds, disconnect */
int timeout_fetchnews = 5*60; /* wait at most this many seconds for server replies in fetchnews */
int clamp_maxage = 1;           /* if 1, maxage will be lowered to
                         * groupexpire or expire if the
                         * applicable parameter is lower than
                         * maxage, to prevent duplicate fetches
                         * after a premature exit of
                         * fetchnews. */
int allowstrangers = 0;
char *filterfile;
struct server *servers = NULL;
int allow_8bit_headers = 0;
char *newsadmin;
unsigned long timeout_lock = 5UL;

/** parse the line in \a l, breaking it into param and value at the "="
 * delimiter. The right-hand side can be quoted with double quotes,
 * inside these a backslash escapes a quote that is part of the string.
 * \return success
 */
static int parse_line(
      /*@unique@*/ char *l /** input, will be modified */,
      /*@out@*/ char *param /** output, left-hand side */,
      /*@out@*/ char *value /** output, right-hand side */);

/* parse a line, destructively */
static int
parse_line(char *l, char *param, char *value)
{
    char *p;
    size_t le, len;
    enum modes { plain, quoted } mode = plain;

    p = l;
    /* skip leading spaces, read parameter */
    SKIPLWS(p);
    le = strcspn(p, "=#");
    /* strip trailing space */
    while(le && strchr(" \t", p[le-1])) le--;
    len = min(le, TOKENSIZE - 1);
    if (!len) return 0;
    memcpy(param, p, len);
    param[len] = '\0';
    p += le;

    SKIPLWS(p);
    if (*p++ != '=')
      return 0;
    SKIPLWS(p);

    /* strip trailing blanks from input */
    le = strlen(p);
    while (le--) {
      if (p[le] == ' ' || p[le] == '\t')
          p[le] = '\0';
      else
          break;
    }

    /* read value */
    for (le = 0 ; le < TOKENSIZE - 1 ; le ++) {
      char c = *p++;
      if (mode == plain) {
          if (c == '#' || c == '\0') { break; }
          if (c == '"') { mode = quoted; continue; }
          *value++ = c;
      } else if (mode == quoted) {
          if (c == '\\') {
            if (*p) {
                *value++ = *p++; continue; 
            } else
                return 0;
          }
          if (c == '\0') return 0;
          if (c == '"') break;
          *value++ = c;
      } else {
          abort();
      }
    }
    *value = '\0';
    return 1;
}

/*
05/25/97 - T. Sweeney - Modified to read user name and password for AUTHINFO.
                        Security questionable as password is stored in
                        plaintext in insecure file.
1999-07-15 - Matthias Andree
             Set p and q defaults to 0
*/

/* parses value into timeout_lock, returns 0 for success, -1 for error */
static int read_timeout_lock(
      const char *value, /* input */
      const char *source /* where did value come from */) {
    char *t;
    unsigned long u;

    errno = 0;
    u = strtoul(value, &t, 10);
    if ((u != 0 || errno == 0)
          && t > value
          && (!*t || isspace((unsigned char)*t)))
    {
      timeout_lock = u;
      if (debugmode) {
          if (timeout_lock) {
            syslog(LOG_DEBUG,
                  "%s: waiting %lu second%s for lockfile",
                  source, timeout_lock, PLURAL(timeout_lock));
          } else {
            syslog(LOG_DEBUG,
                  "%s: waiting indefinitely for lockfile", source);
          }
      }
      return 0;
    } else {
      ln_log(LNLOG_SERR, LNLOG_CTOP, "error: cannot parse lockfile value \"%s\" from %s", value, source);
      return -1;
    }
}

int
readconfig(int logtostderr)
{
    struct server *p = 0, *q = 0;
    struct rlimit corelimit;
    struct expire_entry *ent = NULL, *prev = NULL;
    FILE *f;
    char *l;
    char *param, *value;
    char s[SIZE_s + 1];
    unsigned long curline = 0;

    artlimit = 0;
    param = critmalloc(TOKENSIZE, "allocating space for parsing");
    value = critmalloc(TOKENSIZE, "allocating space for parsing");

    xsnprintf(s, SIZE_s, "%s/config", sysconfdir);
    if ((f = fopen(s, "r")) == NULL) {
      syslog(LOG_ERR, "cannot open %s", s);
      free(param);
      free(value);
      return 0;
    }
    while ((l = getaline(f))) {
      ++curline;
      if (parse_line(l, param, value)) {
          if (strcmp("username", param) == 0) {
            if (p) {
                if (p->username != NULL)
                  free(p->username);
                p->username = critstrdup(value, "readconfig");
                if (debugmode)
                  syslog(LOG_DEBUG, "config: found username for %s",
                         p->name);
            } else
                syslog(LOG_ERR, "config: no server for username %s", value);
          } else if (strcmp("password", param) == 0) {
            if (p) {
                if (p->password != NULL)
                  free(p->password);
                p->password = critstrdup(value, "readconfig");
                if (debugmode)
                  syslog(LOG_DEBUG, "config: found password for %s",
                         p->name);
            } else
                syslog(LOG_ERR, "config: no server for password");
          } else if (strcmp("timeout", param) == 0) {
            if (p) {
                p->timeout = atoi(value);
                if (debugmode)
                  syslog(LOG_DEBUG, "config: timeout is %d second%s",
                         p->timeout, PLURAL(p->timeout));
            } else
                syslog(LOG_ERR, "config: no server for timeout");
          } else if (strcmp("allowSTRANGERS", param) == 0) {
            if (value && strlen(value)) {
                if (atoi(value) == 42)
                  allowstrangers = 1;
                syslog(LOG_DEBUG,
                      "config: allowstrangers is %s",
                      allowstrangers ? "set" : "unset");
            }
          } else if (strcmp("create_all_links", param) == 0) {
            if (value && strlen(value)) {
                create_all_links = atoi(value);
                if (create_all_links && debugmode)
                  syslog(LOG_DEBUG,
                        "config: link articles in all groups");
            }
          } else if (strcmp("expire", param) == 0) {
            int i = atoi(value);
            if (i >= (INT_MAX / SECONDS_PER_DAY))
                i = (INT_MAX / SECONDS_PER_DAY) - 1;
            if (i <= 0) {
                ln_log(LNLOG_SERR, LNLOG_CTOP, "config: expire must be positive, not %d, abort", i);
                exit(1);
            }
            expiredays = i;
            expire = time(NULL) - (time_t) (SECONDS_PER_DAY * i);
            if (debugmode)
                syslog(LOG_DEBUG, "config: expire is %d day%s", i, PLURAL(i));
          } else if (strcmp("newsadmin", param) == 0) {
            if (debugmode)
                syslog(LOG_DEBUG, "config: newsadmin is %s", value);
            newsadmin = critstrdup(value, "readconfig");
          } else if (strcmp("filterfile", param) == 0) {
            if (debugmode)
                syslog(LOG_DEBUG, "config: filterfile is %s", value);
            filterfile = critstrdup(value, "readconfig");
          } else if ((strcmp("hostname", param) == 0) ||
                (strcmp("fqdn", param) == 0)) {
            if (is_validfqdn(value)) {
                if (debugmode)
                  syslog(LOG_DEBUG, "config: hostname is %s", value);
                (void)xstrlcpy(fqdn, value, sizeof(fqdn));
            } else {
                ln_log(LNLOG_SCRIT, LNLOG_CTOP, "config: hostname %s is "
                      "not a valid fully-qualified domain name, exit.",
                      value);
                exit(1);
            }
          } else if ((strcmp("maxcrosspost", param) == 0) ||
                   (strcmp("maxgroups", param) == 0)) {
            /* maxgroups is for compatibility with leafnode+ */
            crosspostlimit = strtol(value, NULL, 10);
            if (debugmode)
                syslog(LOG_DEBUG, "config: crosspostlimit is %ld group%s",
                     crosspostlimit, PLURAL(crosspostlimit));
          } else if (strcmp("article_despite_filter", param) == 0) {
            article_despite_filter = atoi(value) ? 1 : 0;
            if (debugmode)
                syslog(LOG_DEBUG, "config: article_despite_filter is %s",
                     article_despite_filter ? "TRUE" : "FALSE");
          } else if (strcmp("clamp_maxage", param) == 0) {
            clamp_maxage = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: clamp_maxage is %s",
                     clamp_maxage ? "TRUE" : "FALSE");
          } else if (strcmp("maxlines", param) == 0) {
            maxlines = strtol(value, NULL, 10);
            if (debugmode)
                syslog(LOG_DEBUG, "config: postings have max. %ld line%s",
                     maxlines, PLURAL(maxlines));
          } else if (strcmp("minlines", param) == 0) {
            minlines = strtol(value, NULL, 10);
            if (debugmode)
                syslog(LOG_DEBUG, "config: postings have min. %ld line%s",
                     minlines, PLURAL(minlines));
          } else if (strcmp("maxbytes", param) == 0) {
            maxbytes = strtoul(value, NULL, 10);
            if (debugmode)
                syslog(LOG_DEBUG,
                     "config: postings have max. %lu byte%s",
                     maxbytes, PLURAL(maxbytes));
          } else if (strcmp("linebuffer", param) == 0) {
            linebuffer = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: linebuffer is %d", linebuffer);
          } else if (strcmp("allow_8bit_headers", param) == 0) {
            allow_8bit_headers = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: allow_8bit_headers is %d",
                      allow_8bit_headers);
          } else if (strcmp("debugmode", param) == 0) {
            int d;
            d = atoi(value);
            debugmode = d > debugmode ? d : debugmode;
            if (debugmode)
                syslog(LOG_DEBUG, "config: debugmode is %d", debugmode);
          } else if (strcmp("delaybody_in_situ", param) == 0) {
            db_situ = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: delaybody_in_situ is %d (default 0)",
                     db_situ);
          } else if (strcmp("delaybody", param) == 0) {
            delaybody = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: delaybody is %d (default 0)",
                     delaybody);
          } else if (strcmp("timeout_short", param) == 0) {
            timeout_short = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: timeout_short is %d day%s",
                     timeout_short, PLURAL(timeout_short));
          } else if (strcmp("timeout_long", param) == 0) {
            timeout_long = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: timeout_long is %d day%s",
                     timeout_long, PLURAL(timeout_long));
          } else if (strcmp("timeout_active", param) == 0) {
            timeout_active = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: timeout_active is %d day%s",
                     timeout_active, PLURAL(timeout_active));
          } else if (strcmp("timeout_client", param) == 0) {
            timeout_client = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: timeout_client is %d second%s",
                     timeout_client, PLURAL(timeout_client));
          } else if (strcmp("timeout_fetchnews", param) == 0) {
            timeout_fetchnews = atoi(value);
            if (debugmode)
                syslog(LOG_DEBUG, "config: timeout_fetchnews is %d second%s",
                     timeout_fetchnews, PLURAL(timeout_fetchnews));
          } else if (strcmp("timeout_lock", param) == 0) {
            read_timeout_lock(value, "config");
          } else if (strncmp("groupexpire", param, 11) == 0) {
            char *m;
            m = param;
            while (*m && !(isspace((unsigned char)*m)))
                m++;
            while (isspace((unsigned char)*m))
                m++;
            if (m && *m) {
                time_t e, i = (time_t) atol(value);
                if (i >= (INT_MAX / SECONDS_PER_DAY))
                  i = (INT_MAX / SECONDS_PER_DAY) - 1;
                if (debugmode) {
                  if ((long)i < 0)
                      syslog(LOG_DEBUG,
                             "config: groupexpire for %s is %ld (never)",
                           m, (long)i);
                  else if (i == 0) {
                      fprintf(stderr, 
                         "config: groupexpire for %s is 0, which is treated as \"use the default expire\"\n", m);
                      syslog(LOG_INFO,
                         "config: groupexpire for %s is 0, which is treated as \"use the default expire\"",
                         m);
                  } else
                      syslog(LOG_DEBUG,
                             "config: groupexpire for %s is %ld day%s",
                             m, (long)i, PLURAL(i));
                }
                e = time(NULL) - (time_t) (SECONDS_PER_DAY * i);
                ent = (struct expire_entry *)
                  critmalloc(sizeof(struct expire_entry), "readconfig");
                ent->group = critstrdup(m, "readconfig");
                ent->days = i;
                ent->xtime = e;
                ent->next = prev;
                prev = ent;
            }
          } else if ((strcmp("maxage", param) == 0) ||
                   (strcmp("maxold", param) == 0)) {
            /* maxold is for compatibility with leafnode+ */
            maxage = atoi(value);
            if (maxage > LONG_MAX / 86400) {
                maxage = 24854; /* 32-bit: LONG_MAX / 86400 - 1 */
                ln_log(LNLOG_SWARNING, LNLOG_CTOP,
                      "warning: config: maxage cannot exceed %d, "
                      "please fix %s", maxage, s);
            }
            if (debugmode)
                syslog(LOG_DEBUG, "config: maxage is %d", maxage);
          } else if (strcmp("maxfetch", param) == 0) {
            artlimit = strtoul(value, NULL, 10);
            if (debugmode)
                syslog(LOG_DEBUG, "config: maxfetch is %lu", artlimit);
          } else if (strcmp("port", param) == 0) {
            unsigned long pp = strtoul(value, NULL, 10);
            if (p) {
                if (pp == 0 || pp > 65535) {
                  syslog(LOG_ERR,
                         "config: invalid port number for nntpport %s",
                         value);
                } else {
                  p->port = (unsigned int)pp;
                  if (debugmode)
                      syslog(LOG_DEBUG, "config: nntpport is %u",
                           p->port);
                }
            } else {
                syslog(LOG_ERR, "config: no server for nntpport %s", value);
            }
          } else if (strcmp("noactive", param) == 0) {
            if (p) {
                p->updateactive = FALSE;
                if (debugmode)
                  syslog(LOG_DEBUG, "config: no active file updates for %s",
                         p->name);
            } else 
                syslog(LOG_ERR, "config: no server for noactive = %s", 
                     value);
          } else if (strcmp("noxover", param) == 0) {
            if (p) {
                p->noxover = TRUE;
                if (debugmode)
                  syslog(LOG_DEBUG, "config: no XOVER for %s",
                         p->name);
            } else
                syslog(LOG_ERR, "config: no server for noxover = %s", value);
          } else if (strcmp("nodesc", param) == 0) {
            if (p) {
                p->descriptions = FALSE;
                if (debugmode)
                  syslog(LOG_DEBUG, "config: no LIST NEWSGROUPS for %s",
                         p->name);
            } else
                syslog(LOG_ERR, "config: no server for nodesc = %s", value);
          } else if (strcmp("nopost", param) == 0) {
            if (p) {
                p->nopost = atoi(value);
                if (debugmode)
                  syslog(LOG_DEBUG, "config: nopost for %s is %d",
                         p->name, p->nopost);
            } else
                syslog(LOG_ERR, "config: no server for nopost = %s", value);
          } else if (strcmp("post_anygroup", param) == 0) {
            if (p) {
                p->post_anygroup = atoi(value);
                if (debugmode)
                  syslog(LOG_DEBUG, "config: post_anygroup for %s is %d",
                         p->name, p->nopost);
            } else
                syslog(LOG_ERR, "config: no server for post_anygroup = %s", value);
          } else if (strcmp("noread", param) == 0) {
            if (p) {
                p->noread = atoi(value);
                if (debugmode)
                  syslog(LOG_DEBUG, "config: noread for %s is %d",
                         p->name, p->noread);
            } else
                syslog(LOG_ERR, "config: no server for noread = %s", value);
          } else if (strcmp("only_groups_match_all", param) == 0) {
            if (p) {
                p->only_groups_match_all = atoi(value);
                if (debugmode)
                  syslog(LOG_DEBUG, "config: only_groups_match_all for %s is %d",
                         p->name, p->only_groups_match_all);
            } else
                syslog(LOG_ERR, "config: no server for only_groups_match_all = %s", value);
          } else if (strcmp("initialfetch", param) == 0) {
            initiallimit = strtoul(value, NULL, 10);
            if (debugmode)
                syslog(LOG_DEBUG, "config: initialfetch is %lu",
                     initiallimit);
          } else if ((strcmp("server", param) == 0) ||
                   (strcmp("supplement", param) == 0)) {
            if (debugmode)
                syslog(LOG_DEBUG, "config: server is %s", value);
            p = (struct server *)critmalloc(sizeof(struct server),
                                        "allocating space for server");
            p->name = critstrdup(value, "readconfig");
            p->descriptions = TRUE;
            p->next = NULL;
            p->timeout = 30;  /* default 30 seconds */
            p->port = 0;
            p->username = NULL;
            p->password = NULL;
            p->nopost = 0;
            p->noread = 0;
            p->noxover = 0;
            p->post_anygroup = 0;
            p->updateactive = TRUE;
            p->group_pcre = NULL;
            p->only_groups_match_all = 0;
            if (servers == NULL)
                servers = p;
            else
                q->next = p;
            q = p;
          } else if (0 == strcmp("only_groups_pcre", param)) {
            pcre *re = gs_compile(value);
            if (!re) exit(2);
            if (p) {
                p->group_pcre = re;
                if (debugmode)
                  syslog(LOG_DEBUG, "config: only_groups_pcre for %s is %s",
                         p->name, value);
            } else {
                free(re);
                syslog(LOG_ERR, "config: no server for nopost = %s", value);
            }
          } else {
            ln_log(LNLOG_SERR, LNLOG_CTOP,
                  "config: unknown line %lu: \"%s = %s\"", curline,
                  param, value);
          }
      } else {
          size_t i;
          if ((i = strspn(l, " \t")) < strlen(l) && l[i] != '#') {
            ln_log(LNLOG_SERR, LNLOG_CTOP,
                  "config: malformatted line %lu: \"%s\"", curline,
                  l);
          }
      }
    }
    if (maxage != 0 && maxage > expiredays && clamp_maxage == 0) {
      ln_log(LNLOG_SERR, LNLOG_CTOP,
            "config: maxage (%d) > expire (%d). This can cause duplicate download. Please fix your configuration, maxage must not be greater than expire.",
            maxage, expiredays);
      exit(1);
    }
    debug = debugmode;

    if (!newsadmin) {
      const char t[] = NEWS_USER;
      newsadmin = critmalloc(strlen(fqdn) + strlen(t) + 2, "readconfig");
      strcpy(newsadmin, t); /* RATS: ignore */
      strcat(newsadmin, "@");
      strcat(newsadmin, fqdn); /* RATS: ignore */
    }

    expire_base = ent;
    fclose(f);
    free(param);
    free(value);

    if (servers == NULL) {
      syslog(LOG_ERR, "no server declaration in config file");
      return 0;
    }
    if (!expire)
      syslog(LOG_ERR, "no expire declaration in config file");

    /* check for duplicate server configurations */
    {
      unsigned short port = 0;
      struct servent *sp = getservbyname("nntp", "tcp");
      if (sp) port = ntohs(sp->s_port);

      for (p = servers; p ; p=p->next) {
          for (q = p->next ; q ; q=q->next) {
            unsigned short pp = p->port, qp = q->port;
            if (!pp) pp = port;
            if (!qp) qp = port;
            if (!pp || !qp) {
                syslog(LOG_ERR, "Cannot resolve service \"nntp\" protocol \"tcp\".");
                fprintf(stderr, "Cannot resolve service \"nntp\" protocol \"tcp\".\n");
                return 0;
            }
            if (pp != qp) continue;
            if (strcasecmp(p->name, q->name)) continue;
            syslog(LOG_ERR, "Duplicate definition for server %s port %hu", p->name, pp);
            fprintf(stderr, "Duplicate definition for server %s port %hu\n", p->name, pp);
            return 0;
          }
      }
    }

    if (debugmode > 1) {
      getrlimit(RLIMIT_CORE, &corelimit);
      corelimit.rlim_cur = COREFILESIZE;
      if (setrlimit(RLIMIT_CORE, &corelimit) < 0)
          syslog(LOG_DEBUG, "Changing core file size failed: %m");
      corelimit.rlim_cur = 0;
      getrlimit(RLIMIT_CORE, &corelimit);
      syslog(LOG_DEBUG, "Core file size: %d", (int)corelimit.rlim_cur);
    }

    l = getenv("LN_LOCK_TIMEOUT");
    if (l && *l)
      read_timeout_lock(l, "LN_LOCK_TIMEOUT");

    if (linebuffer) {
      fflush(stdout);
      setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
      fflush(stderr);
      setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
    }

    validatefqdn(logtostderr);
    return 1;
}

/*
1997-05-27 - T. Sweeney - Find a group in the expireinfo linked list and return
                          its expire time. Otherwise, return zero.
*/
static struct expire_entry *
lookup_expireent(char *group)
{
    struct expire_entry *a;

    a = expire_base;
    while (a != NULL) {
      if (ngmatch(a->group, group) == 0)
          return a;
      a = a->next;
    }
    return NULL;
}

static void
freeserver(struct server *a) {
    if (a->group_pcre) pcre_free(a->group_pcre);
    if (a->name) free(a->name);
    if (a->username) free(a->username);
    if (a->password) free(a->password);
    free(a);
}

void /* exported for exclusive use in nntpd.c */
freeservers(void) {
    struct server *i = servers, *n;

    while(i != NULL) {
      n = i->next;
      freeserver(i);
      i = n;
    }
    servers = NULL;
}


void
freeexpire(void)
{
    struct expire_entry *a, *b;

    a = expire_base;
    while(a)
    {
      b = a->next;
      free(a->group);
      free(a);
      a = b;
    }
}

void freeconfig(void) {
    freeservers();
    if (newsadmin)
      free(newsadmin);
    freefilter();
    if (filterfile)
      free(filterfile);
    freegetaline();
    freeexpire();
    (void)lookup(LOOKUP_FREE);
    freelastreply();
}

time_t lookup_expire(char *group)
{
    struct expire_entry *e;
    e = lookup_expireent(group);
    if (e) return e->xtime;
    return 0;
}

int lookup_expiredays(char *group)
{
    struct expire_entry *e;
    e = lookup_expireent(group);
    if (e) return e->days;
    return 0;
}

Generated by  Doxygen 1.6.0   Back to index