Logo Search packages:      
Sourcecode: leafnode version File versions

nntpd.c

/*
nntpd -- the NNTP server

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>
and Kazushi (Jam) Marukawa <jam@pobox.com>.
Copyright of the modifications 1998, 1999.
Modified by Matthias Andree <matthias.andree@web.de>
Copyright of the modifications 2000 - 2002.
Modified by Ralf Wildenhues <ralf.wildenhues@gmx.de>
Copyright of the modifications 2002.
Modified by Jonathan Larmour <jifl@jifvik.org>
Copyright of the modifications 2002.

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

#include "leafnode.h"
#include "masock.h"
#include "mastring.h"
#include "validatefqdn.h"
#include "strlcpy.h"
#include "ln_log.h"
#include "nntpd.h"

#ifdef SOCKS
#include <socks.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#ifndef __LCLINT__
#include <arpa/inet.h>
#endif
#include <ctype.h>
#include "system.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <syslog.h>
#include <unistd.h>
#include <utime.h>

#define MAXLINELENGTH 1000

#ifdef HAVE_IPV6
/*
 *  * local union struct
 */
union sockaddr_union {
    struct sockaddr sa;
    struct sockaddr_in sin;
    struct sockaddr_in6 sin6;
};
#endif

/*@null@*/ static struct newsgroup *group;      /* current group, initially none */
/*@null@*/ static struct newsgroup *xovergroup = NULL;
static unsigned long artno;         /* current article number */
/*@dependent@*/ /*@null@*/ static char *cmd;    /* current command line */
static time_t activetime;

int debug = 0;
int verbose = 0;        /* verbose doesn't count here */

static void
fatal_write(void)
{
    /*@observer@*/ static const char *const e = "Write error on stdout.";
    syslog(LOG_CRIT, "%s", e);
    fprintf(stderr, "%s\n", e);
    exit(EXIT_FAILURE);
}

static void
rereadactive(void)
{
    struct stat st;
    char s[SIZE_s+1];

    (void)xsnprintf(s, SIZE_s, "%s/leaf.node/groupinfo", spooldir);

    if ((!stat(s, &st) && (st.st_mtime > activetime)) || (active == NULL)) {
      char *grouptmp = NULL;
      if (debugmode)
          syslog(LOG_DEBUG, "rereading %s", s);
      if (group) {
          grouptmp = critstrdup(group->name, "rereadactive");
      }
      readactive();
      if (activesize == 0) 
          fakeactive();
      else
          activetime = st.st_mtime;
      if (grouptmp) {
          group = findgroup(grouptmp);
          xovergroup = NULL;
          free(grouptmp);
      }
    }
}

/*
 * pseudo article stuff
 */

/* build and return an open fd to pseudoart in group */
static FILE *
buildpseudoart(const char *grp)
{
    FILE *f;
    int fd;

    mastr *n = mastr_new(PATH_MAX);
    (void)mastr_vcat(n, spooldir, "/temp.files/pseudo.XXXXXXXXXX", NULL);
    fd = safe_mkstemp(mastr_modifyable_str(n));
    if (fd < 0) {
      syslog(LOG_ERR, "Could not create pseudoarticle: mkstemp failed: %m");
      mastr_delete(n);
      return NULL;
    }

    (void)unlink(mastr_str(n));

    f = fdopen(fd, "w+b");
    if (f == NULL) {
      syslog(LOG_ERR, "Could not create pseudoarticle: fdopen failed: %m");
      (void)close(fd);
      mastr_delete(n);
      return NULL;
    }

    fprintf(f, "Path: %s\n", fqdn);
    fprintf(f, "Newsgroups: %s\n", grp);
    fprintf(f, "From: Leafnode <%s>\n", newsadmin);
    fprintf(f, "Subject: Leafnode placeholder for group %s\n", grp);
    fprintf(f, "Date: %s\n", rfctime());
    fprintf(f, "Message-ID: <leafnode:placeholder:%s@%s>\n", grp, fqdn);
    fprintf(f, "\n");
    fprintf(f,
          "This server is running leafnode, which is a dynamic NNTP proxy.\n"
          "This means that it does not retrieve newsgroups unless someone is\n"
          "actively reading them.\n"
          "\n"
          "If you do an operation on a group - such as reading an article,\n"
          "looking at the group table of contents or similar, then leafnode\n"
          "will go and fetch articles from that group when it next updates.\n\n");
    fprintf(f,
          "Since you have read this dummy article, leafnode will retrieve\n"
          "the newsgroup %s when fetchnews is run\n"
          "the next time. If you'll look into this group a little later, you\n"
          "will see real articles.\n\n", grp);
    fprintf(f,
          "If you see articles in groups you do not read, that is almost\n"
          "always because of cross-posting.  These articles do not occupy any\n"
          "more space - they are hard-linked into each newsgroup directory.\n"
          "\n"
          "If you do not understand this, please talk to your newsmaster.\n"
          "\n"
          "Leafnode can be found at\n" "\thttp://leafnode.sourceforge.net/\n\n");

    if (ferror(f)) {
      (void)fclose(f);
      f = NULL;
    } else {
      rewind(f);
    }
    mastr_delete(n);
    return f;
}

static int
parserange(char *arg, unsigned long *a, unsigned long *b)
{
    char *l = arg;
    /* no argument */
    if (!*l) return 0;

    /* parse */
    if (*l != '-')
      *a = strtoul(arg, &l, 10);
    SKIPLWS(l);
    if (*l == '-') {
      ++l;
      SKIPLWS(l);
      if (isdigit((unsigned char)*l))
          *b = strtoul(l, &l, 10);
    } else {
      *b = *a;
    }
    SKIPLWS(l);

    /* trailing garbage */
    if (l && *l) {
      return 0;
    }

    return 1;
}

static int
is_pseudogroup(/*@null@*/ const struct newsgroup *g)
{
    if (!g) return 0;
    if (!chdirgroup(g->name, FALSE)) return 1;
/*    if (isinteresting(g->name)) return 0; */
    if (g->last < g->first) return 1;
    if (g->last <= 1) return 1;
    return 0;
}

static void
markinterest(const char *ng)
{
    struct stat st;
    struct utimbuf buf;
    int err;
    time_t now;
    FILE *f;
    char s[SIZE_s+1];

    err = 0;
    (void)xsnprintf(s, SIZE_s, "%s/interesting.groups/%s", spooldir, ng);
    if (stat(s, &st) == 0) {
      now = time(NULL);
      buf.actime = (now < st.st_atime) ? st.st_atime : now;
      /* now < update may happen through HW failures */
      buf.modtime = st.st_mtime;
      if (utime(s, &buf)) {
          err = 1;
      }
      if (debugmode)
          syslog(LOG_DEBUG, "markinterest: %s touched", ng);
    } else {
      err = 1;
    }
    if (err) {
      f = fopen(s, "w");
      if (f == NULL || fclose(f) == EOF) {
          syslog(LOG_ERR, "Could not create %s: %m", s);
      } else {
          if (debugmode)
            syslog(LOG_DEBUG, "markinterest: %s new", ng);
      }
    } else {
      int fd = open(".", O_RDONLY);
      (void)chdirgroup(ng, FALSE);
      if (fd >= 0) {
          (void)fchdir(fd);
          (void)close(fd);
      }
    }
}

/* open a pseudo art */
/* WARNING: article_num MUST be 0 for selection based on Message-ID */
static FILE *
fopenpseudoart(const char *arg, const unsigned long article_num)
{
    FILE *f = NULL;
    char *c;
    struct newsgroup *g;

    if (group && (article_num && ((article_num == group->first &&
          group->first >= group->last) || is_pseudogroup(group)))) {
      f = buildpseudoart(group->name);
    } else if (!article_num) {
      if (!strncmp(arg, "<leafnode:placeholder:", 22)) {
          mastr *msgidbuf = mastr_new(1024);
          (void)mastr_cpy(msgidbuf, arg + 22);
          if ((c = strchr(mastr_modifyable_str(msgidbuf), '@')) != NULL) {
            *c++ = '\0';
            (void)strtok(c, ">");
            if (0 == strcasecmp(c, fqdn)) {
                g = findgroup(mastr_str(msgidbuf));
                if (g && (g->last <= 1 || g->first >= g->last)) {
                  markinterest(g->name);
                  f = buildpseudoart(g->name);
                }
            }
          }
          mastr_delete(msgidbuf);
      }
    }
    return f;
}

/* open an article by number or message-id */
static FILE *
fopenart(const char *arg)
{
    unsigned long int a;
    FILE *f;
    char *t;
    struct stat st;
    /*@temp@*/ char buf[32];

    t = NULL;
    a = strtoul(arg, &t, 10);
    if (arg && *arg == '<') {
      /* message ID given */
      f = fopen(lookup(arg), "r");
      if (!f) f = fopenpseudoart(arg, 0);
    } else if (t && !*t && group != NULL) {
      const char *ptr = arg;
      /* number not given -> take current article pointer */
      if (!a) {
          a = artno;
          sprintf(buf, "%lu", artno);     /* RATS: ignore */
          ptr = buf;
      }
      if (is_pseudogroup(group)) {
          f = fopenpseudoart(ptr, a);
      } else {
          f = fopen(ptr, "r");
      }
      if (f != NULL)
          artno = a;
      markinterest(group->name);
    } else {
      f = NULL;
    }
    if (f != NULL && (fstat(fileno(f), &st) || st.st_size == 0)) {
      (void)fclose(f);
      f = NULL;
    }
    return f;
}


/*
 * Mark an article for download by appending its number to the
 * corresponding file in interesting.groups
 */
static int
markdownload(const char *ng, unsigned long id)
{
    int i, e = 0;
    unsigned long n;
    FILE *f;
    char *t;
    char s[SIZE_s+1];

    (void)xsnprintf(s, SIZE_s, "%s/interesting.groups/%s", spooldir, ng);
    if ((f = fopen(s, "r+"))) {
      i = 0;
      while ((t = getaline(f))) {
          if (sscanf(t, "%lu", &n) == 1 && n == id) {
            (void)fclose(f);  /* we only read from the file */
            return 0;   /* already marked */
          }
          if (ferror(f))
            e = errno;
          ++i;
      }
      if (i < BODY_DOWNLOAD_LIMIT) {
          (void)fprintf(f, "%lu\n", id);
          if (ferror(f))
            e = errno;
          if (debugmode)
            syslog(LOG_DEBUG, "Marking %s %lu for download", ng, id);
      } else {
          syslog(LOG_ERR, "Too many bodies marked in %s", ng);
      }
      if (fclose(f))
          e = errno;
    }
    if (e) {
      syslog(LOG_ERR, "I/O error handling \"%s\": %s", s, strerror(e));
      return -1;
    }
    return 1;
}

static void
nogroup(void)
{
    printf("412 Use the GROUP command first\r\n");
    if (debugmode)
      syslog(LOG_DEBUG, ">412 Use the GROUP command first");
    return;
}

/* display an article or somesuch */
/* DOARTICLE */
static void
doarticle(const char *arg, int what)
{
    FILE *f;
    char *p = NULL;
    char *q = NULL;
    char *t;
    unsigned long localartno;
    unsigned long replyartno;
    char *localmsgid, *xref = NULL;
    char *markgroup = NULL;
    char s[SIZE_s+1];

    f = fopenart(arg);
    if (!f) {
      if (arg && *arg != '<' && !group) {
          nogroup();
      } else if (strlen(arg)) {
          printf("430 No such article: %s\r\n", arg);
          if (debugmode)
            syslog(LOG_DEBUG, ">430 No such article: %s", arg);
      } else {
          printf("423 No such article: %lu\r\n", artno);
          if (debugmode)
            syslog(LOG_DEBUG, ">423 No such article: %lu", artno);
      }
      return;
    }

    if (!*arg) {
          /* no. implicit */
      localartno = artno;
      localmsgid = fgetheader(f, "Message-ID:");
    } else if (*arg == '<') {
          /* message-id -- do not modify artno */
      localartno = 0;
      localmsgid = critstrdup(arg, "doarticle");
    } else {
          /* no. explicit */
      localartno = strtoul(arg, NULL, 10);
      localmsgid = fgetheader(f, "Message-ID:");
    }

    replyartno = localartno;
    if (!localartno) {
      /* we have to extract the article number and the newsgroup from
       * the Xref: header */
      xref = fgetheader(f, "Xref:");
      p = xref;
      if (p) {
          /* skip host name */
          while (*p && !isspace((unsigned char)*p)) {
            p++;
          }
          while (isspace((unsigned char)*p)) {
            p++;
          }
      }
      if (p) {
          /* search article number of current group in Xref: */
          if (group) {
            while ((q = strstr(p, group->name)) != NULL) {
                q += strlen(group->name);
                if (*q++ == ':') {
                  localartno = strtoul(q, NULL, 10);
                  markgroup = group->name;
                  break;
                }
                p = q + strcspn(q, " \t");
            }
          }
          /* if we don't have a localartno, then we need to mark this
           * article in a different news group */
          if (!localartno) {
            char *r = p;
            while ((q = strchr(r, ':'))) {
                *q++ = '\0';
                if (isinteresting(p)) {
                  /* got one we can mark */
                  markgroup = r;
                  localartno = strtoul(q, NULL, 10);
                  break;
                }
                while (isdigit((unsigned char)*q)) {
                  q++;
                }
                while (isspace((unsigned char)*q)) {
                  q++;
                }
                r = q;
            }
          }
      }
    } else if (group) {
      markgroup = group->name;
    }

    if (!localmsgid) {
      const char *tmp = "423 Corrupt article.";
      printf("%s\r\n", tmp);
      syslog(LOG_WARNING, ">%s", tmp);
      if (replyartno) {
          (void)xsnprintf(s, SIZE_s, "%lu", replyartno);
          (void)log_unlink(s, 0);
      }
      (void)fclose(f);
      if (xref)
          free(xref);
      return;
    }
    (void)xsnprintf(s, SIZE_s - 24, "%3d %lu %s article retrieved - ",
                223 - what, replyartno, localmsgid);
    free(localmsgid);

    if (what == 0)
      strcat(s, "request text separately");
    else if (what == 1)
      strcat(s, "body follows");
    else if (what == 2)
      strcat(s, "head follows");
    else
      strcat(s, "text follows");
    printf("%s\r\n", s);
    if (debugmode)
      syslog(LOG_DEBUG, ">%s", s);

    while ((t = getaline(f)) && *t) {
      if (what & 2) {
          if (*t == '.')
            (void)fputc('.', stdout);
          (void)fputs(t, stdout);
          (void)fputs("\r\n", stdout);
          if (ferror(stdout))
            fatal_write();
      }
    }
    /* Matthias Andree, 2002-03-05:
     * t == NULL or *t == '\0' makes a difference here.
     * -  t == NULL means we ran into EOF and don't have a body in delaybody mode.
     * -  t != NULL but *t == '\0' means we just read the empty separator line between header and body.
     */

    if (what == 3)
      printf("\r\n");         /* empty separator line */

    if (what & 1) {
      /* delaybody:
         t == NULL: body is missing, mark for download
         t != NULL: body is present */
      if (t == NULL) {
          if (localartno && markgroup != NULL) {
            switch (markdownload(markgroup, localartno)) {
            case 0:
                printf("\r\n\r\n"
                     "\t[ Leafnode: ]\r\n"
                     "\t[ This message has already been "
                     "marked for download. ]\r\n");
                break;
            case 1:
                printf("\r\n\r\n"
                     "\t[ Leafnode: ]\r\n"
                     "\t[ Message %lu of %s ]\r\n"
                     "\t[ has been marked for download. ]\r\n",
                     localartno, markgroup);
                break;
            default:
                printf("\r\n\r\n"
                     "\t[ Leafnode: ]\r\n"
                     "\t[ Message %lu of %s ]\r\n"
                     "\t[ cannot be marked for download. ]\r\n"
                     "\t[ (Check the server's syslog "
                     "for information). ]\r\n", localartno, markgroup);
                break;
            }
          } else {
            /* did not figure a group for which to mark this article */
            syslog(LOG_ERR,
                   "cannot mark for body download: arg=\"%s\" "
                   "localartno=%lu markgroup=\"%s\" group=\"%s\"",
                   arg, localartno, markgroup ? markgroup : "(null)",
                   group ? group->name : "(null)");
            printf("\r\n\r\n"
                   "\t[ Leafnode: ]\r\n"
                   "\t[ I cannot figure out a newsgroup for which to download ]\r\n"
                   "\t[ this article. Please report this condition to the ]\r\n"
                   "\t[ leafnode mailing list, with leafnode version, the name ]\r\n"
                   "\t[ and the version of your news reader and a log excerpt. ]\r\n");
          }
      } else {          /* immediate body */
          while ((t = getaline(f))) {
            if (*t == '.')
                (void)fputc('.', stdout);
            (void)fputs(t, stdout);
            (void)fputs("\r\n", stdout);
            if (ferror(stdout))
                fatal_write();
          }
      }
    }
    if (what)
      printf(".\r\n");
    (void)fclose(f);

    if (xref)
      free(xref);

    return;             /* OF COURSE there were no errors */
}


/* change to group - note no checks on group name */
static int
dogroup(const char *arg)
{
    struct newsgroup *g;

    g = findgroup(arg);
    if (g) {
      group = g; /* global */
      if (isinteresting(arg)) {
          if (debugmode)
            syslog(LOG_DEBUG, "marked group %s interesting", arg);
          markinterest(arg);
      }
      if (!is_pseudogroup(g)) {
          /* regular news group */
          if (debugmode)
            syslog(LOG_DEBUG, ">211 %lu %lu %lu %s group selected",
                   g->last >= g->first ? g->last - g->first + 1 : 0,
                   g->first, g->last, g->name);
          printf("211 %lu %lu %lu %s group selected\r\n",
               g->last >= g->first ? g->last - g->first + 1 : 0,
               g->first, g->last, g->name);
      } else {
          /* pseudo news group */
          if (debugmode)
            syslog(LOG_DEBUG,
                   ">211 %lu %lu %lu %s group selected (pseudo article)",
                   1lu, g->first, g->first, g->name);
          printf("211 %lu %lu %lu %s group selected (pseudo article)\r\n",
                   1lu, g->first, g->first, g->name);
      }
      artno = g->first;
    } else {
      if (debugmode)
          syslog(LOG_DEBUG, ">411 No such group");
      printf("411 No such group\r\n");
    }
    if (fflush(stdout)) return -1;
    return 0;
}

static void
dohelp(void)
{
    fputs("100 Legal commands on THIS server:\r\n"
          " ARTICLE [<Message-ID>|<Number>]\r\n"
          " BODY [<Message-ID>|<Number>]\r\n"
          " DATE\r\n"
          " GROUP <Newsgroup>\r\n"
          " HDR <Header> <Message-ID>|<Range>\r\n"
          " HEAD [<Message-ID>|<Number>]\r\n"
          " HELP\r\n"
          " LAST\r\n"
          " LIST [ACTIVE|NEWSGROUPS] [<Wildmat>]]\r\n"
          " LIST [ACTIVE.TIMES|EXTENSIONS|OVERVIEW.FMT]\r\n"
          " LISTGROUP <Newsgroup>\r\n"
          " MODE READER\r\n"
          " NEWGROUPS <yymmdd> <hhmmss> [GMT]\r\n"
          " NEXT\r\n"
          " POST\r\n"
          " OVER <Range>\r\n"
          " SLAVE\r\n"
          " STAT [<Message-ID>|<Number>]\r\n"
          " XHDR <Header> <Message-ID>|<Range>\r\n"
          " XOVER <Range>\r\n"
          ".\r\n", stdout);
}

static void
domove(int by)
{
    char *msgid;
    char s[SIZE_s+1];

    by = (by < 0) ? -1 : 1;
    if (group) {
      if (artno) {
          artno += by;
          do {
            sprintf(s, "%lu", artno);
            msgid = getheader(s, "Message-ID:");
            if (!msgid)
                artno += by;
          } while (msgid == NULL && artno >= group->first && artno <= group->last);
          if (msgid && (artno > group->last || artno < group->first)) {
            free(msgid);
            msgid = NULL;
          }
          if (msgid == NULL) {
            if (by > 0) {
                artno = group->last;
                printf("421 There is no next article\r\n");
                if (debugmode)
                  syslog(LOG_DEBUG, ">421 There is no next article");
            } else {
                artno = group->first;
                printf("422 There is no previous article\r\n");
                if (debugmode)
                  syslog(LOG_DEBUG, ">422 There is no previous article");
            }
          } else {
            printf("223 %lu %s article retrieved\r\n", artno, msgid);
            if (debugmode)
                syslog(LOG_DEBUG, ">223 %lu %s article retrieved",
                     artno, msgid);
            free(msgid);
          }
      } else {
          printf("420 There is no current article\r\n");
          if (debugmode)
            syslog(LOG_DEBUG, ">420 There is no current article");
      }
    } else {
      nogroup();
    }
}

static int is_pattern(const char *s) {
    return s ? strcspn(s, "?*[") < strlen(s) : 1;
}

/* LIST ACTIVE if what==0,
 * LIST NEWSGROUPS if what==1
 * LIST ACTIVE.TIMES if what==2
 */
static void
printlist(const struct newsgroup *g, const int what)
{
    switch(what) {
      case 0:
          if (is_pseudogroup(g))
            printf("%s %lu %lu y\r\n", g->name, g->first, g->first);
          else
            printf("%s %lu %lu y\r\n", g->name, g->last, g->first);
          break;
      case 1:
          printf("%s\t%s\r\n", g->name, g->desc ? g->desc : "-x-");
          break;
      case 2:
          printf("%s %lu %s\r\n", g->name, (unsigned long)g->age, newsadmin);
          break;
      default:
          abort();
          break;
    }
}

/* LIST ACTIVE if what==0,
 * LIST NEWSGROUPS if what==1
 * LIST ACTIVE.TIMES if what==2
 */
static void
list(struct newsgroup *g, const int what, const char *pattern)
{
    if (is_pattern(pattern)) {
      while(g->name) {
          if (!pattern || !ngmatch(pattern, g->name)) {
            printlist(g, what);
          }
          g++;
      }
    } else {
      /* single group */
      g = findgroup(pattern);
      if (g) {
          printlist(g, what);
          if (what == 0 && isinteresting(pattern))
            markinterest(pattern);
      }
    }
}

static void
dolist(char *arg)
{
    if (!strcasecmp(arg, "extensions")) {
      printf("202 extensions supported follow\r\n"
            "HDR\r\n"
            "LISTGROUP\r\n"
            "OVER\r\n"
            "XHDR\r\n"
            "XOVER\r\n"
            ".\r\n");
      if (debugmode)
          syslog(LOG_DEBUG, ">202 extensions supported follow");
    } else if (!strcasecmp(arg, "overview.fmt")) {
      printf("215 information follows\r\n"
            "Subject:\r\n"
            "From:\r\n"
            "Date:\r\n"
            "Message-ID:\r\n"
            "References:\r\n"
            "Bytes:\r\n"
            "Lines:\r\n"
            "Xref:full\r\n"
            ".\r\n");
      if (debugmode)
          syslog(LOG_DEBUG, ">215 information follows");
    } else if (!strncasecmp(arg, "active.times", 12)) {
      if (active) {
          const char m[] = "215 Note that leafnode will fetch groups on demand.";
            printf("%s\r\n", m);
          if (debugmode)
            syslog(LOG_DEBUG, ">%s", m);
          list(active, 2, NULL);
          printf(".\r\n");
      } else {
          const char e[] = "503 Active file has not been read.";
          printf("%s\r\n", e);
          if (debugmode)
            syslog(LOG_DEBUG, ">%s", e);
      }
    } else {
      if (!active) {
          printf("503 Group information file does not exist!\r\n");
          syslog(LOG_ERR, ">503 Group information file does not exist!");
      } else if (!*arg || !strncasecmp(arg, "active", 6)) {
          printf("215 Newsgroups in form \"group high low flags\".\r\n");
          if (debugmode)
            syslog(LOG_DEBUG,
                  ">215 Newsgroups in form \"group high low flags\".");
          if (active) {
            if (!*arg || strlen(arg) == 6)
                list(active, 0, NULL);
            else {
                while (*arg && (!isspace((unsigned char)*arg)))
                  arg++;
                while (*arg && isspace((unsigned char)*arg))
                  arg++;
                list(active, 0, arg);
            }
          }
          printf(".\r\n");
      } else if (!strncasecmp(arg, "newsgroups", 10)) {
          printf("215 Descriptions in form \"group description\".\r\n");
          if (debugmode)
            syslog(LOG_DEBUG,
                  ">215 Descriptions in form \"group description\".");
          if (active) {
            if (strlen(arg) == 10)
                list(active, 1, NULL);
            else {
                while (*arg && (!isspace((unsigned char)*arg)))
                  arg++;
                while (*arg && isspace((unsigned char)*arg))
                  arg++;
                list(active, 1, arg);
            }
          }
          printf(".\r\n");
      } else {
          printf("503 Syntax error\r\n");
          if (debugmode)
            syslog(LOG_DEBUG, ">503 Syntax error");
      }
    }
}

static void
donewgroups(const char *arg)
{
    struct tm timearray;
    struct tm *ltime;
    time_t age;
    time_t now;
    int year, century;
    char *l;
    long a;
    long b;
    struct newsgroup *ng;

    now = time(NULL);
    ltime = localtime(&now);
    if (ltime == NULL) {
      syslog(LOG_CRIT, "fatal: localtime returned NULL. abort.");
      abort();
    }
    year = ltime->tm_year % 100;
    century = ltime->tm_year / 100; /* 0 for 1900-1999, 1 for 2000-2099 etc */

    memset(&timearray, 0, sizeof(timearray));
    l = NULL;
    a = (int)strtol(arg, &l, 10);
    /* NEWGROUPS may have the form YYMMDD or YYYYMMDD.
       Distinguish between the two */
    b = a / 10000;
    if (b < 100) {
      /* YYMMDD */
      if (b <= year)
          timearray.tm_year = (int)(b + (century * 100));
      else
          timearray.tm_year = (int)(b + (century - 1) * 100);
    } else if (b < 1000) {
      /* YYYMMDD - happens with buggy newsreaders */
      /* In these readers, YYY=100 is equivalent to YY=00 or YYYY=2000 */
      syslog(LOG_NOTICE,
             "NEWGROUPS year is %ld: please update your newsreader", b);
      timearray.tm_year = (int)b;
    } else {
      /* YYYYMMDD */
      timearray.tm_year = (int)b - 1900;
    }
    timearray.tm_mon = (int)(a % 10000 / 100) - 1;
    timearray.tm_mday = (int)(a % 100);
    while (*l && isspace((unsigned char)*l))
      l++;
    a = strtol(l, &l, 10);    /* we don't care about the rest of the line */
    while (*l && isspace((unsigned char)*l))
      l++;
    timearray.tm_hour = (int)(a / 10000);
    timearray.tm_min = (int)(a % 10000 / 100);
    timearray.tm_sec = (int)(a % 100);
    /* mktime() shall guess correct value of tm_isdst (0 or 1) */
    timearray.tm_isdst = -1;
    if (0 == strncasecmp(l, "gmt", 3))
      age = timegm(&timearray);
    else
      age = mktime(&timearray);

    printf("231 List of new newsgroups since %ld follows\r\n", (long)age);
    if (debugmode)
      syslog(LOG_DEBUG, "231 List of new newsgroups since %ld follows",
             (long)age);

    ng = active;
    if (ng != NULL) 
      while (ng->name) {
          if (ng->age >= age)
            printf("%s %lu %lu y\r\n", ng->name, ng->last, ng->first);
          ng++;
      }
    printf(".\r\n");
}

/* next bit is copied from INN 1.4 and modified ("broken") by agulbra

   mail to Rich $alz <rsalz@uunet.uu.net> bounced */

/* Scale time back a bit, for shorter Message-ID's. */
#define OFFSET    (time_t)1026380000L

/*@observer@*/ static char *
generateMessageID(void)
{
    static char ALPHABET[] = "0123456789abcdefghijklmnopqrstuv";

    static char buff[1000];
    static time_t then;
    static unsigned int fudge;
    time_t now;
    char *p;
    unsigned long n;

    now = time(NULL);         /* might be 0, in which case fudge
                           will almost fix it */
    if (now < OFFSET) {
      ln_log(LNLOG_SCRIT, LNLOG_CTOP,
            "your system clock cannot be right. abort.");
      abort();
    }
    if (now != then)
      fudge = 0;
    else
      fudge++;
    then = now;

    p = buff;
    *p++ = '<';
    n = (unsigned long)now - OFFSET;
    while (n) {
      *p++ = ALPHABET[(int)(n & 31)];
      n >>= 5;
    }
    *p++ = '-';
    n = fudge * 32768 + (int)getpid();
    while (n) {
      *p++ = ALPHABET[(int)(n & 31)];
      n >>= 5;
    }
    sprintf(p, ".ln1@%-.256s>", fqdn);
    return buff;
}
/* the end of what I stole from rsalz and then mangled */


static int
dopost(void)
{
    char *line;
    int havefrom = 0;
    int havepath = 0;
    int havedate = 0;
    int havenewsgroups = 0;
    int havemessageid = 0;
    int havesubject = 0;
    int err = 0, ferr = 0;
    /*@observer@*/ const char *ferrstr = NULL;
    int o;
    size_t len;
    FILE *out;
    char outname[1000];
    static int postingno;     /* starts as 0 */
    char *suggmid;
    /*@observer@*/ const char *appendheader = NULL;

    if (getenv("LN_REJECT_POST_PRE")) {
      printf("400 Posting rejected - debug variable LN_REJECT_POST_PRE exists\r\n");
      return 0;
    }

    do {
      (void)xsnprintf(outname, sizeof(outname), "%s/out.going/%d-%d-%d",
            spooldir, (int)getpid(), (int)time(NULL), ++postingno);

      o = open(outname, O_WRONLY | O_EXCL | O_CREAT, 0244);
      if (o < 0 && errno != EEXIST) {
          char *errmsg = strerror(errno);
          printf("441 Unable to open spool file %s: %s\r\n", outname, errmsg);
          syslog(LOG_ERR, ">441 Unable to open spool file %s: %s", outname,
                errmsg);
          return 0;
      }
    } while (o < 0);

    out = fdopen(o, "w");
    if (out == NULL) {
      char *errmsg = strerror(errno);
      printf("441 Unable to fdopen(%d): %s\r\n", o, errmsg);
      syslog(LOG_ERR, ">441 Unable to fdopen(%d): %s", o, errmsg);
      return 0;
    }

    suggmid = generateMessageID();
    printf("340 Ok, recommended ID %s\r\n", suggmid);
    if (debugmode)
      syslog(LOG_DEBUG, ">340 Go ahead.");
    if (fflush(stdout)) return -1;

    /* get headers */
    do {
      debug = 0;
      line = getaline(stdin);
      if (!line) { /* timeout */
            unlink(outname);
          exit(0);
      }

      if (0 == strcmp(line, ".")) {
          ferr = TRUE;
          ferrstr = "No body found.";
          break;
      }
      debug = debugmode;

      if (!strncasecmp(line, "From: ", 6)) {
          if (havefrom)
            ferr = TRUE, ferrstr = "Duplicate From: header";
          else
            havefrom = TRUE;
      }
      if (!strncasecmp(line, "Path: ", 6)) {
          if (havepath)
            ferr = TRUE, ferrstr = "Duplicate Path: header";
          else
            havepath = TRUE;
      }
      if (!strncasecmp(line, "Message-ID: ", 12)) {
          if (havemessageid)
            ferr = TRUE, ferrstr = "Duplicate Message-ID: header";
          else {
            char *vec[2]; /* RATS: ignore */
            int rc;
            
            havemessageid = TRUE;
            if (debugmode)
                syslog(LOG_DEBUG, "debug header: %s", line);
            if (2 != (rc = pcre_extract(line, "Message-ID:\\s+<(?:[^>]+)@([^@>]+)>\\s*$", vec, 2))
                  || vec[1] == NULL) {
                ferr = TRUE, ferrstr = "Malformatted Message-ID: header.";
            } else if (!strchr(vec[1], '.')) {
                ferr = TRUE, ferrstr = "Message-ID: header does not have domain name part.";
            } else if (!is_validfqdn(vec[1])) {
                ferr = TRUE, ferrstr = "Message-ID: header contains invalid domain name part.";
            }
            pcre_extract_free(vec, rc);
          }
      }
      if (!strncasecmp(line, "Subject: ", 9)) {
          if (havesubject)
            ferr = TRUE, ferrstr = "Duplicate Subject: header";
          else
            havesubject = TRUE;
      }
      if (!strncasecmp(line, "Newsgroups: ", 12)) {
          if (havenewsgroups)
            ferr = TRUE, ferrstr = "Duplicate Newsgroups: header";
          else
            havenewsgroups = TRUE;
      }
      if (!strncasecmp(line, "Date: ", 6)) {
          if (havedate)
            ferr = TRUE, ferrstr = "Duplicate Date: header";
          else
            havedate = TRUE;
      }

      len = strlen(line);

      /* check for illegal 8bit/control stuff in header */
      {
          char *t;
          for (t = line; *t; t++) {
            if (*t & 0x80) {
                if (allow_8bit_headers) {
                  appendheader = "X-Leafnode-Warning: administrator "
                      "allowed illegal use of 8-bit data in header.\r\n";
                } else {
                  ferr = TRUE;
                  ferrstr = "Illegal use of 8-bit data in header.";
                  break;
                }
            }
            if ((unsigned char)*t < (unsigned char)0x20u && *t != '\t') {
                ferr = TRUE;
                ferrstr = "Illegal use of control data in header.";
                break;
            }
          }
      }

      /* checks for non-folded lines */
      if (*line && *line != ' ' && *line != '\t') {
          if (strchr(line, ':') == NULL) {
            /* must have a colon */
            ferr = TRUE;
            ferrstr = "Header tag not found.";
          } else if (strcspn(line, " \t") < strcspn(line, ":")) {
            /* must not have space before colon */
            ferr = TRUE;
            ferrstr = "Whitespace in header tag is not allowed.";
          }
      }

      if (len) {
          if (fwrite(line, 1, len, out) != (size_t) len)
            err = 1;
      } else {
          if (!havepath) {
            if (fputs("Path: ", out) == EOF)
                err = 1;
            if (fputs(fqdn, out) == EOF)
                err = 1;
            if (fprintf(out, "!%s\r\n", NEWS_USER) < 0)
                err = 1;
          }
          if (!havedate) {
            const char *l = rfctime();
            if (fputs("Date: ", out) == EOF)
                err = 1;
            if (fputs(l, out) == EOF)
                err = 1;
            if (fputs("\r\n", out) == EOF)
                err = 1;
          }
          if (!havemessageid) {
            if (fputs("Message-ID: ", out) == EOF)
                err = 1;
            if (fputs(suggmid, out) == EOF)
                err = 1;
            if (fputs("\r\n", out) == EOF)
                err = 1;
          }
          if (appendheader) {
            if (fputs(appendheader, out) == EOF)
                err = 1;
          }
      }
      if (fputs("\r\n", out) == EOF)
          err = 1;
    } while (*line);

    /* get bodies */
    if (strcmp(line, "."))
      do {
          debug = 0;
          line = getaline(stdin);
          debug = debugmode;
          if (!line) {
            (void)unlink(outname);
            exit(1);
          }

          len = strlen(line);
          if (line[0] == '.') {
            if (len > 1) {
                if (fputs(line + 1, out) == EOF)
                  err = 1;
                if (fputs("\r\n", out) == EOF)
                  err = 1;
            }
          } else {
            if (fputs(line, out) == EOF)
                err = 1;
            if (fputs("\r\n", out) == EOF)
                err = 1;
          }
      } while (line[0] != '.' || line[1] != '\0');

    if (fflush(out))
      err = 1;
    
    if (fsync(fileno(out)))
      err = 1;

    if (fclose(out))
      err = 1;

    if (!havenewsgroups)
      ferrstr = "Missing Newsgroups: header";
    if (!havesubject)
      ferrstr = "Missing Subject: header";
    if (!havefrom)
      ferrstr = "Missing From: header";

    if (getenv("LN_REJECT_POST_POST"))
      ferr = 1;

    if (havefrom && havesubject && havenewsgroups && !ferr) {
      if (!err && 0 == chmod(outname, 0644)) {
          printf("240 Article posted, now be patient\r\n");
          if (debugmode)
            syslog(LOG_DEBUG, ">240 Article posted, now be patient");
          return 0;
      } else {
          (void)unlink(outname);
          printf("441 I/O error, article not posted\r\n");
          syslog(LOG_INFO, ">441 I/O error, article not posted");
          return 0;
      }
    }

    (void)unlink(outname);

    if (getenv("LN_REJECT_POST_POST")) {
      printf("400 Posting rejected - debug variable LN_REJECT_POST_POST exists\r\n");
      syslog(LOG_INFO, ">400 Posting rejected - debug variable LN_REJECT_POST_POST exists\r\n");
      return 0;
    }

    if (ferrstr) {
      printf("441 Post rejected, formatting error: %s\r\n", ferrstr);
      syslog(LOG_INFO, ">441 Post rejected, formatting error: %s", ferrstr);
    } else {
      printf("441 Post rejected, formatting error\r\n");
      syslog(LOG_INFO, ">441 Post rejected, formatting error");
    }

    return 0;
}

static void invalidrange(void)
{
    printf("420 No articles in specified range.\r\n");
    if (debugmode)
      syslog(LOG_DEBUG, ">420 No articles in specified range.");
}

/* check if a - b is a valid range for the current group.
 * If it's not, print a 420 error and return 0.
 * If it is, do not print anything and return 1.
 * group must not be NULL!
 */
static int checkrange(const struct newsgroup *g,
      unsigned long a, unsigned long b)
{
    if ((a > b) || (g->first <= g->last
            ? (a > g->last) || (b < g->first)
            : (a > g->first) || (b < g->first))) {
      invalidrange();
      return 0;
    }
    return 1;
}



static void
doxhdr(char *arg)
{
    static const char *h[] = { "Subject", "From", "Date", "Message-ID",
      "References", "Bytes", "Lines"
    };

    int n = 7;
    size_t i;
    char *l;
    char *buf;
    unsigned long a, b = 0, c;
    char s[SIZE_s+1];

    if (!arg || !*arg) {
      if (debugmode)
          syslog(LOG_DEBUG,
               ">502 Usage: HDR header first[-last] or "
               "HDR header message-id");
      printf("502 Usage: HDR header first[-last] or "
             "HDR header message-id\r\n");
      return;
    }

    /* go figure header */
    l = arg;
    while (l && *l && !isspace((unsigned char)*l))
      l++;
    if (l && *l)
      *l++ = '\0';
    SKIPLWS(l);

    buf = critmalloc((i = strlen(arg)) + 2, "doxhdr");
    strcpy(buf, arg); /* RATS: ignore */
    if (buf[i - 1] != ':')
      strcpy(buf + i, ":");

    if (l && *l == '<') {     /* handle message-id form (well) */
      FILE *f;
      char *m = critstrdup(l, "doxhdr");
      f = fopenart(l);
      if (!f) {
          printf("430 No such article\r\n");
          if (debugmode)
            syslog(LOG_DEBUG, ">430 No such article");
          free(buf);
          free(m);
          return;
      }
      l = fgetheader(f, buf);
      if (debugmode) {
          syslog(LOG_DEBUG, ">221 %s header of %s follows:", buf, m);
          if (l) syslog(LOG_DEBUG, ">%s %s", m, l);
          syslog(LOG_DEBUG, ">.");
      }
      printf("221 %s header of %s follows:\r\n", buf, m);
      if (l) printf("%s %s\r\n", m, l);
      printf(".\r\n");
      free(m);
      (void)fclose(f);
      free(buf);
      if (l) free(l);
      return;
    }

    if (!group) {
      nogroup();
      free(buf);
      return;
    }

    markinterest(group->name);

    a = group->first;
    b = group->last;
    if (b < a) b = a;
    if (!parserange(l, &a, &b)) {
      if (debugmode)
          syslog(LOG_DEBUG, ">502 Usage: XHDR header first[-last] "
                "or XHDR header message-id");
      printf("502 Usage: XHDR header first[-last] "
            "or XHDR header message-id\r\n");
      free(buf);
      return;
    }

    if (!checkrange(group, a, b)) {
      free(buf);
      return;
    }

    if (!is_pseudogroup(group)) {
          if (xovergroup != group && chdirgroup(group->name, FALSE))
                if (getxover())
                      xovergroup = group;
    }

    if (is_pseudogroup(group)) {
      do {
          n--;
      } while (n >= 0 && strncasecmp(h[n], buf, strlen(h[n])) != 0);
      if ((n < 0) && strncasecmp("Newsgroups", buf, 10)) {
          printf("430 No such header: %s\r\n", buf);
          if (debugmode)
            syslog(LOG_DEBUG, ">430 No such header: %s", buf);
          free(buf);
          return;
      }
      if (debugmode)
          syslog(LOG_DEBUG,
                ">221 First line of %s pseudo-header follows:", buf);
      printf("221 First line of %s pseudo-header follows:\r\n", buf);
      if (a <= b && a <= group->first && b >= group->last) {
          printf("%lu ", group->first);
          if (n == 0)         /* Subject: */
            printf("Leafnode placeholder for group %s\r\n", group->name);
          else if (n == 1)    /* From: */
            printf("Leafnode <%s>\r\n", newsadmin);
          else if (n == 2)    /* Date: */
            printf("%s\r\n", rfctime());
          else if (n == 3)    /* Message-ID: */
            printf("<leafnode:placeholder:%s@%s>\r\n", group->name, fqdn);
          else if (n == 4)    /* References */
            printf("(none)\r\n");
          else if (n == 5)    /* Bytes */
            printf("%d\r\n", 1024); /* FIXME: just a guess */
          else if (n == 6)    /* Lines */
            printf("%d\r\n", 22);   /* FIXME: from buildpseudoart() */
          else                /* Newsgroups */
            printf("%s\r\n", group->name);
      }
      printf(".\r\n");
      free(buf);
      return;
    }

    do {
      n--;
    } while (n > -1 && strncasecmp(buf, h[n], strlen(h[n])));

    if (a < group->first)
      a = group->first;

    if (b > group->last)
      b = group->last;

    if (n >= 0) {
      if (debugmode)
          syslog(LOG_DEBUG, "221 %s header (from overview) "
               "for postings %lu-%lu:", h[n], a, b);
      printf("221 %s header (from overview) for postings %lu-%lu:\r\n",
             h[n], a, b);

      s[sizeof(s)-1] = '\0';
      for (c = a; c <= b; c++) {
          if (xoverinfo &&
            c >= xfirst && c <= xlast && xoverinfo[c - xfirst].text) {
            char *l2 = xoverinfo[c - xfirst].text;
            int d;
            for (d = 0; l2 && d <= n; d++)
                l2 = strchr(l2 + 1, '\t');
            if (l2) {
                char *p;
                (void)strlcpy(s, ++l2, sizeof(s));
                p = strchr(s, '\t');
                if (p)
                  *p = '\0';
            }
            if (l2 && *l2) printf("%lu %s\r\n", c, s);
          }
      }
    } else {
      if (debugmode)
          syslog(LOG_DEBUG, ">221 %s header (from article files) "
               "for postings %lu-%lu:", buf, a, b);
      printf("221 %s header (from article files) for postings %lu-%lu:\r\n",
             buf, a, b);
      for (c = a; c <= b; c++) {
          sprintf(s, "%lu", c);
          l = getheader(s, buf);
          if (l) {
            printf("%lu %s\r\n", c, l);   /* (l && *l) ? l : "(none)" ); */
            free(l);
          }
      }
    }

    free(buf);
    printf(".\r\n");
    return;
}

static void
doxover(char *arg)
{
    unsigned long a, b, art;

    if (!group) {
      nogroup();
      return;
    }

    markinterest(group->name);
    a = group->first;
    b = group->last;
    if (b < a) b = a;

    if (!arg || !*arg)
      a = b = artno;
    else if (!parserange(arg, &a, &b)) {
      printf("502 Usage: OVER first[-[last]]\r\n");
      if (debugmode)
          syslog(LOG_DEBUG, ">502 Usage: OVER first[-[last]]");
      return;
    }

    if (!checkrange(group, a, b))
      return;

    if (!is_pseudogroup(group)) {
      if (xovergroup != group && chdirgroup(group->name, FALSE))
          if (getxover()) xovergroup = group;

      if (NULL == xoverinfo) {
          invalidrange();
          return;
      }
      if (b > xlast)
          b = xlast;
      if (a < xfirst)
          a = xfirst;

      printf("224 Overview information for postings %lu-%lu:\r\n", a, b);
      if (debugmode)
          syslog(LOG_DEBUG, ">224 Overview information for postings %lu-%lu:",
               a, b);
      for (art = a; art <= b; art++) {
          if (xoverinfo[art - xfirst].text)
            printf("%s\r\n", xoverinfo[art - xfirst].text);
      }
      printf(".\r\n");
    } else {
      if ((a > b) || (group->first <= group->last
            ? (a > group->last) || (b < group->first)
            : (a > group->first) || (b < group->first))) {
          printf("420 No articles in specified range.\r\n");
          if (debugmode)
            syslog(LOG_DEBUG, ">420 No articles in specified range.");
          return;
      }

      printf("224 Overview information (pseudo) for postings %lu-%lu:\r\n", 
            group->first, group->first);
      if (debugmode)
          syslog(LOG_DEBUG, ">224 Overview information (pseudo) for "
               "postings %lu-%lu:", group->first, group->first);
      printf("%lu\t"
             "Leafnode placeholder for group %s\t"
             "%s (Leafnode)\t%s\t"
             "<leafnode:placeholder:%s@%s>\t\t1000\t40\r\n", group->first,
             group->name, newsadmin, rfctime(), group->name, fqdn);
      printf(".\r\n");
      if (debugmode)
          syslog(LOG_DEBUG, ">%lu\tLeafnode placeholder for group %s\t"
               "%s (Leafnode)\t%s\t<leafnode:placeholder:%s@%s>\t\t1000\t40",
               group->first, group->name, newsadmin, rfctime(), group->name, fqdn);
    }
}

static void
dolistgroup(const char *arg)
{
    unsigned long art;

    if (arg && *(arg)) {
      struct newsgroup *g;
      g = findgroup(arg);
      if (g) {
          group = g;
          artno = g->first;
      } else  {
          printf("411 No such group: %s\r\n", arg);
          if (debugmode)
            syslog(LOG_DEBUG, ">411 No such group: %s", arg);
          return;
      }
    }

    if (!group) {
      nogroup();
      return;
    }

    /* group = g; */
    markinterest(group->name);
    if ((NULL == xovergroup || xovergroup != group)
          && chdirgroup(group->name, FALSE))
          if (getxover()) xovergroup = group;

    if (is_pseudogroup(group)) {
      printf("211 Article list for %s follows (pseudo)\r\n", group->name);
      if (debugmode)
          syslog(LOG_DEBUG,
               ">211 Article list for %s follows (pseudo)", group->name);
      printf("%lu\r\n", group->first ? group->first : 1);
    } else {
      printf("211 Article list for %s follows\r\n", group->name);
      if (debugmode)
          syslog(LOG_DEBUG, ">211 Article list for %s follows", group->name);
      if (xoverinfo)
          for (art = xfirst; art <= xlast; art++) {
            if (xoverinfo[art - xfirst].text)
                printf("%lu\r\n", art);
          }
    }
    printf(".\r\n");
}

static void
parser(void)
{
    char *arg;
    int n;
    size_t size;

    mgetaline_settimeout(timeout_client);

    while ((cmd = mgetaline(stdin))) {
      if (debug == 1)
          syslog(LOG_DEBUG, "<%s", cmd);

      size = strlen(cmd);
      if (size == 0)
          continue;           /* ignore */
      if (size > MAXLINELENGTH || (long)size > (long)INT_MAX) {
          /* ignore attempts at buffer overflow */
          if (debugmode)
            syslog(LOG_DEBUG, ">500 Dazed and confused");
          printf("500 Dazed and confused\r\n");
          continue;
      }

      /* parse command line */
      n = 0;
      while (isalpha((unsigned char)cmd[n]))
          n++;
      while (isspace((unsigned char)cmd[n]))
          cmd[n++] = '\0';

      arg = cmd + n;

      while (cmd[n])
          n++;
      n--;
      while (n >= 0 && isspace((unsigned char)cmd[n]))
          cmd[n--] = '\0';

      if (!strcasecmp(cmd, "quit")) {
          if (debugmode)
            syslog(LOG_DEBUG, ">205 Always happy to serve!");
          printf("205 Always happy to serve!\r\n");
          return;
      }
      rereadactive();
      if (!strcasecmp(cmd, "article")) {
          doarticle(arg, 3);
      } else if (!strcasecmp(cmd, "head")) {
          doarticle(arg, 2);
      } else if (!strcasecmp(cmd, "body")) {
          doarticle(arg, 1);
      } else if (!strcasecmp(cmd, "stat")) {
          doarticle(arg, 0);
      } else if (!strcasecmp(cmd, "help")) {
          dohelp();
      } else if (!strcasecmp(cmd, "last")) {
          domove(-1);
      } else if (!strcasecmp(cmd, "next")) {
          domove(1);
      } else if (!strcasecmp(cmd, "list")) {
          dolist(arg);
      } else if (!strcasecmp(cmd, "date")) {
          dodate();
      } else if (!strcasecmp(cmd, "mode")) {
          if (debugmode)
            syslog(LOG_DEBUG, ">200 Leafnode %s, pleased to meet you!",
                   version);
          printf("200 Leafnode %s, pleased to meet you!\r\n", version);
      } else if (!strcasecmp(cmd, "newgroups")) {
          donewgroups(arg);
      } else if (!strcasecmp(cmd, "newnews")) {
          if (debugmode)
            syslog(LOG_DEBUG,
                   ">500 NEWNEWS is meaningless for this server");
          printf("500 NEWNEWS is meaningless for this server\r\n");
      } else if (!strcasecmp(cmd, "post")) {
          if (dopost()) break;
      } else if (!strcasecmp(cmd, "slave")) {
          if (debugmode)
            syslog(LOG_DEBUG, ">202 Cool - I always wanted a slave");
          printf("202 Cool - I always wanted a slave\r\n");
      } else if (!strcasecmp(cmd, "xhdr")) {
          doxhdr(arg);
      } else if (!strcasecmp(cmd, "hdr")) {
          doxhdr(arg);
      } else if (!strcasecmp(cmd, "xover")) {
          doxover(arg);
      } else if (!strcasecmp(cmd, "over")) {
          doxover(arg);
      } else if (!strcasecmp(cmd, "listgroup")) {
          dolistgroup(arg);
      } else if (!strcasecmp(cmd, "group")) {
          if (dogroup(arg)) break;
      } else {
          if (debugmode)
            syslog(LOG_DEBUG, ">500 Unknown command");
          printf("500 Unknown command\r\n");
      }
      if (ferror(stdout) || fflush(stdout)) {
          syslog(LOG_ERR, "Cannot write to client.");
          break;
      }
    }
    if (debugmode)
      syslog(LOG_DEBUG, "Client timeout, disconnecting.");

    /* There was once a 400 error message here. It confused broken
     * clients, most notably, tin.
     * Future NNTP drafts command that we don't send stuff back on
     * timeout, so we anticipate these. */
}

int
main(int argc, char **argv)
{
    socklen_t fodder;
    char peername[256]; /* RATS: ignore */
#ifdef HAVE_IPV6
    char *st;
    int h_err;
#define ADDRLEN INET6_ADDRSTRLEN
    union sockaddr_union su;
#else
    struct hostent *he;
#ifdef INET_ADDRSTRLEN
#define ADDRLEN INET_ADDRSTRLEN
#else
#define ADDRLEN 16
#endif
    struct sockaddr_in sa;
#endif
    char peerip[ADDRLEN]; /* RATS: ignore */
    char ownip[ADDRLEN]; /* RATS: ignore */
    char origfqdn[FQDNLEN + 1]; /* RATS: ignore */

    ln_log_use_console(0); /* disable console logging */
    (void)argc;   /* quiet compiler warning */
    myopenlog("leafnode");

    /* this gets the actual hostname */
    if (!initvars(argv[0]))
      exit(1);

    artno = 0;
    verbose = 0;
    (void)umask(2);

    /* this reads the host name from the config file */
    if (!readconfig(1)) {
      const char *m = "503 Unable to read configuration file, exiting; the server's syslog should have more information.";
      printf("%s\r\n", m);
      syslog(LOG_ERR, "%s", m);
      exit(1);
    }
    freeservers();

    strcpy(origfqdn, fqdn); /* same size buffer */ /* RATS: ignore */

    /* get own name */
#ifdef HAVE_IPV6
    fodder = sizeof(union sockaddr_union);
    if (0 == getsockname(0, (struct sockaddr *)&su, &fodder)) {
      if (su.sin.sin_family == AF_INET6)
          inet_ntop(AF_INET6, &su.sin6.sin6_addr, ownip, sizeof(ownip));
      else
          inet_ntop(AF_INET, &su.sin.sin_addr, ownip, sizeof(ownip));

      if ((st = masock_sa2name((struct sockaddr *)&su, &h_err))) {
          xstrlcpy(fqdn, st, sizeof(fqdn));
          free(st);
      }
    }
#else
    fodder = sizeof(struct sockaddr_in);
    if (0 == getsockname(0, (struct sockaddr *)&sa, &fodder)) {
      he = gethostbyaddr((char *)&sa.sin_addr.s_addr,
                     sizeof(sa.sin_addr.s_addr), AF_INET);
      *fqdn = '\0';
      (void)xstrlcpy(fqdn,
            he && he->h_name ? he->h_name : inet_ntoa(sa.sin_addr),
            sizeof(fqdn));
      strcpy(ownip, inet_ntoa(sa.sin_addr));
    }
#endif
    else {
      strcpy(ownip, "no IP");
    }

    /* get remote name */
#ifdef HAVE_IPV6
    fodder = sizeof(union sockaddr_union);
    if (0 == getpeername(0, (struct sockaddr *)&su, &fodder)) {
      if (su.sa.sa_family == AF_INET6)
          inet_ntop(AF_INET6, &su.sin6.sin6_addr, peername, sizeof(peername));
      else
          inet_ntop(AF_INET, &su.sin.sin_addr, peername, sizeof(peername));

      strcpy(peerip, peername);

      if ((st = masock_sa2name((struct sockaddr *)&su, &h_err))) {
          xstrlcpy(peername, st, sizeof(peername));
          free(st);
      }

    }
#else
    fodder = sizeof(struct sockaddr_in);
    if (0 == getpeername(0, (struct sockaddr *)&sa, &fodder)) {
      he = gethostbyaddr((char *)&sa.sin_addr.s_addr,
                     sizeof(sa.sin_addr.s_addr), AF_INET);
      (void)xstrlcpy(peername,
            he && he->h_name ? he->h_name : inet_ntoa(sa.sin_addr),
            sizeof(peername));
      strcpy(peerip, inet_ntoa(sa.sin_addr));
    }
#endif
    else {
      if (errno == ENOTSOCK) {
          strcpy(peername, "(local file)");
          strcpy(peerip, "no IP");
      } else {
          strcpy(peerip, "unknown");
          strcpy(peername, "(unknown)");
      }
    }

    syslog(LOG_INFO, "connect from %s (%s) to %s (%s) (my fqdn: %s)",
          peername, peerip, fqdn, ownip, origfqdn);

    if (allowstrangers == 0 && checkpeerlocal(0) != 1) {
      unsigned int i = 5;
      syslog(LOG_NOTICE, "Denying access from address outside the local networks. (Check config.example.)");
      while (i) i = sleep(i);
      printf("502 Remote access denied.\n");
      exit(0);
    }

    printf("200 Leafnode NNTP Daemon, version %s "
          "running at %s (my fqdn: %s)\r\n",
          version, fqdn, origfqdn);
    if (fflush(stdout)) exit(0);

    strcpy(fqdn, origfqdn);

    rereadactive();

    parser();

    (void)fflush(stdout);
    freeactive(active);
    freexover();
    freeconfig();
    sleep(1); /* protect against process ID induced file name collisions */
    exit(0);
}

Generated by  Doxygen 1.6.0   Back to index