#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