#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "nntpread.h"
#include "dbz.h"

#define WDEBUG

#define CNEWS
/*#define PDEBUG /**/
/*#define SDEBUG /**/

#define SS 50

#define MAX 3

#define ART 1
#define STAT 2

char * Pname= "nntpxfer";
FILE * server;
int serv;
char *server_name;

struct buffer {
  int start, end, stacked;
  char *name;
  int list[SS];
} r = {0,0,0,"rd"}, w = {0,0,0,"wr"};

int first, last, curr;
int misart = 0;

int read_next_group(void);
void set_nonblock(int);
void put(struct buffer *, int);
int get(struct buffer *);
void open_server(void);
void do_art(void);
void do_stat(void);
void get_stat(void);
void get_article(void);
void do_read(void);
char *sockread(void);
void sockwrite(char *, ...);
void open_dbase(void);
void update_dbase(void);
void main(int argc, char *argv[]) {
  int i;
  if (argc != 2) {
    fprintf(stderr,"usage: %s <hostname>\n", argv[0]);
    exit(1);
  }

  server_name = argv[1];

  open_server();
  
  open_dbase();

  while (read_next_group()) {
    while (r.stacked || first <= last || w.stacked ) {
      while (w.stacked && r.stacked < MAX )
	do_art();
      while (first <= last && r.stacked < MAX )
	do_stat();
      do_read();
    }
  }
  fclose(server);
  dbmclose();
}

void do_art(void)
{
  sockwrite("ARTICLE %d", get(&w));
  put(&r, ART);
}

void do_stat(void)
{
  if (first > last)
    return;
  sockwrite("STAT %d", first);
  ++first;
  put(&r, STAT);
}

void do_read(void)
{
  int i;
  i = get(&r);
  if (i == ART) {
    get_article();
    return;
  }
  get_stat();
}

void get_stat(void)
{
  char *p, *p1, *buf;
  int i;
  buf = sockread();

  switch(buf[0]) {
  case '4':
    misart++;
  default:
    return;
  case '2':
    p = strchr(buf, ' ');
    if (p) i = atoi(p+1);
    if (p) p = strchr(p+1, ' ');
    if (p) p1 = strchr(p+1, ' ');
    if (p1) p1[0] = 0;
    if (p)
      if (wewant(p+1))
	put(&w, i);
  }
}

void get_article(void)
{
  char *buf;
  FILE *inews;

  buf = sockread();

#ifdef DEBUG
  (void) fprintf(stderr, "%s\n", buf);
#endif
  if (buf[0] == '4')	/* missing article, just skipit */
    {
      misart++;
      return;
    }
  
  if (buf[0] != '2')	/* uh-oh, something's wrong! */
    {
      fprintf(stderr, "Protocol error!\n");
      exit(1);
    }
  
#ifdef DEBUG
  (void) fprintf(stderr, "command: %s\n", RNEWS);
#endif
  if ( (inews = popen(RNEWS, "w")) == (FILE *) 0)
    {
      perror(RNEWS);
      exit(1);
    }
  
  /* and here comes the article, terminated with a "." */
#ifdef DEBUG
  (void) fprintf(stderr, "data\n");
#endif
  while (1)
    {
      buf = sockread();
      if (buf[0] == '.' && buf[1] == '\0')
	break;
#ifdef DEBUG
      lines++;
#endif
      (void) strcat(buf,"\n");
      (void) fputs(((buf[0] == '.') ? buf + 1 : buf),
		   inews);
    }
#ifdef DEBUG
  (void)fprintf(stderr, ".\n%d lines\n", lines);
#endif
  (void) fflush(inews);
  (void) pclose(inews);
  return;
}

/*---------------------*/
void open_server(void) 
{
  char *buf;
  if ((serv = get_tcp_conn(server_name,"nntp")) < 0) 
    {
      fprintf(stderr,"could not open socket");
      exit(1);
    }
  
  server = fdopen(serv, "r");
  buf = sockread();
  if (buf[0] != '2') {
    fprintf(stderr, "Error contacting server.\n");
    exit(1);
  }
}
/*-------------------------*/
				/* Open the database for reading.. */
void open_dbase(void) 
{  
  if (dbminit(HISTORY_FILE) < 0)
    {
      fprintf(stderr,"dbm:couldn't open history file\n");
      exit(1);
    }
}
				/* close it and open it, so we re-read */
				/* it from disk. Just in case it has */
				/* been updated via newsrun. */
void update_dbase(void)
{
  dbmclose();
  open_dbase();
}

/*-------------*/

int
wewant(articleid)
char *articleid;
	{
	datum k, d;

	char id[BUFSIZ];
	char *p;

	/* remove any case sensitivity */
	(void) strcpy(id, articleid);
	p = id;
#ifndef CNEWS
	while (*p)
		{
		if (isupper(*p))
			*p = tolower(*p);
		p++;
		}
#endif

	k.dptr = id;
	k.dsize = strlen(articleid)+1;


	d = dbzfetch(k);
	if (d.dptr)
		{
#ifdef WDEBUG
		(void) fprintf(stderr, "dup: '%s'\n", id);
#endif
		return(0);
		}
#ifdef WDEBUG
	(void) fprintf(stderr, "new: '%s'\n",id);
#endif
	return(1);
}


void sockwrite(char *fmt, ...) {
  char buff[2000];
  va_list v;
  int i;
  va_start(v, fmt);
				/* A command follows.. */
  if (fmt)
    vsprintf(buff, fmt, v);
#ifdef PDEBUG
  fprintf(stderr,"w:'%s'\n", buff);
#endif
  if (write(serv, buff, strlen(buff)) != strlen(buff)) {
    fprintf(stderr, "Write error.dying.\n");
    exit(1);
  }
  if (write(serv,"\r\n", 2) != 2) {
    fprintf(stderr, "Write error.dying.\n");
    exit(1);
  }
  va_end(v);
}


char * sockread(void)
{
  static char buf[2000];
  int rz;
  char *ret;

  ret  = fgets(buf, 2000, server);
  rz = strlen(buf);
  buf[rz-2] = '\0';
#ifdef PDEBUG
  fprintf(stderr,"rd:'%s'\n",buf);
#endif
  if (ret == 0) {
    perror("read");
    fprintf(stderr, "Read error.dying.\n");
    exit(1);
  }
  return buf;
}

  
void put(struct buffer *b, int i)
{
  b->list[b->start++] = i;
  b->start = b->start % SS;
  b->stacked++;
#ifdef SDEBUG
  fprintf(stderr,"\tPUT: %s %d (%d)\n", b->name, i, b->stacked);
#endif
}

int get(struct buffer *b)
{
  int i;
  i = b->list[b->end++];
  --b->stacked;
  b->end = b->end % SS;
#ifdef SDEBUG
  fprintf(stderr, "\tGET: %s %d (%d)\n", b->name, i, b->stacked);
#endif
  return i;
}

char *
errmsg(code)
int code;
{
	extern int sys_nerr;
/*	extern char *sys_errlist[];*/
	static char ebuf[6+5+1];

	if (code > sys_nerr || code < 0) {
		(void) sprintf(ebuf, "Error %d", code);
		return ebuf;
	} else
		return sys_errlist[code];
}

int read_next_group(void)
{
  char b[200];
  char *p, *buf, *bufp;
  do {
    if (!gets(b))
      return 0;
    if (b[0] == '#') {
      printf("%s\n", b);
      fflush(stdout);
      continue;
    }
    p = strchr(b, ' ');
    if (!p) {
      fprintf(stderr, "Invalid input line '%s'\n", b);
      printf("%s", b);
      continue;
    }
    first = atoi(p+1);
    *p++ = 0;
    fprintf(stderr, "About to do group %s (%d)\n:", b, first);
    sockwrite("GROUP %s", b);
    buf = sockread();
#ifdef DEBUG
    (void) fprintf(stderr, "%s\n", buf);
#endif
    if (buf[0] != '2')	/* uh-oh, something's wrong! */
      {
	fprintf(stderr,"protocol error: got '%s'", buf);
	printf("%s %d\n", b, first);
	continue;
      }

    bufp = strchr(buf, ' ') +1;
    bufp = strchr(bufp, ' ') +1;
    if (atoi(bufp) > first)
      first = atoi(bufp);
    bufp = strchr(bufp, ' ')+ 1;
    last = atoi(bufp);
    fprintf(stderr, "First %d, last %d\n", first, last);
    printf("%s %d\n", b, last+1);
    fflush(stdout);
  } while (first > last);
/*  first -= 100; if (first < 1) first = 1;*/
  update_dbase();
  return 1;
}

