#ifndef lint
static char * scsid = "@(#)$Id: nntpxfer.c,v 1.18 1996/01/09 12:40:28 sob Exp sob $";
#endif
/***************************************************************************
This work in its current form is Copyright 1996 Stan Barber
and is based on the work of Brian Krantor of the University of California
at San Diego. This software may be distributed freely as long as no profit is
made from such distribution and this notice is reproducted in whole.
***************************************************************************
This software is provided on an "as is" basis with no guarantee of 
usefulness or correctness of operation for any purpose, intended or
otherwise. The author is in no way liable for this software's performance
or any damage it may cause to any data of any kind anywhere.
***************************************************************************
*/

/*
 * 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

#include <sys/types.h>
#ifdef DIRENT
#include <dirent.h>
#else
#ifdef NDIR
#ifdef M_XENIX
#include <sys/ndir.h>
#else
#include <ndir.h>
#endif
#else
#include <sys/dir.h>
#endif
#endif
#ifdef USG
#include <time.h>
#else
#include <sys/time.h>
#endif

#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 <syslog.h>
#endif
#endif

#ifdef DBM
# ifdef DBZ
#   include <dbz.h>
#else /* DBZ */
# undef NULL
# include <dbm.h>
# undef NULL
# define NULL	0
#endif /* DBZ */
#endif  /* DBM */

#ifdef NDBM
#include <ndbm.h>
#include <fcntl.h>
static DBM *db = NULL;
#endif
#ifndef TIMEOUT
#define TIMEOUT (30*60)
#endif
#ifndef MAX_ARTICLES
#define MAX_ARTICLES 4096
#endif
#ifdef linux
#define __SERR _IO_ERR_SEEN
#endif
char	*malloc();
char	*strcpy();
char	*strcat();
char	*rindex();
u_long	inet_addr();

#ifdef __FreeBSD__
#include <unistd.h>
#else
extern int errno;
#endif
char *artlist[MAX_ARTICLES];
int server;			/* stream socket to the nntp server */
FILE * rd_fp, * wr_fp;
int newart, dupart, misart;
char * Pname;

main(argc, argv)
int argc;
char *argv[];
{
	FILE *dtfile;		/* where last xfer date/time stored */
	char buf[BUFSIZ];
	char lastdate[16];
	char distributions[BUFSIZ];
	char dtname[128];
	char newsgroups[BUFSIZ];
	char lasttime[16];
	int i;
	int omitupdate = 0;		/* 1 = don't update datetime */
	long clock;
	long newdate, newtime;
	struct tm *now;
	Pname = ((Pname = rindex(argv[0], '/')) ? Pname + 1 : argv[0]);
	/* 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")
	*/

	if (argc != 2 && argc != 5 && argc != 6)
		{
		(void) printf("Usage: %s host [groups YYMMDD HHMMSS [<dist>]]\n",
			argv[0]);
		exit(1);
		}
	
	if (argc > 2)
		{
		omitupdate=1;
		(void) strcpy(newsgroups, argv[2]);
		(void) strcpy(lastdate, argv[3]);
		(void) strcpy(lasttime, argv[4]);
		(void) strcpy(distributions, "");
		if (argc > 5)
			(void) strcpy(distributions, argv[5]);
		}
	else
		{
		(void) sprintf(dtname, "%s/nntp.%s",SPOOLDIR,argv[1]);
		dtfile = fopen(dtname, "r");
		if (dtfile == (FILE *) 0)
			{
			(void) printf("%s not found; using * 860101 000000 \n", 
				dtname);
			(void) strcpy(newsgroups, "*");
			(void) strcpy(lastdate, "860101");
			(void) strcpy(lasttime, "000000");
			(void) strcpy(distributions, "");
			}
		else
			{
			if (fscanf(dtfile, "%s %s %s %s",
				newsgroups, lastdate, lasttime, distributions) < 3)
				{
				(void) printf("%s invalid; using * 860101 000000\n",
					dtname);
				(void) strcpy(newsgroups, "*");
				(void) strcpy(lastdate, "860101");
				(void) strcpy(lasttime, "000000");
				(void) strcpy(distributions, "");
				}
			(void) fclose(dtfile);
			}
		clock = time((long *)0);
		now = gmtime(&clock);

/* DG 9/8/98 - use tm_year %100 to avoid overflow in Y2K - see man 3 ctime */

		newdate = (now->tm_year %100 * 10000) + 
			((now->tm_mon + 1) * 100) + now->tm_mday;
		newtime = (now->tm_hour * 10000) +
			(now->tm_min * 100) + now->tm_sec;
#ifdef DEBUG
	printf("server is %s\n",argv[1]);
	printf("lastdate is %s\n",lastdate);
	printf("lasttime is %s\n",lasttime);
	printf("newsgroups is '%s'\n",newsgroups);
	printf("distributions is '%s'\n",distributions);
#endif
		}
#ifdef SYSLOG
#ifdef BSD_42
	openlog("nntpxfer", LOG_PID);
#else
	openlog("nntpxfer", LOG_PID, SYSLOG);
#endif
#endif

#ifdef DBM
	if (dbminit(HISTORY_FILE) < 0)
		{
#ifdef SYSLOG
		syslog(LOG_ERR,"couldn't open history file: %m");
#else
		perror("nntpxfer: couldn't open history file");
#endif
		exit(1);
		}
#endif
#ifdef NDBM
 	if ((db = dbm_open(HISTORY_FILE, O_RDONLY, 0)) == NULL) 
 		{
#ifdef SYSLOG
 		syslog(LOG_ERR,"couldn't open history file: %m");
#else
 		perror("nntpxfer: couldn't open history file");
#endif
 		exit(1);
 		}
#endif
	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
	printf("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) printf("%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);
		}


	/* first, tell them we're a slave process to get priority */
	sockwrite("SLAVE");
	(void) sockread(buf);
#ifdef DEBUG
	(void) printf("%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
		(void) close(server);
		exit(1);
		}
	
	/* now, ask for a list of new articles */
	if (strlen(distributions))
		(void) sprintf(buf,"NEWNEWS %s %s %s GMT <%s>", 
			newsgroups, lastdate, lasttime, distributions);
	else
		(void) sprintf(buf,"NEWNEWS %s %s %s GMT", 
			newsgroups, lastdate, lasttime);
	sockwrite(buf);
	(void) sockread(buf);
#ifdef DEBUG
	(void) printf("%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
		(void) close(server);
		exit(1);
		}
	/* and here comes the list, terminated with a "." */
#ifdef DEBUG
	(void) printf("data\n");
#endif
	dupart = newart = 0;
	while (1)
		{
		(void) sockread(buf);
		if (!strcmp(buf,"."))
			break;
		if (wewant(buf))
			{
			if (newart >= MAX_ARTICLES)
				{
				omitupdate=1;
				continue;
				}
			artlist[newart] = malloc((unsigned)(strlen(buf)+1));
			(void) strcpy(artlist[newart], buf);
			newart++;
			}
		else
			dupart++;
		}
#ifdef DEBUG
	(void) printf(".\n%d new, %d dup articles\n", newart, dupart);
#endif

	/* now that we know which articles we want, retrieve them */
	for (i=0; i < newart; i++)
		(void) artfetch(artlist[i]);

#ifdef DEBUG
	(void) printf("%d missing articles\n", misart);
#endif
	/* we're all done, so tell them goodbye */
	sockwrite("QUIT");
	(void) sockread(buf);
#ifdef DEBUG
	(void) printf("%s\n", buf);
#endif
	if (buf[0] != '2')	/* uh-oh, something's wrong! */
		{
#ifdef SYSLOG
		syslog(LOG_NOTICE,"error: got '%s'", buf);
#else
		(void) printf("%s: error: got '%s'\n", Pname,buf);
#endif
		(void) close(server);
		exit(1);
		}
	(void) close(server);

	/* do we want to update the timestamp file? */
	if (!omitupdate)
		{
		(void) sprintf(buf, "%s %06d %06d %s\n",
			newsgroups, newdate, newtime, distributions);
#ifdef DEBUG
		(void) printf("updating %s:\n\t%s\n", dtname, buf);
#endif
		dtfile = fopen(dtname, "w");
		if (dtfile == (FILE *) 0)
			{
			perror(dtname);
			exit(1);
			}
		(void) fputs(buf,dtfile);
		(void) fclose(dtfile);
		}
	exit(0);
}

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

	/* now, ask for the article */
	(void) sprintf(buf,"ARTICLE %s", articleid);
	sockwrite(buf);
	(void) sockread(buf);
#ifdef DEBUG
	(void) printf("%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) printf("%s: protocol error: got '%s'\n", Pname, buf);
#endif
		(void) close(server);
		exit(1);
		}
#ifdef DEBUG
	(void) printf("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) printf("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) printf(".\n%d lines\n", lines);
#endif
	(void) fflush(inews);
	(void) pclose(inews);
	return(0);
        }

static	jmp_buf	SFGstack;
/* ARGUSED */
static SIGRET
to_sfgets(dummy)
int dummy;
{
	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);
#ifdef apollo
		rd_fp->_flag |= _SIERR;
#else
# if defined(BSD_44) && !defined(__osf__)
		rd_fp->_flags |= __SERR;
# else
		rd_fp->_flag |= _IOERR;		/* set stdio error */
# endif
#endif
#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) printf(">>> %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;
	{
#if defined(DBM) || defined(NDBM)
	datum k, d;
#else
	FILE *k;
	char *histfile();
	FILE *histfp;		/* USG history file */
	char line[BUFSIZ];
	int len;
#endif
	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
#if defined(DBM) || defined(NDBM)
	k.dptr = id;
	k.dsize = strlen(articleid) + 1;

#ifdef DBM
	d = fetch(k);
#else
 	d = dbm_fetch(db, k);
#endif
	if (d.dptr)
		{
#ifdef DEBUG
		(void) printf("dup: '%s'\n", articleid);
#endif
		return(0);
		}
#ifdef DEBUG
	(void) printf("new: '%s'\n", articleid);
#endif
	return(1);
#else
	histfp = fopen(histfile(articleid), "r");
	if (histfp == NULL) 
		{
#ifdef DEBUG
		(void) printf("new: '%s'\n", articleid);
#endif
		return(1);
		}
	len = strlen(articleid);
	while (fgets(line, sizeof (line), histfp))
		if (!strncmp(articleid, line, len))
			break;

	if (feof(histfp)) {
		(void) fclose(histfp);
#ifdef DEBUG
		(void) printf("new: '%s'\n", articleid);
#endif
		return (1);
	}
	(void) fclose(histfp);
#ifdef DEBUG
	(void) printf("dup: '%s' %s\n", articleid,line);
#endif
	return(0);
#endif
}

#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;
{
#ifndef __FreeBSD__
	extern int sys_nerr;
	extern char *sys_errlist[];
#endif
	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];
}




syntax highlighted by Code2HTML, v. 0.9.1