/*
 * nntpxfer
 *
 * Connects to the specified nntp server, and transfers all new news
 * since the last successful invocation.
 *
 * last successful invocation date and time are stored in a file at
 * /usr/spool/news/nntp.<hostname> as 
 *	groups YYMMDD HHMMSS distributions\n
 * in case you need to edit it.  You can also override this on 
 * the command line in the same format, in which case the file won't
 * be updated.
 *
 *	Brian Kantor, UCSD 1986
 * (some bug fixes by ambar@athena.mit.edu)
 * Modified to use NNTP distribution conf.h file and nntpxmit's get_tcp_conn.c
 * subroutines so that nntpxfer could be used on more systems.
 * Stan Barber, November 7, 1989 <sob@bcm.tmc.edu>
 *
 */

#include "conf.h"
#ifdef DEBUG
#undef SYSLOG
#endif

#define WDEBUG 1
/* #define DEBUG 1 */

#include <sys/types.h>
#if defined(NDIR) && !defined(linux)
#ifdef M_XENIX
#include <sys/ndir.h>
#else
#include <ndir.h>
#endif
#else
#include <sys/dir.h>
#endif
#ifdef USG
#include <time.h>
#else
#include <sys/time.h>
#endif
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <setjmp.h>
#ifndef NONETDB
#include <netdb.h>
#endif
#include <signal.h>
#ifdef SYSLOG
#ifdef FAKESYSLOG
#include "../server/fakesyslog.h"
#else
#include <sys/syslog.h>
#endif
#endif

#include "dbz.h"

#ifndef TIMEOUT
#define TIMEOUT (30*60)
#endif
#ifndef MAX_ARTICLES
#define MAX_ARTICLES 4096
#endif

char	*malloc();
char	*strcpy();
char	*strcat();
char	*rindex();
long	time();
u_long	inet_addr();

extern int errno;
char *artlist[MAX_ARTICLES];
int server;			/* stream socket to the nntp server */
FILE * rd_fp, * wr_fp;
int newart, dupart, misart;
char * Pname= "nntpxfer";

main(argc, argv)
int argc;
char *argv[];
{
  char buff[200];
  char buf[200];
  if (argc != 2) {
    fprintf(stderr, "Usage: %s <hostname>\n", argv[0]);
    exit(1);
  }

	openlog("nntpxfer", LOG_PID);
	openlog("nntpxfer", LOG_PID, SYSLOG);

	if (dbminit(HISTORY_FILE) < 0)
		{
#ifdef SYSLOG
		syslog(LOG_ERR,"dbm:couldn't open history file: %m");
#else
		perror("nntpxfer: couldn't open history file");
#endif
		exit(1);
		}
	if ((server = get_tcp_conn(argv[1],"nntp")) < 0) 
		{
#ifdef SYSLOG
		syslog(LOG_ERR,"could not open socket: %m");
#else
		perror("nntpxfer: could not open socket");
#endif
		exit(1);
		}

	if ((rd_fp = fdopen(server,"r")) == (FILE *) 0){
#ifdef SYSLOG
		syslog(LOG_ERR,"could not fdopen socket: %m");
#else
		perror("nntpxfer: could not fdopen socket");
#endif
		exit(1);
		}

#ifdef SYSLOG
	syslog(LOG_DEBUG,"connected to nntp server at %s", argv[1]);
#endif
#ifdef DEBUG
	fprintf(stderr, "connected to nntp server at %s\n", argv[1]);
#endif
	/*
	* ok, at this point we're connected to the nntp daemon 
	* at the distant host.
	*/
	/* get the greeting herald */
	(void) sockread(buf);
#ifdef DEBUG
	(void) fprintf(stderr, "%s\n", buf);
#endif
	if (buf[0] != '2')	/* uh-oh, something's wrong! */
		{
#ifdef SYSLOG
		syslog(LOG_NOTICE,"protocol error: got '%s'\n", buf);
#else
		(void) printf("%s: protocol error: got '%s'\n", Pname,buf);
#endif
		(void) close(server);
		exit(1);
		}

  while (fgets(buff, 200, stdin)) {
    char *p;
    if (buff[0] == '#') {
      printf("%s", buff);
      continue;
    }

    p = strchr(buff, ' ');
    if (!p) {
      fprintf(stderr, "Invalid input line.\n");
      printf("%s", buff);
      continue;
    }
    *p++ = 0;
    fprintf(stderr, "About to do group %s (%d)\n:", buff, atoi(p));
    do_group(buff, atoi(p));
  }
  /* we're all done, so tell them goodbye */
  sockwrite("QUIT");
  (void) sockread(buf);
#ifdef DEBUG
  (void) fprintf(stderr, "%s\n", buf);
#endif
  if (buf[0] != '2')	/* uh-oh, something's wrong! */
    {
#ifdef SYSLOG
      syslog(LOG_NOTICE,"error: got '%s'", buf);
#else
      (void) fprintf(stderr, "%s: error: got '%s'\n", Pname,buf);
#endif
      (void) close(server);
      exit(1);
    }
  
  (void) close(server);
  exit(0);
}

do_group(char *name, int first) {
	FILE *dtfile;		/* where last xfer date/time stored */
	char buf[BUFSIZ];
	int last;
	char *bufp;

	char lastdate[16];
	char distributions[BUFSIZ];
	char dtname[128];
	char lasttime[16];
	int i;
	int omitupdate = 0;		/* 1 = don't update datetime */
	long clock;
	long newdate, newtime;
	struct tm *now;

	/* OPTIONS
		argv[1] MUST be the host name
		argv[2-4] MAY be "newsgroups YYMMDD HHMMSS"
			argv[5] MAY be distributions
		(otherwise use 2-4/5 from the file
		"/usr/spool/news/nntp.hostname")
	*/

	/* now, ask for a list of new articles */
	(void) sprintf(buf,"GROUP %s", 
		       name);

	sockwrite(buf);
	(void) sockread(buf);
#ifdef DEBUG
	(void) fprintf(stderr, "%s\n", buf);
#endif
	if (buf[0] != '2')	/* uh-oh, something's wrong! */
	  {
#ifdef SYSLOG
	    syslog(LOG_NOTICE,"protocol error: got '%s'", buf);
#else
	    (void) printf("%s: protocol error: got '%s'\n", Pname,buf);
#endif
	    printf("%s %d\n", name, first);
	    return;
	  }
	/* and here comes the list, terminated with a "." */
#ifdef DEBUG
	(void) fprintf(stderr, "data\n");
#endif
	
	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);

	/* now that we know which articles we want, retrieve them */
	for (i=first; i < last+1; i++)
	  (void) artfetch(i);

#ifdef DEBUG
	(void) fprintf(stderr, "%d missing articles\n", misart);
#endif
	printf("%s %d\n", name, last+1);
      }

artfetch(articleid)
int articleid;
	{
#ifdef DEBUG
	int lines = 0;
#endif
	char buf[BUFSIZ];
	FILE *inews;
	char *p, *p1;

	sprintf(buf, "STAT %d" , articleid);
	sockwrite(buf);
	(void) sockread(buf);
	switch(buf[0]) {
	case '4':
	  misart++;
	default:
	  return 0;
	case '2':
	  p = strchr(buf, ' ');
	  if (p) p = strchr(p+1, ' ');
	  if (p) p1 = strchr(p+1, ' ');
	  if (p1) p1[0] = 0;
	  if (p)
	    if (!wewant(p+1)) return 0;
	}
	/* now, ask for the article */
	(void) sprintf(buf,"ARTICLE %d", articleid);
	sockwrite(buf);
	(void) sockread(buf);
#ifdef DEBUG
	(void) fprintf(stderr, "%s\n", buf);
#endif
	if (buf[0] == '4')	/* missing article, just skipit */
		{
		misart++;
		return(0);
		}

	if (buf[0] != '2')	/* uh-oh, something's wrong! */
		{
#ifdef SYSLOG
		syslog(LOG_NOTICE,"protocol error: got '%s'", buf);
#else
		(void) fprintf(stderr, "%s: protocol error: got '%s'\n", Pname, buf);
#endif
		return 0;
		}
#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)
		{
		(void) sockread(buf);
		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(0);
        }

static	jmp_buf	SFGstack;

static SIGRET
to_sfgets()
{
	longjmp(SFGstack, 1);
}

int
sockread(buf)
char *buf;
{
	int	esave, rz;
	char * ret;
	if (setjmp(SFGstack)) {
		(void) alarm(0);	/* reset alarm clock */
		(void) signal(SIGALRM, SIG_DFL);
/*		rd_fp->_flag |= _IOERR;	/* set stdio error */
#ifndef ETIMEDOUT
		errno = EPIPE;		/* USG doesn't have ETIMEDOUT */
#else
		errno = ETIMEDOUT;		/* connection timed out */
#endif
#ifdef SYSLOG
		syslog(LOG_ERR,"nntpxfer: read error on server socket: %m");
#else
		(void) perror("nntpxfer: read error on server socket");
#endif
		(void) close(server);
		exit(1);
	}
	(void) signal(SIGALRM, to_sfgets);
	(void) alarm(TIMEOUT);
	ret  = fgets(buf, BUFSIZ, rd_fp);
	esave = errno;
	(void) alarm(0);			/* reset alarm clock */
	(void) signal(SIGALRM, SIG_DFL);	/* reset SIGALRM */
	errno = esave;
	rz = strlen(buf);
	buf[rz-2] = '\0';
	if (ret  == (char * ) 0) {
#ifdef SYSLOG
    		syslog(LOG_ERR,"nntpxfer: read error on server socket: %m");
#else
		(void) perror("nntpxfer: read error on server socket");
#endif
		(void) fclose(rd_fp);
		exit(1);
	}
	return(0);
}

sockwrite(buf)
char *buf;
	{
	register int sz;
	char buf2[BUFSIZ];
#ifdef DEBUG
	(void) fprintf(stderr, ">>> %s\n", buf);
#endif
	(void) strcpy(buf2,buf);
	(void) strcat(buf2,"\r\n");
	sz = strlen(buf2);
	if (write(server,buf2,sz) != sz)
		{
#ifdef SYSLOG
		syslog(LOG_ERR,"nntpxfer: write error on server socket");
#else
		(void) printf("nntpxfer: write error on server socket\n");
#endif
		(void) close(server);
		exit(1);
		}
	}

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", articleid);
#endif
		return(0);
		}
#ifdef WDEBUG
	(void) fprintf(stderr, "new: '%s'\n", articleid);
#endif
	return(1);
}

#ifdef USGHIST
/*
** Generate the appropriate history subfile name
*/
char *
histfile(hline)
char *hline;
{
	char chr;	/* least significant digit of article number */
	static char subfile[BUFSIZ];

	chr = findhfdigit(hline);
	sprintf(subfile, "%s.d/%c", HISTORY_FILE, chr);
	return subfile;
}

findhfdigit(fn)
char *fn;
{
	register char *p;
	register int chr;
	extern char * index();

	p = index(fn, '@');
	if (p != NULL && p > fn)
		chr = *(p - 1);
	else
		chr = '0';
	if (!isdigit(chr))
		chr = '0';
	return chr;
}
#endif
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];
}

