NNTP 1.5.11 patches for ISC 2.2.1

Stan Barber nntp at tmc.edu
Wed Mar 27 16:24:28 AEST 1991


The following shar contains new versions of files that add compatibility with
ISC 2.2 with TCP for the NNTP server. This is an UNOFFICAL patch. Please
define LAI_TCP in common/conf.h to make use of these modifications. They
will be available on the "archive-server at bcm.tmc.edu" as nntp.isc.shar in 
the public directory.



#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	server/timer.c
#	server/subnet.c
#	xmit/nntpxmit.c
#	xmit/remote.c
#	xfer/nntpxfer.c
# This archive created: Tue Mar 26 09:40:27 1991
:
export PATH; PATH=/bin:$PATH
if test -d 'server'
then
echo "Entering directory 'server'"
cd 'server'
echo shar: extracting "'timer.c'" '(4008 characters)'
sed 's/^	X//' << \SHAR_EOF > 'timer.c'
	X/*
	X * Machinery to run routines off timers.
	X */
	X#include "common.h"
	X
	X#ifdef TIMERS
	X#ifndef lint
	Xstatic char rcsid[] =
	X    "@(#) $Header: timer.c,v 1.3 91/03/19 03:02:41 sob Exp $ (NNTP with TIMERS)";
	X#endif
	X#else
	X#ifndef lint
	Xstatic char rcsid[] =
	X    "@(#) $Header: timer.c,v 1.3 91/03/19 03:02:41 sob Exp $ (NNTP without TIMERS)";
	X#endif
	X#endif
	X
	X#ifdef TIMERS
	X#include <sys/time.h>
	X#include "timer.h"
	X#ifdef USG
	X#ifdef LAI_TCP
	X#include <sys/bsdtypes.h>
	X#define BSDSELECT
	X#endif
	X#else
	X#ifndef FD_SETSIZE
	X/* Forward compatability */
	X#define FD_SET(n, p)    ((p)->fds_bits[0] |= (1<<(n)))
	X#define FD_CLR(n, p)    ((p)->fds_bits[0] &= ~(1<<(n)))
	X#define FD_ISSET(n, p)  ((p)->fds_bits[0] & (1<<(n)))
	X#define FD_ZERO(p)      ((p)->fds_bits[0] = 0)
	X#define BSDSELECT
	X#endif
	X#endif
	X/* non-portable */
	X#define BUFFERED_DATA(f) ((f)->_cnt > 0)
	X
	Xstatic long lastsecs;
	X
	X/*
	X * Should be called before first call to timer_sleep()
	X */
	Xvoid
	Xtimer_init(timers, ntimer)
	X	register struct timer *timers;
	X	register int ntimer;
	X{
	X	register int i;
	X	register struct timer *tp;
	X
	X#ifdef SYSLOG
	X	if (ntimer <= 0)
	X		syslog(LOG_ERR,
	X		    "timer_init(): configuration error, %d timers\n", ntimer);
	X#endif
	X
	X	/* Reset all timers */
	X	for (i = ntimer, tp = timers; i > 0; --i, ++tp)
	X		tp->left = tp->seconds;
	X
	X	/* Start clock */
	X	lastsecs = time((long *)0);
	X}
	X
	X/*
	X * Sleep until input or next timer needs to be run and then run any
	X * expired timers. Returns true if input is available to be read.
	X */
	Xint
	Xtimer_sleep(timers, ntimer)
	X	register struct timer *timers;
	X	register int ntimer;
	X{
	X	register int i, n;
	X	register struct timer *tp;
	X	register long secs;
	X#ifndef BSDSELECT
	X	long timeout;
	X	long readfds;
	X#else
	X	register struct timeval *timeoutp;
	X	struct timeval timeout;
	X	fd_set readfds;
	X#endif
	X
	X	/* No need to do the select if there are characters in the buffer */
	X	if (BUFFERED_DATA(stdin))
	X		return(1);
	X
	X	/* Length of next timeout is minimum of all "timers" */
	X#ifndef BSDSELECT
	X	timeout = -1;
	X	for (i = ntimer, tp = timers; i > 0; --i, ++tp)
	X		if (tp->left >= 0 &&
	X		    (tp->left < timeout || timeout < 0))
	X			timeout = tp->left;
	X
	X	/* If active timeouts (this can easily happen), block until input */
	X	if (timeout < 0)
	X		timeout = 0;
	X#ifdef EXCELAN
	X	readfds = 1<<(fileno(stdin));
	X	timeout = timeout * 1000;     /* timeout needs to be in milliseconds */
	X#endif /* EXCELAN */
	X#else
	X	timeout.tv_sec = -1;
	X	timeout.tv_usec = 0;
	X	for (i = ntimer, tp = timers; i > 0; --i, ++tp)
	X		if (tp->left >= 0 &&
	X		    (tp->left < timeout.tv_sec || timeout.tv_sec < 0))
	X			timeout.tv_sec = tp->left;
	X
	X	/* If active timeouts (this can easily happen), block until input */
	X	if (timeout.tv_sec < 0)
	X		timeoutp = 0;
	X	else
	X		timeoutp = &timeout;
	X
	X	/* Do select */
	X	FD_ZERO(&readfds);
	X	FD_SET(fileno(stdin), &readfds);
	X#endif /* BSDSELECT */
	X	errno = 0;
	X#ifdef EXCELAN
	X	n = select(fileno(stdin) + 1, &readfds, (long*)0, timeout);
	X#else
	X	n = select(fileno(stdin) + 1,
	X	    &readfds, (fd_set*)0, (fd_set*)0, timeoutp);
	X#endif
	X	/* "Interrupted system call" isn't a real error */
	X	if (n < 0 && errno != EINTR) {
	X#ifdef SYSLOG
	X		syslog(LOG_ERR, "%s read select: %m", hostname);
	X#endif
	X		exit(1);
	X	}
	X
	X	/* Calculate off seconds since last time */
	X	secs = time((long *)0) - lastsecs;
	X	if (secs < 0)
	X		secs = 0;
	X
	X	/* Subtract time from "timers" that have time remaining */
	X	for (i = ntimer, tp = timers; i > 0; --i, ++tp)
	X		if (tp->left > 0 && (tp->left -= secs) < 0)
	X			tp->left = 0;
	X
	X	/* Update lastsecs */
	X	lastsecs += secs;
	X
	X	/* If we have input, reset clock on guys that like it that way */
	X	if (n > 0)
	X		for (i = ntimer, tp = timers; i > 0; --i, ++tp)
	X			if (tp->resetoninput)
	X				tp->left = tp->seconds;
	X
	X	/* Process "timers" that have timed out */
	X	for (i = ntimer, tp = timers; i > 0; --i, ++tp) {
	X		if (tp->left == 0) {
	X			(tp->subr)();
	X			/* resetoninput guys only get "reset on input" */
	X			if (tp->resetoninput)
	X				tp->left = -1;
	X			else
	X				tp->left = tp->seconds;
	X		}
	X	}
	X
	X	/* Indicate no input */
	X	if (n <= 0)
	X		return(0);
	X	return(1);
	X	
	X}
	X#endif
SHAR_EOF
if test 4008 -ne "`wc -c < 'timer.c'`"
then
	echo shar: error transmitting "'timer.c'" '(should have been 4008 characters)'
fi
echo shar: extracting "'subnet.c'" '(6645 characters)'
sed 's/^	X//' << \SHAR_EOF > 'subnet.c'
	X#ifndef lint
	Xstatic	char	*sccsid = "@(#)$Header: subnet.c,v 1.9 91/03/19 03:02:30 sob Exp $";
	X#endif
	X
	X#include "../common/conf.h"
	X
	X#ifdef SUBNET
	X
	X#include <sys/types.h>
	X#ifdef LAI_TCP
	X#include <sys/bsdtypes.h>
	X#include <sys/stream.h>
	X#endif
	X#include <sys/socket.h>
	X#include <netinet/in.h>
	X#ifndef NETMASK
	X#include <net/if.h>
	X#endif
	X#ifdef LAI_TCP
	X#include <sys/sioctl.h>
	X#else
	X#include <sys/ioctl.h>
	X#endif
	X/*
	X * The following routines provide a general interface for
	X * subnet support.  Like the library function "inet_netof",
	X * which returns the standard (i.e., non-subnet) network
	X * portion of an internet address, "inet_snetof" returns
	X * the subnetwork portion -- if there is one.  If there
	X * isn't, it returns 0.
	X *
	X * Subnets, under 4.3, are specific to a given set of
	X * machines -- right down to the network interfaces.
	X * Because of this, the function "getifconf" must be
	X * called first.  This routine builds a table listing
	X * all the (internet) interfaces present on a machine,
	X * along with their subnet masks.  Then when inet_snetof
	X * is called, it can quickly scan this table.
	X *
	X * Unfortunately, there "ain't no graceful way" to handle
	X * certain situations.  For example, the kernel permits
	X * arbitrary subnet bits -- that is, you could have a
	X * 22 bit network field and a 10 bit subnet field.
	X * However, due to braindamage at the user level, in
	X * such sterling routines as getnetbyaddr, you need to
	X * have a subnet mask which is an even multiple of 8.
	X * Unless you are running with class C subnets, in which
	X * case it should be a multiple of 4.  Because of this rot,
	X * if you have non-multiples of 4 bits of subnet, you should
	X * define DAMAGED_NETMASK when you compile.  This will round
	X * things off to a multiple of 8 bits.
	X *
	X * Finally, you may want subnet support even if your system doesn't
	X * support the ioctls to get subnet mask information.  If you want
	X * such a thing, you can define NETMASK to be a constant that is
	X * the subnet mask for your network.
	X *
	X * And don't *even* get me started on how the definitions of the inet_foo()
	X * routines changed between 4.2 and 4.3, making internet addresses
	X * be unsigned long vs. struct in_addr.  Don't blame me if this
	X * won't lint...
	X */
	X
	X/*
	X * One structure for each interface, containing
	X * the network number and subnet mask, stored in HBO.
	X */
	Xstruct in_if {
	X	u_long	i_net;		/* Network number, shifted right */
	X	u_long	i_subnetmask;	/* Subnet mask for this if */
	X	int	i_bitshift;	/* How many bits right for outside */
	X};
	X
	X/*
	X * Table (eventually, once we malloc) of
	X * internet interface subnet information.
	X */
	Xstatic	struct in_if	*in_ifsni;
	X
	Xstatic	int		if_count;
	X
	X/*
	X * Get the network interface configuration,
	X * and squirrel away the network numbers and
	X * subnet masks of each interface.  Return
	X * number of interfaces found, or -1 on error.
	X * N.B.: don't call this more than once...
	X */
	X
	Xgetifconf()
	X{
	X#ifndef NETMASK
	X	register int	i, j;
	X	int		s;
	X	struct ifconf	ifc;
	X	char		buf[1024];
	X	register struct ifreq	*ifr;
	X        int		inet_netof();
	X	u_long		addr;
	X
	X	/*
	X	 * Find out how many interfaces we have, and malloc
	X	 * room for information about each one.
	X	 */
	X
	X	s = socket(AF_INET, SOCK_DGRAM, 0);
	X	if (s < 0)
	X		return (-1);
	X
	X	ifc.ifc_buf = buf;
	X	ifc.ifc_len = sizeof (buf);
	X
	X	if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
	X		(void) close(s);
	X		return (-1);
	X	}
	X
	X	/*
	X	 * if_count here is the count of possible
	X	 * interfaces we may be interested in... actual
	X	 * interfaces may be less (some may not be internet,
	X	 * not all are necessarily up, etc.)
	X	 */
	X
	X	if_count = ifc.ifc_len / sizeof (struct ifreq);
	X
	X	in_ifsni = (struct in_if *)
	X		malloc((unsigned) if_count * sizeof (struct in_if));
	X	if (in_ifsni == 0) {
	X		(void) close(s);
	X		return (-1);
	X	}
	X
	X	for (i = j = 0; i < if_count; ++i) {
	X		struct sockaddr_in *s_in;
	X
	X		ifr = &ifc.ifc_req[i];
	X		if (ioctl(s, SIOCGIFFLAGS, ifr) < 0)
	X			continue;
	X		if ((ifr->ifr_flags & IFF_UP) == 0)
	X			continue;
	X		if (ioctl(s, SIOCGIFADDR, ifr) < 0)
	X			continue;
	X		if (ifr->ifr_addr.sa_family != AF_INET)
	X			continue;
	X		s_in = (struct sockaddr_in *) &ifr->ifr_addr;
	X		addr = s_in->sin_addr.s_addr;
	X		in_ifsni[j].i_net = inet_netof(s_in->sin_addr);
	X		if (ioctl(s, SIOCGIFNETMASK, ifr) < 0)
	X			continue;
	X		s_in = (struct sockaddr_in *) &ifr->ifr_addr;
	X		in_ifsni[j].i_subnetmask = ntohl(s_in->sin_addr.s_addr);
	X		/*
	X		 * The following should "never happen".  But under SunOS
	X		 * 3.4, along with the rest of their broken networking code,
	X		 * SIOCGIFNETMASK can get a netmask which is 0.  There
	X		 * really isn't anything that "right" that we can do
	X		 * about it, so we'll set their subnet mask to be their
	X		 * *net*work mask.  Which may or may not be right.
	X		 */
	X		if (in_ifsni[j].i_subnetmask == 0) {
	X			addr = ntohl(addr);
	X			if (IN_CLASSA(addr))
	X				in_ifsni[j].i_subnetmask = IN_CLASSA_NET;
	X			else if (IN_CLASSB(addr))
	X				in_ifsni[j].i_subnetmask = IN_CLASSB_NET;
	X			else if (IN_CLASSC(addr))
	X				in_ifsni[j].i_subnetmask = IN_CLASSC_NET;
	X			else			/* what to do ... */
	X				in_ifsni[j].i_subnetmask = IN_CLASSC_NET;
	X		} else
	X			in_ifsni[j].i_bitshift = bsr(in_ifsni[j].i_subnetmask);
	X		j++;
	X	}
	X
	X	if_count = j;
	X
	X	(void) close(s);
	X
	X	return (if_count);
	X
	X#else	/* hard-coded subnets */
	X
	X	if_count = 1;
	X
	X	in_ifsni = (struct in_if *) malloc(if_count * sizeof (struct in_if));
	X	if (in_ifsni == 0) {
	X		return (-1);
	X	}
	X	in_ifsni[0].i_net = 0;
	X	in_ifsni[0].i_subnetmask = NETMASK;
	X	in_ifsni[0].i_bitshift = bsr(in_ifsni[0].i_subnetmask);
	X	return (if_count);
	X#endif
	X}
	X
	X
	X/*
	X * Return the (sub)network number from an internet address.
	X * "in" is in NBO, return value in host byte order.
	X * If "in" is not a subnet, return 0.
	X */
	X
	Xu_long
	Xinet_snetof(in)
	X	u_long	in;
	X{
	X	register int	j;
	X	register u_long	i = ntohl(in);
	X	register u_long	net;
	X	int		inet_netof(), inet_lnaof();
	X	struct in_addr in_a;
	X
	X	in_a.s_addr = in;
	X	net = inet_netof(in_a);
	X
	X	/*
	X	 * Check whether network is a subnet;
	X	 * if so, return subnet number.
	X	 */
	X	for (j = 0; j < if_count; ++j)
	X#ifdef NETMASK
	X		if (1) {
	X#else
	X		if (net == in_ifsni[j].i_net) {
	X#endif
	X			net = i & in_ifsni[j].i_subnetmask;
	X			in_a.s_addr = htonl(net);
	X			if (inet_lnaof(in_a) == 0)
	X				return (0);
	X			else
	X				return (net >> in_ifsni[j].i_bitshift);
	X		}
	X
	X	return (0);
	X}
	X
	X
	X/*
	X * Return the number of bits required to
	X * shift right a mask into a getnetent-able entity.
	X */
	X
	Xbsr(mask)
	X	register long	mask;
	X{
	X	register int	count = 0;
	X
	X	if (mask == 0)		/* "never happen", except with SunOS 3.4 */
	X		return (0);
	X
	X	while ((mask & 1) == 0) {
	X		++count;
	X		mask >>= 1;
	X	}
	X#ifdef DAMAGED_NETMASK
	X	count /= 8;			/* XXX gag retch puke barf */
	X	count *= 8;
	X#endif
	X	return (count);
	X}
	X
	X#endif
SHAR_EOF
if test 6645 -ne "`wc -c < 'subnet.c'`"
then
	echo shar: error transmitting "'subnet.c'" '(should have been 6645 characters)'
fi
echo "Done with directory 'server'"
cd ..
fi
if test -d 'xmit'
then
echo "Entering directory 'xmit'"
cd xmit
echo shar: extracting "'nntpxmit.c'" '(27564 characters)'
sed 's/^	X//' << \SHAR_EOF > 'nntpxmit.c'
	X#ifndef lint
	Xstatic char * rcsid = "@(#)$Header: nntpxmit.c,v 1.7 91/03/19 03:03:16 sob Exp $";
	X#endif
	X/* nntpxmit - transmit netnews articles across the internet with nntp
	X**
	X** This program is for transmitting netnews articles between sites
	X** that offer the NNTP service, internet style. There are two forms
	X** of article transmission that can be used in this environment, since
	X** the communication is interactive (and relatively more immediate,
	X** when compared to batched file transfer protocols, like UUCP). They
	X** are: active send (I have `x', do you want it?) and polling (what
	X** have you gotten lately?).
	X**
	X** 		A C T I V E   S E N D
	X**
	X** Sites on the UUCP network generally use active send, without asking
	X** in advance (that is, unless you got an article from your neighbor,
	X** or their site is listed in the Path: header already, you assume
	X** they don't have it and send it along). There is an ihave/sendme
	X** protocol for doing active send over batched links, but I claim that
	X** it won't work well because of the high latency between queueing
	X** and actual transfer that UUCP links typically have. That is, you'll
	X** still end up with a high rate of duplicate articles being sent over
	X** that type of link.
	X**
	X** With NNTP-based IHAVE, the update window in which another site can
	X** give the remote the article you just offered him is the time between
	X** the remote telling you it doesn't have the article, and your
	X** completed transfer of the article (pretty small). In practice, we
	X** still get duplicates, but generally from two problems: synchronized
	X** transmission of an article from two different neighbors (this can
	X** only be fixed by putting inews(1) into nntpd), and by articles
	X** being accepting during an expire(1) run (expire locks out inews
	X** processing while it is running, and articles collect until expire
	X** is done; since accepted article message-ids aren't added to
	X** the history file until expire is done, several clients can offer
	X** you the same article, and you'll accept all the copies offered you.
	X** When rnews gets run after expire, it will reject the duplicates).
	X**
	X** 		P O L L I N G
	X**
	X** Polling presents some article and distribution security problems,
	X** because the server has no contol over what a transmission client
	X** will ask for, and it must therefore control what it tells a client
	X** in response to a query.
	X**
	X** Articles that appear in local newsgroup hierarchies, or appear in
	X** the generally distributed USENET newsgroups with local distributions
	X** have to be filtered out from the list of message-IDs that the server
	X** gives to a client in response to a NEWNEWS query, or filtered when
	X** the server fetches the articles off the disk in response to an
	X** ARTICLE command (and therefore has complete access to the required
	X** information). Otherwise, distributions will leak.
	X**
	X** The other problem with polling is that a good client should keep track
	X** of when it last successfully polled a server, so that it doesn't force
	X** the server to dump its entire history file across the network, and this
	X** involves more file locking and manipulations routines.
	X**
	X** nntpxmit only implements active send, for now.
	X**
	X** Erik E. Fair <fair at ucbarpa.berkeley.edu>, Dec 4, 1987
	X** Stan Barber <sob at bcm.tmc.edu>, Jan 1, 1989
	X*/
	X
	X#include "../common/conf.h"
	X#include "nntpxmit.h"
	X#include <stdio.h>
	X#include <errno.h>
	X#include <ctype.h>
	X#include <sys/types.h>
	X#ifdef LAI_TCP
	X#include <sys/bsdtypes.h>
	X#endif
	X#include <sys/time.h>
	X#if defined(BSD_42) || defined(BSD_43)
	X#include <sys/resource.h>
	X#else
	X#include <sys/times.h>
	Xextern	time_t	time();
	X#endif
	X#include <sys/file.h>
	X#include <fcntl.h>
	X#include <signal.h>
	X#ifdef USG
	X#include "sysexits.h"
	X#else
	X#include <sysexits.h>
	X#endif
	X#ifdef	SYSLOG
	X#ifdef FAKESYSLOG
	X#include "../server/fakesyslog.h"
	X#else
	X#include <syslog.h>
	X#endif
	X#endif	/* SYSLOG */
	X#include "../common/nntp.h"
	X#include "llist.h"
	X
	X#define	MAXFNAME	BUFSIZ	/* maximum filename size - big enough? */
	X#define	FCLOSE(fp)	if (fp) (void) fclose(fp); (fp) = (FILE *)NULL
	X
	Xchar	*getline();
	Xchar	*getmsgid();
	Xchar	*errmsg();
	Xvoid	requeue();
	XSIGRET	catchsig();
	Xvoid	restsig();
	Xvoid	logstats();
	Xvoid	log();
	Xint	interrupted();
	X
	X/*
	X** Globals that certain things need.
	X**
	X** Various subroutines want the program name to report errors.
	X** The queue file, queue file pointer and current article name are
	X** there to write out the state of the queue file from a signal handler
	X** (that is, the list of unsent and (possibly) failed articles) so
	X** that when next we try sending to a given remote site, we don't send
	X** stuff we've already sent.
	X*/
	Xchar	*Pname;			/* this program's invocation name */
	Xchar	*Host;			/* current remote host */
	Xchar	*Qfile;			/* current queue file we're operating on */
	XFILE	*Qfp;			/* the (FILE *) for above */
	Xchar	Article[MAXFNAME];	/* current article filename */
	X
	X/*
	X** Some flags, toggled by arguments
	X*/
	X#define	TOGGLE(boolean)	(boolean) = !(boolean)
	Xchar	Debug = FALSE;
	Xchar	Report_Stats = TRUE;
	Xchar	ReQueue_Fails = TRUE;
	X
	Xchar	*USAGE = "USAGE: nntpxmit [-d][-s][-r][-T][-F][-D] hostname|hostname:file [...]";
	Xchar	*Fmt = "%s localhost %s[%d]: %s\n";
	Xchar	*E_fopen = "fopen(%s, \"%s\"): %s";
	Xchar	*E_unlk = "unlink(%s): %s";
	X
	Xll_t	FailedArticles;		/* list of failed articles */
	X
	Xstruct {
	X	u_long	offered;
	X	u_long	accepted;
	X	u_long	rejected;
	X	u_long	failed;
	X} Stats = {0L, 0L, 0L, 0L};
	X
	Xdouble Tbegin, Tend;		/* transfer timestamps */
	X
	Xextern	int	errno;
	Xextern 	int	strncmp();
	Xextern	char	*rindex();
	Xextern	char	*index();
	Xextern	char	*mktemp();
	Xextern	char	*strcpy();
	Xextern	char	*strcat();
	X
	X#ifdef	USG
	Xvoid
	Xbzero(s, l)
	Xregister caddr_t s;
	Xregister int	l;
	X{
	X	while(l-- > 0) *s++ = 0;
	X}
	X#endif	/* USG */
	X
	Xmain(ac, av)
	Xint	ac;
	Xchar	*av[];
	X{
	X	register int	i;
	X	int	transport = T_IP_TCP;	/* default is IP/TCP */
	X	int	isQfile = TRUE;		/* file arg is a Queue file */
	X#if	defined(BSD_42) || defined(BSD_43)
	X	struct timeval tod;
	X	struct timezone tz;
	X
	X	(void) gettimeofday(&tod, &tz);
	X	Tbegin = tod.tv_sec + (double)tod.tv_usec/1000000.;
	X#else
	X	Tbegin = (double) time((time_t *)NULL);
	X#endif
	X
	X	Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]);
	X	
	X	if (ac < 2) {
	X		fprintf(stderr, "%s: %s\n", Pname, USAGE);
	X		exit(EX_USAGE);
	X	}
	X
	X#ifdef	SYSLOG
	X	/* 4.2 BSD openlog has only two args */
	X#ifdef	BSD_42
	X	(void) openlog(Pname, LOG_PID);
	X#else
	X	(void) openlog(Pname, LOG_PID, SYSLOG);
	X#endif	/* BSD_42 */
	X#endif	/* SYSLOG */
	X
	X	for(i = 1; i < ac; i++) {
	X		if (av[i][0] == '-') {
	X			switch(av[i][1]) {
	X			case 'T':
	X				transport = T_IP_TCP;
	X				break;
	X			case 'D':
	X				transport = T_DECNET;
	X				break;
	X			case 'F':
	X				transport = T_FD;
	X				break;
	X			case 's':
	X				TOGGLE(Report_Stats);
	X				break;
	X			case 'd':
	X				TOGGLE(Debug);
	X				break;
	X			case 'r':
	X				TOGGLE(ReQueue_Fails);
	X				break;
	X			case 'a':
	X				isQfile = FALSE;
	X				break;
	X			default:
	X				fprintf(stderr, "%s: no such option: -%c\n",
	X					Pname, av[i][1]);
	X				fprintf(stderr, "%s: %s\n", Pname, USAGE);
	X				exit(EX_USAGE);
	X			}
	X			continue;
	X		}
	X
	X		/*
	X		** OK, it wasn't an option, therefore it must be a
	X		** hostname, filename pair.
	X		**
	X		** If the user typed host::file, then it's DECNET,
	X		** whether they remembered the "-D" option or not.
	X		*/
	X		Host = av[i];
	X		if ((Qfile = index(Host, ':')) != (char *)NULL) {
	X			if (Qfile[1] == ':') {
	X				transport = T_DECNET;
	X				*Qfile++ = '\0';
	X			} else if (transport != T_FD)
	X				transport = T_IP_TCP;
	X			*Qfile++ = '\0';
	X		} else
	X			Qfile = Host;
	X
	X		bzero((caddr_t)&Stats, sizeof(Stats));
	X		if (isQfile) {
	X			if (sendnews(Host, transport, Qfile, isQfile) && Report_Stats) {
	X				logstats();
	X			}
	X		} else {
	X			/* one-shot */
	X			(void) strcpy(Article, Qfile);
	X			exit(sendnews(Host, transport, Qfile, isQfile) ? EX_OK : EX_TEMPFAIL);
	X		}
	X	}
	X	exit(EX_OK);
	X}
	X
	X/*
	X** Calculate how much time we've used,
	X** and report that (and the transfer statistics).
	X**
	X*/
	Xvoid
	Xlogstats()
	X{
	X	static double ouser = 0.0, osys = 0.0;
	X	double user, sys;
	X	char buf[BUFSIZ];
	X#if	defined(BSD_42) || defined(BSD_43)
	X	struct rusage self, kids;
	X	struct timeval tod;
	X	struct timezone tzdummy;
	X
	X	(void) getrusage(RUSAGE_SELF, &self);
	X	(void) getrusage(RUSAGE_CHILDREN, &kids);
	X	(void) gettimeofday(&tod, &tzdummy);
	X
	X	Tend = tod.tv_sec + (double)tod.tv_usec/1000000.;
	X
	X	user = self.ru_utime.tv_sec + kids.ru_utime.tv_sec +
	X		(double) self.ru_utime.tv_usec/1000000. +
	X		(double) kids.ru_utime.tv_usec/1000000.;
	X	
	X	sys = self.ru_stime.tv_sec + kids.ru_stime.tv_sec +
	X		(double) self.ru_stime.tv_usec/1000000. +
	X		(double) kids.ru_stime.tv_usec/1000000.;
	X#else
	X#define	HZ	60.0	/* typical system clock ticks - param.h */
	X	struct tms	cpu;
	X
	X	(void) times(&cpu);
	X
	X	Tend = (double) time((time_t *)NULL);
	X	user = (double)(cpu.tms_utime + cpu.tms_cutime) / HZ;
	X	sys  = (double)(cpu.tms_stime + cpu.tms_cstime) / HZ;
	X#endif
	X	sprintf(buf,
	X		"%s stats %lu offered %lu accepted %lu rejected %lu failed",
	X		Host, Stats.offered, Stats.accepted, Stats.rejected,
	X		Stats.failed);
	X	log(L_INFO, buf);
	X	sprintf(buf, "%s xmit user %.3f system %.3f elapsed %.3f",
	X		Host, (user - ouser), (sys - osys), (Tend - Tbegin));
	X	log(L_INFO, buf);
	X	/* reset reference point */
	X	Tbegin = Tend;	
	X	ouser = user;
	X	osys = sys;
	X}
	X
	X/*
	X** Given a hostname to connect to, and a file of filenames (which contain
	X** netnews articles), send those articles to the named host using NNTP.
	X**
	X** Return code behavior is different depending upon isQfile.
	X**
	X**	TRUE	- return TRUE if we contacted the remote and started
	X**		  transferring news - this is to decide whether to
	X**		  record CPU and transfer statistics.
	X**
	X**	FALSE	- a one-shot file transfer - return TRUE or FALSE depending
	X**		  upon whether we successfully transferred the one article.
	X*/
	Xsendnews(host, transport, file, isQfile)
	Xchar	*host, *file;
	Xint	transport, isQfile;
	X{
	X#ifdef	FTRUNCATE
	X	char	*mode = "r+";		/* so we can use ftruncate() */
	X#else
	X	char	*mode = "r";
	X#endif	/* FTRUNCATE */
	X	char	*msgid;
	X
	X	if ((Qfp = fopen(file, mode)) == (FILE *)NULL) {
	X		char	buf[BUFSIZ];
	X
	X		sprintf(buf, E_fopen, file, mode, errmsg(errno));
	X		log(L_WARNING, buf);
	X		return(FALSE);
	X	}
	X
	X	/*
	X	** interlock with other copies of this process.
	X	** non-blocking.
	X	*/
	X	if (isQfile) {
	X		if (!lockfd(fileno(Qfp), file, DONT_BLOCK)) {
	X			FCLOSE(Qfp);
	X			return(FALSE);
	X		}
	X	}
	X
	X	/*
	X	** Open a connection to the remote server
	X	*/
	X	if (hello(host, transport) == FAIL) {
	X		FCLOSE(Qfp);
	X		return(FALSE);
	X	}
	X
	X	if (isQfile) {
	X		/*
	X		** We're sending a batch queue:
	X		**	open article
	X		**	get message-ID
	X		**	send "IHAVE <message-ID>" to remote
	X		**	read their reply
	X		**	send article if appropriate
	X		**	iterate to end of queue file
	X		*/
	X		catchsig(interrupted);
	X
	X		while ((msgid = getline(Qfp, Article, sizeof(Article))) != NULL) {
	X			if (!sendarticle(host, Article, msgid)) {
	X				requeue(Article, msgid);
	X				Article[0] = '\0';
	X				cleanup();
	X				goodbye(DONT_WAIT);
	X				restsig();
	X				return(TRUE);
	X			}
	X		}
	X
	X		cleanup();
	X		goodbye(WAIT);
	X		restsig();
	X		return(TRUE);
	X	} else {
	X		/*
	X		** Qfp is a netnews article - this is a one-shot
	X		** operation, exit code dependent upon remote's
	X		** acceptance of the article
	X		*/
	X		register int	retcode;
	X
	X		FCLOSE(Qfp);
	X		retcode = sendarticle(host, file, (char *) NULL);
	X		goodbye(retcode ? WAIT : DONT_WAIT);
	X		return(retcode && Stats.accepted == 1 && Stats.failed == 0);
	X	}
	X}
	X
	X/*
	X** Perform one transfer operation:
	X**	Give IHAVE command
	X**	Wait for reply, and send article if they ask for it
	X**	Wait for transfer confirmation, and requeue the article
	X**		if they drop it.
	X**	Watch all network I/O for errors, return FALSE if
	X**		the connection fails and we have to cleanup.
	X*/
	Xsendarticle(host, file, msgid)
	Xchar	*host;
	Xchar	*file;
	Xchar	*msgid;
	X{
	X	register int	code;
	X	FILE	*fp = NULL;
	X	int	error;
	X	char	buf[BUFSIZ];
	X	char	*e_xfer = "%s xfer: %s";
	X
	X	errno = 0;
	X	if (msgid == NULL || *msgid == '\0') {
	X		if ((msgid = getmsgid(file, &fp)) == NULL) {
	X			if (fp) { (void) fclose(fp); fp = NULL; }
	X			return TRUE;
	X		}
	X	}
	X	switch(code = ihave(msgid)) {
	X	case CONT_XFER:
	X		/*
	X		** They want it. Give it to 'em.
	X		*/
	X		if (!fp) { fp = fopen(file, "r"); }
	X		if (fp == NULL && errno != ENOENT) {
	X			/* Worse than "No such file or directory"? */
	X			sprintf(buf, E_fopen, file, "r", errmsg(errno));
	X			log(L_WARNING, buf);
	X			goodbye(DONT_WAIT);
	X			exit(EX_OSERR);
	X		}
	X		if (fp == NULL) {
	X			/* Hmph. The file didn't exist. */
	X			error = sendcmd(".");
	X		} else {
	X			error = !sendfile(fp);
	X			(void) fclose(fp);
	X			fp = NULL;
	X		}
	X		if (error) {
	X			sprintf(buf, "%s xfer: sendfile: %s",
	X				host, errmsg(errno));
	X			log(L_NOTICE, buf);
	X			Stats.failed++;
	X			if (fp) { (void) fclose(fp); fp = NULL; }
	X			return(FALSE);
	X		}
	X		/*
	X		** Did the article transfer OK?
	X		** Stay tuned to this same socket to find out!
	X		*/
	X		errno = 0;
	X		if ((code = readreply(buf, sizeof(buf))) != OK_XFERED) {
	X			Stats.failed++;
	X			if (code < 0) {
	X				if (errno > 0) {
	X					sprintf(buf, e_xfer, host, errmsg(errno));
	X					log(L_NOTICE, buf);
	X				} else {
	X					char errbuf[BUFSIZ];
	X
	X					sprintf(errbuf, e_xfer, host, buf);
	X					log(L_NOTICE, errbuf);
	X				if (fp) { (void) fclose(fp); fp = NULL; }
	X				}
	X				return(FALSE);
	X			}
	X			if (ReQueue_Fails && code != ERR_XFERRJCT && fp != NULL) {
	X				requeue(Article, msgid);
	X				Article[0] = '\0';
	X			}
	X		}
	X		break;
	X	case ERR_GOTIT:
	X		/* they don't want it */
	X		break;
	X	case ERR_XFERFAIL:
	X		if (fp) { (void) fclose(fp); fp = NULL; }
	X		/* they can't do it right now, but maybe later */
	X		return(FALSE);
	X		break;
	X	default:
	X		if (code < 0) {
	X			if (errno > 0) {
	X				sprintf(buf, e_xfer, host, errmsg(errno));
	X				log(L_NOTICE, buf);
	X			} else {
	X				sprintf(buf, e_xfer, host, "ihave");
	X				log(L_NOTICE, buf);
	X			}
	X		} else {
	X			sprintf(buf, "%s improper response to IHAVE: %d while offering %s", host, code, Article);
	X			log(L_WARNING, buf);
	X			if (fp) { (void) fclose(fp); fp = NULL; }
	X		}
	X		return(FALSE);
	X	}
	X	if (fp) { (void) fclose(fp); fp = NULL; }
	X	return(TRUE);
	X}
	X
	Xchar *
	Xerrmsg(code)
	Xint code;
	X{
	X	extern int sys_nerr;
	X	extern char *sys_errlist[];
	X	static char ebuf[6+5+1];
	X
	X	if (code > sys_nerr || code < 0) {
	X		(void) sprintf(ebuf, "Error %d", code);
	X		return ebuf;
	X	} else
	X		return sys_errlist[code];
	X}
	X
	X/*
	X** strip leading and trailing spaces
	X*/
	Xchar *
	Xsp_strip(s)
	Xregister char	*s;
	X{
	X	register char	*cp;
	X
	X	if (s == NULL)
	X		return(NULL);
	X
	X	if (*s == '\0')
	X		return(s);
	X	
	X	cp = &s[strlen(s) - 1];
	X	while(cp > s && isspace(*cp))
	X		cp--;
	X
	X	*++cp = '\0';	/* zap trailing spaces */
	X
	X	for(cp = s; *cp && isspace(*cp); cp++)
	X		continue;
	X
	X	return(cp);	/* return pointer to first non-space */
	X}
	X
	X/*
	X** convert `s' to lower case
	X*/
	Xchar *
	Xlcase(s)
	Xregister char	*s;
	X{
	X	register char	*cp;
	X
	X	if (s == (char *)NULL)
	X		return(s);
	X
	X	for(cp = s; *cp != '\0'; cp++)
	X		if (isupper(*cp))
	X			*cp = tolower(*cp);
	X	return(s);
	X}
	X
	X/*
	X** Get the message-id header field data with a minimum of fuss.
	X*/
	Xchar *
	Xgetmsgid(file, fpp)
	Xchar *file;
	XFILE **fpp;
	X{
	X	static	char	buf[BUFSIZ];
	X	static	char	msgid[] = "message-id";
	X	register char	*cp, *cp2;
	X
	X	*fpp = fopen(file, "r");
	X	if (*fpp == NULL) return NULL;
	X
	X	while(fgets(buf, sizeof(buf), *fpp) != NULL) {
	X		switch(buf[0]) {
	X		case '\n':
	X			(void) fclose(*fpp);
	X			*fpp = NULL;
	X			return NULL;	/* EOH, we failed */
	X		case 'M':
	X		case 'm':
	X			cp = index(buf, ':');
	X			if (cp == NULL) continue;
	X			*cp++ = '\0';
	X			if (strncmp(lcase(buf), msgid, strlen(msgid)) == 0) {
	X				/* dump extraneous trash - umass.bitnet */
	X				/* hope nobody quotes an '>' in a msgid */
	X				cp2 = index(cp, '>');
	X				if (cp2 != NULL) *++cp2 = '\0';
	X				(void) rewind(*fpp);
	X				return(sp_strip(cp));
	X			}
	X			break;
	X		}
	X	}
	X	(void) fclose(*fpp);
	X	*fpp = NULL;
	X	return NULL;	/* EOF, failed. */
	X}
	X
	X#ifdef	notdef	/* nobody obeys the triply damned protocol anyway! */
	X/*
	X** Special characters, see RFC822, appendix D.
	X*/
	Xisspecial(c)
	Xchar	c;
	X{
	X	char	*specials = "()<>@,;:\\\".[]";
	X
	X	return(index(specials, c) != (char *)NULL ? TRUE : FALSE);
	X}
	X
	X/*
	X** Check on the validity of an RFC822 message-id
	X**
	X** By The Book, RFC822 Appendix D.
	X**	msg-id		= "<" addr-spec ">"
	X**	addr-spec	= local-part "@" domain
	X**	local-part	= word *("." word)
	X**	word		= atom / quoted-string
	X**	domain 		= sub-domain *("." sub-domain)
	X**	sub-domain	= domain-ref / domain-literal
	X**	domain-ref	= atom
	X**	domain-literal	= "[" *(dtext / quoted-pair) "]"
	X**
	X** NOTE: close reading of the RFC822 spec indicates that a fully
	X**	qualified domain name (i.e. one with at least one dot) is
	X**	NOT required in the domain part of the addr-spec. However,
	X**	I've decided to be an asshole and require them, since we'll 
	X**	all die a slow death later on if I don't at this juncture.
	X**	To disable, if you disagree with me, see the last return
	X**	statement. - Erik E. Fair <fair at ucbarpa.berkeley.edu>
	X**	May 30, 1986
	X*/
	Xmsgid_ok(id)
	Xregister char	*id;
	X{
	X	register Langle = FALSE;
	X	register Rangle = FALSE;
	X	register local_part = FALSE;
	X	register at = FALSE;
	X	register dot = FALSE;
	X
	X	/* skip up to the opening angle bracket */
	X	if (id == (char *)NULL || (id = index(id, '<')) == (char *)NULL)
	X		return(FALSE);		/* don't waste my time! */
	X
	X	for(; *id != '\0'; id++) {
	X		switch(*id) {
	X		case '<':
	X			if (Langle) return(FALSE);
	X			Langle = local_part = TRUE;
	X			break;
	X		case '>':
	X			if (Rangle || !Langle || !at) return(FALSE);
	X			else Rangle = TRUE;
	X			break;
	X		case '@':		/* should be a domain spec */
	X			at = TRUE;
	X			local_part = FALSE;
	X			break;
	X		case '.':
	X			dot = at;
	X			break;
	X		case '\\':
	X			/*
	X			** quoted pair; this disallows NULs, but how
	X			** many mailers would die if someone used one?
	X			*/
	X			if (!local_part || (*++id) == '\0') return(FALSE);
	X			break;
	X		case '"':
	X			/*
	X			** quoted string
	X			*/
	X			if (!local_part) return(FALSE);
	X			do {
	X				switch(*++id) {
	X				case '\\':
	X					if ((*++id) == '\0') return(FALSE);
	X					break;
	X				case '\r':
	X					return(FALSE);
	X				}
	X			} while(*id != '\0' && *id != '"');
	X			break;
	X		case '[':
	X			/*
	X			** domain literal
	X			*/
	X			if (local_part) return(FALSE);
	X			do {
	X				switch(*++id) {
	X				case '\\':
	X					if ((*++id) == '\0') return(FALSE);
	X					break;
	X				case '\r':
	X					return(FALSE);
	X				}
	X			} while(*id != '\0' && *id != ']');
	X			break;
	X		default:
	X			if (!isascii(*id) || iscntrl(*id) || isspace(*id) || isspecial(*id))
	X				return(FALSE);	/* quit immediately */
	X			break;
	X		}
	X	}
	X	return(at && dot && Langle && Rangle);
	X}
	X#else notdef
	X
	X/*
	X** Simpleton's check for message ID syntax.
	X** A concession to the realities of the ARPA Internet.
	X*/
	Xmsgid_ok(s)
	Xregister char *s;
	X{
	X	register char	c;
	X	register in_msgid = FALSE;
	X
	X	if (s == (char *)NULL)
	X		return(FALSE);
	X
	X	while((c = *s++) != '\0') {
	X		if (!isascii(c) || iscntrl(c) || isspace(c))
	X			return(FALSE);
	X		switch(c) {
	X		case '<':
	X			in_msgid = TRUE;
	X			break;
	X		case '>':
	X			return(in_msgid);
	X		}
	X	}
	X	return(FALSE);
	X}
	X#endif	/* notdef */
	X
	X/*
	X** Read the header of a netnews article, snatch the message-id therefrom,
	X** and ask the remote if they have that one already.
	X*/
	Xihave(id)
	Xchar	*id;
	X{
	X	register int	code;
	X	char	buf[BUFSIZ];
	X
	X	if (id == NULL || *id == '\0') {
	X		/*
	X		** something botched locally with the article
	X		** so we don't send it, but we don't break off
	X		** communications with the remote either.
	X		*/
	X		sprintf(buf, "%s: message-id missing!", Article);
	X		log(L_DEBUG, buf);
	X		return(ERR_GOTIT);
	X	}
	X
	X	if (!msgid_ok(id)) {
	X		sprintf(buf, "%s: message-id syntax error: %s", Article, id);
	X		log(L_DEBUG, buf);
	X		return(ERR_GOTIT);
	X	}
	X
	Xagain:
	X	sprintf(buf, "IHAVE %s", id);
	X	Stats.offered++;
	X
	X	switch(code = converse(buf, sizeof(buf))) {
	X	case CONT_XFER:
	X		Stats.accepted++;
	X		return(code);
	X	case ERR_GOTIT:
	X		Stats.rejected++;
	X		return(code);
	X#ifdef AUTH
	X	case ERR_NOAUTH:
	X		xmitauth(Host);
	X		goto again;
	X#endif
	X	default:
	X		return(code);
	X	}
	X}
	X
	X/*
	X** Read the next line from fp into line,
	X** break it apart into filename and message-id,
	X** and return a pointer to the message-id.
	X** Returns "" if no message-id.
	X** Returns NULL at end of file.
	X*/
	Xchar *
	Xgetline(fp, line, len)
	XFILE	*fp;
	Xchar	*line;
	Xint	len;
	X{
	X	register char	*cp;
	X
	X	do {
	X		if (fgets(line, len, fp) == NULL) return NULL;
	X		line[len - 1] = '\0';
	X
	X		cp = index(line, '\n');
	X		if (cp != NULL) *cp = '\0';
	X	} while (line[0] == '\0');
	X
	X	cp = &line[0];
	X	while (*cp != '\0' && !isspace(*cp)) ++cp;
	X	if (*cp != '\0') {
	X		*cp++ = '\0';
	X		while (*cp != '\0' && isspace(*cp)) ++cp;
	X		/* cp now points to the message-id, if any. */
	X	}
	X	return cp;
	X}
	X
	X/*
	X** OK, clean up any mess and requeue failed articles
	X*/
	Xcleanup()
	X{
	X	dprintf(stderr, "%s: cleanup()\n", Pname);
	X	if (Qfp == (FILE *)NULL || Qfile == (char *)NULL)
	X		return;
	X
	X	if ((ReQueue_Fails && Stats.failed > 0) || !feof(Qfp)) {
	X		rewrite();
	X	} else {
	X		/*
	X		** Nothing to clean up after, reset stuff and
	X		** nuke the queue file.
	X		*/
	X		requeue((char *)NULL, (char *)NULL);
	X		if (feof(Qfp)) {
	X			dprintf(stderr, "%s: unlink(%s)\n", Pname, Qfile);
	X			if (unlink(Qfile) < 0) {
	X				char	buf[BUFSIZ];
	X
	X				sprintf(buf, E_unlk, Qfile, errmsg(errno));
	X				log(L_WARNING, buf);
	X			}
	X		}
	X		FCLOSE(Qfp);
	X	}
	X}
	X 
	X/*
	X** Add an article file name to an allocated linked list,
	X** so that we can rewrite it back to the queue file later.
	X** Calling this with a NULL pointer resets the internal pointer.
	X*/
	Xvoid
	Xrequeue(article, msgid)
	Xchar *msgid;
	Xchar *article;
	X{
	X	char buf[BUFSIZ];
	X	static ll_t *lp = &FailedArticles;
	X
	X	if (article == (char *)NULL) {
	X		dprintf(stderr, "%s: requeue(): reset\n", Pname);
	X		goto reset;		/* this is for our static pointer */
	X	}
	X
	X	if (*article == '\0')
	X		return;
	X
	X	(void) strcpy(buf, article);
	X	if (msgid != NULL && *msgid != '\0') {
	X		(void) strcat(strcat(buf, " "), msgid);
	X	}
	X
	X	dprintf(stderr, "%s: requeue(%s)\n", Pname, buf);
	X	if ((lp = l_alloc(lp, buf, strlen(buf) + 1)) == (ll_t *)NULL) {
	X		fprintf(stderr, "%s: requeue(%s) failed, dumping fail list\n",
	X			Pname, buf);
	X		/*
	X		** Wow! Did you know that this could blow the stack
	X		** if we recurse too deeply? I sure didn't!
	X		*/
	Xreset:
	X		l_free(&FailedArticles);
	X		lp = &FailedArticles;
	X	}
	X}
	X
	X/*
	X** Note that if I'm not running as "news" or "usenet" (or whatever
	X** account is supposed to own netnews), the resultant file will be the
	X** wrong ownership, permissions, etc.
	X*/
	Xrewrite()
	X{
	X	register ll_t	*lp;
	X	register FILE	*tmpfp;
	X	register int	nart = 0;
	X	char	*mode = "w+";
	X	static char template[] = "/tmp/nntpxmitXXXXXX";
	X	char	buf[BUFSIZ];
	X	static char	*tempfile = (char *)NULL;
	X
	X	dprintf(stderr, "%s: rewrite(%s)\n", Pname, Qfile);
	X
	X	if (tempfile == (char *)NULL)	/* should only need this once */
	X		tempfile = mktemp(template);
	X
	X	if ((tmpfp = fopen(tempfile, mode)) == (FILE *)NULL) {
	X		sprintf(buf, E_fopen, tempfile, mode, errmsg(errno));
	X		log(L_WARNING, buf);
	X		FCLOSE(Qfp);
	X		return;
	X	}
	X
	X	/*
	X	** Requeue the rest of the queue file first,
	X	** so that failed articles (if any) go to the end
	X	** of the new file.
	X	*/
	X	if (!feof(Qfp)) {
	X		dprintf(stderr, "%s: copying the unused portion of %s to %s\n",
	X			Pname, Qfile, tempfile);
	X		while(fgets(buf, sizeof(buf), Qfp) != (char *)NULL)
	X			(void) fputs(buf, tmpfp);
	X	}
	X
	X	/*
	X	** Here we write out the filenames of articles which
	X	** failed at the remote end.
	X	*/
	X	dprintf(stderr, "%s: writing failed article filenames to %s\n",
	X		Pname, tempfile);
	X	L_LOOP(lp, FailedArticles) {
	X		fprintf(tmpfp, "%s\n", lp->l_item);
	X		nart++;
	X	}
	X	dprintf(stderr, "%s: wrote %d article filenames to %s\n",
	X		Pname, nart, tempfile);
	X
	X	(void) fflush(tmpfp);
	X	/*
	X	** If writing the temp file failed (maybe /tmp is full?)
	X	** back out and leave the queue file exactly as it is.
	X	*/
	X	if (ferror(tmpfp)) {
	X		sprintf(buf, "rewrite(): copy to %s failed", tempfile);
	X		log(L_WARNING, buf);
	X		(void) fclose(tmpfp);
	X		FCLOSE(Qfp);
	X		if (unlink(tempfile) < 0) {
	X			sprintf(buf, E_unlk, tempfile, errmsg(errno));
	X			log(L_WARNING, buf);
	X		}
	X		requeue((char *)NULL,(char *)NULL);	/* reset */
	X		return;
	X	}
	X
	X	rewind(tmpfp);
	X#ifdef	FTRUNCATE
	X	rewind(Qfp);
	X	if (ftruncate(fileno(Qfp), (off_t)0) < 0) {
	X		sprintf(buf, "ftruncate(%s, 0): %s", Qfile, errmsg(errno));
	X		log(L_WARNING, buf);
	X		FCLOSE(Qfp);
	X		(void) fclose(tmpfp);
	X		if (unlink(tempfile) < 0) {
	X			sprintf(buf, E_unlk, tempfile, errmsg(errno));
	X			log(L_WARNING, buf);
	X		}
	X		requeue((char *)NULL,(char *)NULL);	/* reset */
	X		return;
	X	}
	X#else
	X	FCLOSE(Qfp);	/* we just nuked our lock here (lockfd) */
	X	if ((Qfp = fopen(Qfile, mode)) == (FILE *)NULL) {
	X		sprintf(buf, E_fopen, Qfile, mode, errmsg(errno));
	X		log(L_WARNING, buf);
	X		(void) fclose(tmpfp);
	X		if (unlink(tempfile) < 0) {
	X			sprintf(buf, E_unlk, tempfile, errmsg(errno));
	X			log(L_WARNING, buf);
	X		}
	X		requeue((char *)NULL,(char *)NULL);	/* reset */
	X		return;
	X	}
	X	/* Try to get our lock back (but continue whether we do or not) */
	X	(void) lockfd(fileno(Qfp), Qfile, DONT_BLOCK);
	X#endif	/* FTRUNCATE */
	X
	X	dprintf(stderr, "%s: copying %s back to %s\n", Pname, tempfile, Qfile);
	X	while(fgets(buf, sizeof(buf), tmpfp) != (char *)NULL)
	X		(void) fputs(buf, Qfp);
	X
	X	(void) fflush(Qfp);
	X	if (ferror(Qfp)) {
	X		sprintf(buf, "rewrite(): copy to %s failed", Qfile);
	X		log(L_WARNING, buf);
	X	}
	X	(void) fclose(tmpfp);
	X	FCLOSE(Qfp);
	X	if (unlink(tempfile) < 0) {
	X		sprintf(buf, E_unlk, tempfile, errmsg(errno));
	X		log(L_WARNING, buf);
	X	}
	X	requeue((char *)NULL,(char *)NULL);		/* reset */
	X	dprintf(stderr, "%s: rewrite(%s): done\n", Pname, Qfile);
	X	return;
	X}
	X
	X/*
	X** Signal stuff
	X**
	X** There's probably too much stuff to do in this signal
	X** handler, but we're going to exit anyway...
	X*/
	Xinterrupted(sig)
	Xint	sig;
	X{
	X	char buf[BUFSIZ];
	X
	X#ifndef RELSIG
	X	catchsig(SIG_IGN);	/* for System V - hope we're quick enough */
	X#endif	/* RELSIG */
	X	sprintf(buf, "%s signal %d", Host, sig);
	X	log(L_NOTICE, buf);
	X	requeue(Article,(char *)NULL);
	X	cleanup();
	X	if (Report_Stats)
	X		logstats();
	X	goodbye(DONT_WAIT);
	X	exit(EX_TEMPFAIL);
	X}
	X
	Xstruct {
	X	int	signo;
	X	ifunp	state;
	X} SigList[] = {
	X	{SIGHUP},
	X	{SIGINT},
	X	{SIGQUIT},
	X	{SIGTERM},
	X	{NULL}
	X};
	X
	XSIGRET
	Xcatchsig(handler)
	Xifunp	handler;
	X{
	X	register int	i;
	X
	X	if (handler != SIG_IGN) {
	X		for(i = 0; SigList[i].signo != NULL; i++) {
	X			SigList[i].state = signal(SigList[i].signo, handler);
	X		}
	X	} else {
	X		for(i = 0; SigList[i].signo != NULL; i++) {
	X			(void) signal(SigList[i].signo, handler);
	X		}
	X	}
	X}
	X
	Xvoid
	Xrestsig()
	X{
	X	register int	i;
	X
	X	for(i = 0; SigList[i].signo != NULL; i++) {
	X		if (SigList[i].state != (ifunp)(-1))
	X			(void) signal(SigList[i].signo, SigList[i].state);
	X	}
	X}
	X
	X/*
	X** log stuff
	X*/
	Xvoid
	Xlog(importance, error)
	Xint	importance;
	Xchar	*error;
	X{
	X	int skip = FALSE;
	X	FILE	*report = (importance == L_INFO ? stdout : stderr);
	X	fprintf(report, "%s: %s\n", Pname, error);
	X#ifdef	SYSLOG 
	X	switch(importance) {
	X#ifdef LOG
	X	case L_DEBUG:	importance = LOG_DEBUG;		break;
	X#else
	X	case L_DEBUG:	skip = TRUE;			break;
	X#endif
	X	case L_INFO:	importance = LOG_INFO;		break;
	X	case L_NOTICE:	importance = LOG_NOTICE;	break;
	X	case L_WARNING:	importance = LOG_WARNING;	break;
	X	default:	importance = LOG_DEBUG;		break;
	X	}
	X	if (skip == FALSE) syslog(importance, error);
	X#endif	/* SYSLOG */
	X}
	X
	X/*
	X** Lock a file descriptor
	X**
	X** NOTE: if the appropriate system calls are unavailable,
	X** this subroutine is a no-op.
	X*/
	Xlockfd(fd, file, non_blocking)
	Xint	fd, non_blocking;
	Xchar	*file;			/* just for error reporting */
	X{
	X	char	buf[BUFSIZ];
	X#ifdef	USG
	X#ifdef	F_TLOCK
	X	if (lockf(fd, (non_blocking ? F_TLOCK : F_LOCK), 0) < 0) {
	X		if (errno != EACCES) {
	X			sprintf(buf, "lockf(%s): %s\n", file, errmsg(errno));
	X			log(L_WARNING, buf);
	X		}
	X		return(FALSE);
	X	}
	X#endif	/* F_TLOCK */
	X#else
	X#ifdef	LOCK_EX
	X	if (flock(fd, LOCK_EX|(non_blocking ? LOCK_NB : 0)) < 0) {
	X		if (errno != EWOULDBLOCK) {
	X			sprintf(buf, "flock(%s): %s\n", file, errmsg(errno));
	X			log(L_WARNING, buf);
	X		}
	X		return(FALSE);
	X	}
	X#endif	/* LOCK_EX */
	X#endif	/* USG */
	X	return(TRUE);
	X}
SHAR_EOF
if test 27564 -ne "`wc -c < 'nntpxmit.c'`"
then
	echo shar: error transmitting "'nntpxmit.c'" '(should have been 27564 characters)'
fi
echo shar: extracting "'remote.c'" '(10680 characters)'
sed 's/^	X//' << \SHAR_EOF > 'remote.c'
	X#ifndef lint
	Xstatic char * rcsid = "@(#)$Header: remote.c,v 1.6 91/03/19 03:03:28 sob Exp $";
	X#endif
	X/*
	X** remote communication routines for NNTP/SMTP style communication.
	X**
	X************
	X** This version has been modified to support mmap()'ing of article files
	X** on systems that support it.
	X**
	X** David Robinson (david at elroy.jpl.nasa.gov) and
	X** Steve Groom (stevo at elroy.jpl.nasa.gov), June 30, 1989.
	X**
	X************
	X**
	X**	sendcmd		- return TRUE on error.
	X**
	X**	readreply	- return reply code or FAIL for error;
	X**				modifies buffer passed to it.
	X**
	X**	converse	- sendcmd() & readreply();
	X**				return reply code or FAIL for error;
	X**				modifies buffer passed to it.
	X**
	X**	hello		- establish connection with remote;
	X**				check greeting code.
	X**
	X**	goodbye		- give QUIT command, and shut down connection.
	X**
	X**	sfgets		- safe fgets(); does fgets with TIMEOUT.
	X**			  (N.B.: possibly unportable stdio macro ref in here)
	X**
	X**	rfgets		- remote fgets() (calls sfgets());
	X**				does SMTP dot escaping and
	X**				\r\n -> \n conversion.
	X**
	X**	sendfile	- send a file with SMTP dot escaping and
	X**				\n -> \r\n conversion.
	X**
	X** Erik E. Fair <fair at ucbarpa.berkeley.edu>
	X*/
	X#include "../common/conf.h"
	X#include "nntpxmit.h"
	X#include <sys/types.h>
	X#include <sys/socket.h>
	X#include <errno.h>
	X#ifdef LAI_TCP
	X#include <net/errno.h>
	X#endif
	X#include <stdio.h>
	X#include <ctype.h>
	X#include <setjmp.h>
	X#include <signal.h>
	X#ifdef dgux
	X#define _IOERR  _IO_ERR
	X#endif
	X#ifdef SYSLOG
	X#ifdef FAKESYSLOG
	X#include "../server/fakesyslog.h"
	X#else
	X#include <syslog.h>
	X#endif
	X#endif
	X#include "get_tcp_conn.h"
	X#include "../common/nntp.h"
	X
	Xstatic	jmp_buf	SFGstack;
	XFILE	*rmt_rd;
	XFILE	*rmt_wr;
	Xchar	*sfgets();
	Xchar	*rfgets();
	X
	Xextern	int	errno;
	Xextern	char	Debug;
	Xextern	char	*errmsg();
	Xextern	char	*strcpy();
	Xextern	void	log();
	X
	X/*
	X** send cmd to remote, terminated with a CRLF.
	X*/
	Xsendcmd(cmd)
	Xchar	*cmd;
	X{
	X	if (cmd == (char *)NULL)
	X		return(TRUE);	/* error */
	X	dprintf(stderr, ">>> %s\n", cmd);	/* DEBUG */
	X	(void) fprintf(rmt_wr, "%s\r\n", cmd);
	X	(void) fflush(rmt_wr);
	X	return(ferror(rmt_wr));
	X}
	X
	X/*
	X** read a reply line from the remote server and return the code number
	X** as an integer, and the message in a buffer supplied by the caller.
	X** Returns FAIL if something went wrong.
	X*/
	Xreadreply(buf, size)
	Xregister char	*buf;
	Xint	size;
	X{
	X	register char	*cp;
	X	register int	len;
	X
	X	if (buf == (char *)NULL || size <= 0)
	X		return(FAIL);
	X
	X	/*
	X	** make sure it's invalid, unless we say otherwise
	X	*/
	X	buf[0] = '\0';
	X
	X	/*
	X	** read one line from the remote
	X	*/
	X	if (sfgets(buf, size, rmt_rd) == NULL)
	X		return(FAIL);	/* error reading from remote */
	X
	X	/*
	X	** Make sure that what the remote sent us had a CRLF at the end
	X	** of the line, and then null it out.
	X	*/
	X	if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r' &&
	X		*(cp + 1) == '\n')
	X	{
	X		*cp = '\0';
	X	} else
	X		return(FAIL);	/* error reading from remote */
	X
	X	dprintf(stderr, "%s\n", buf);	/* DEBUG */
	X	/*
	X	** Skip any non-digits leading the response code 
	X	** and then convert the code from ascii to integer for
	X	** return from this routine.
	X	*/
	X	cp = buf;
	X	while(*cp != '\0' && isascii(*cp) && !isdigit(*cp))
	X		cp++;	/* skip anything leading */
	X
	X	if (*cp == '\0' || !isascii(*cp))
	X		return(FAIL);	/* error reading from remote */
	X
	X	return(atoi(cp));
	X}
	X
	X/*
	X** send a command to the remote, and wait for a response
	X** returns the response code, and the message in the buffer
	X*/
	Xconverse(buf, size)
	Xchar	*buf;
	Xint	size;
	X{
	X	register int	resp;
	X
	X	if (sendcmd(buf))
	X		return(FAIL);	/* Ooops! Something went wrong in xmit */
	X	/*
	X	** Skip the silly 100 series messages, since they're not the
	X	** final response we can expect
	X	*/
	X	while((resp = readreply(buf, size)) >= 100 && resp < 200)
	X		continue;
	X	return(resp);
	X}
	X
	X/*
	X** Contact the remote server and set up the two global FILE pointers
	X** to that descriptor.
	X**
	X** I can see the day when this routine will have 8 args:  one for
	X** hostname, and one for each of the seven ISO Reference Model layers
	X** for networking. A curse upon those involved with the ISO protocol
	X** effort: may they be forced to use the network that they will create,
	X** as opposed to something that works (like the Internet).
	X*/
	Xhello(host, transport)
	Xchar	*host;
	Xint	transport;
	X{ char	*service;
	X	char	*rmode = "r";
	X	char	*wmode = "w";
	X	char	*e_fdopen = "fdopen(%d, \"%s\"): %s";
	X	int	socket0, socket1;	/* to me (bad pun) */
	X	char	buf[BUFSIZ];
	X
	X	switch(transport) {
	X	case T_IP_TCP:
	X		service = "nntp";
	X		socket0 = get_tcp_conn(host, service);
	X		break;
	X	case T_DECNET:
	X#ifdef DECNET
	X		(void) signal(SIGPIPE, SIG_IGN);
	X		service = "NNTP";
	X		socket0 = dnet_conn(host, service, 0, 0, 0, 0, 0);
	X		if (socket0 < 0) {
	X			switch(errno) {
	X			case EADDRNOTAVAIL:
	X				socket0 = NOHOST;
	X				break;
	X			case ESRCH:
	X				socket0 = NOSERVICE;
	X				break;
	X			}
	X		}
	X		break;
	X#else
	X		log(L_WARNING, "no DECNET support compiled in");
	X		return(FAIL);
	X#endif
	X	case T_FD:
	X		service = "with a smile";
	X		socket0 = atoi(host);
	X		break;
	X	}
	X
	X	if (socket0 < 0) {
	X		switch(socket0) {
	X		case NOHOST:
	X			sprintf(buf, "%s host unknown", host);
	X			log(L_WARNING, buf);
	X			return(FAIL);
	X		case NOSERVICE:
	X			sprintf(buf, "%s service unknown: %s", host, service);
	X			log(L_WARNING, buf);
	X			return(FAIL);
	X		case FAIL:
	X			sprintf(buf, "%s hello: %s", host, errmsg(errno));
	X			log(L_NOTICE, buf);
	X			return(FAIL);
	X		}
	X	}
	X
	X	if ((socket1 = dup(socket0)) < 0) {
	X		sprintf(buf, "dup(%d): %s", socket0, errmsg(errno));
	X		log(L_WARNING, buf);
	X		(void) close(socket0);
	X		return(FAIL);
	X	}
	X
	X	if ((rmt_rd = fdopen(socket0, rmode)) == (FILE *)NULL) {
	X		sprintf(buf, e_fdopen, socket0, rmode);
	X		log(L_WARNING, buf);
	X		(void) close(socket0);
	X		(void) close(socket1);
	X		return(FAIL);
	X	}
	X
	X	if ((rmt_wr = fdopen(socket1, wmode)) == (FILE *)NULL) {
	X		sprintf(buf, e_fdopen, socket1, wmode);
	X		log(L_WARNING, buf);
	X		(void) fclose(rmt_rd);
	X		rmt_rd = (FILE *)NULL;
	X		(void) close(socket1);
	X		return(FAIL);
	X	}
	X
	X	switch(readreply(buf, sizeof(buf))) {
	X	case OK_CANPOST:
	X	case OK_NOPOST:
	X		if (ferror(rmt_rd)) {
	X			goodbye(DONT_WAIT);
	X			return(FAIL);
	X		}
	X		break;
	X	default:
	X		if (buf[0] != '\0') {
	X			char	err[BUFSIZ];
	X
	X			sprintf(err, "%s greeted us with %s", host, buf);
	X			log(L_NOTICE, err);
	X		}
	X		goodbye(DONT_WAIT);
	X		return(FAIL);
	X	}
	X	return(NULL);
	X}
	X
	X/*
	X** Say goodbye to the nice remote server.
	X**
	X** We trap SIGPIPE because the socket might already be gone.
	X*/
	Xgoodbye(wait_for_reply)
	Xint	wait_for_reply;
	X{
	X	register ifunp	pstate = signal(SIGPIPE, SIG_IGN);
	X
	X	if (sendcmd("QUIT"))
	X		wait_for_reply = FALSE;	/* override, something's wrong. */
	X	/*
	X	** I don't care what they say to me; this is just being polite.
	X	*/
	X	if (wait_for_reply) {
	X		char	buf[BUFSIZ];
	X
	X		(void) readreply(buf, sizeof(buf));
	X	}
	X	(void) fclose(rmt_rd);
	X	rmt_rd = (FILE *)NULL;
	X	(void) fclose(rmt_wr);
	X	rmt_wr = (FILE *)NULL;
	X	if (pstate != (ifunp)(-1))
	X		(void) signal(SIGPIPE, pstate);
	X}
	X
	Xstatic SIGRET
	Xto_sfgets()
	X{
	X	longjmp(SFGstack, 1);
	X}
	X
	X/*
	X** `Safe' fgets, ala sendmail. This fgets will timeout after some
	X** period of time, on the assumption that if the remote did not
	X** return, they're gone.
	X** WARNING: contains a possibly unportable reference to stdio
	X** error macros.
	X*/
	Xchar *
	Xsfgets(buf, size, fp)
	Xchar	*buf;
	Xint	size;
	XFILE	*fp;
	X{
	X	register char	*ret;
	X	int	esave;
	X
	X	if (buf == (char *)NULL || size <= 0 || fp == (FILE *)NULL)
	X		return((char *)NULL);
	X	if (setjmp(SFGstack)) {
	X		(void) alarm(0);		/* reset alarm clock */
	X		(void) signal(SIGALRM, SIG_DFL);
	X#ifdef apollo
	X		fp->_flag |= _SIERR;
	X#else
	X		fp->_flag |= _IOERR;		/* set stdio error */
	X#endif
	X#ifndef ETIMEDOUT
	X		errno = EPIPE;			/* USG doesn't have ETIMEDOUT*/
	X#else
	X		errno = ETIMEDOUT;		/* connection timed out */
	X#endif
	X		return((char *)NULL);		/* bad read, remote time out */
	X	}
	X	(void) signal(SIGALRM, to_sfgets);
	X	(void) alarm(TIMEOUT);
	X	ret = fgets(buf, size, fp);
	X	esave = errno;
	X	(void) alarm(0);			/* reset alarm clock */
	X	(void) signal(SIGALRM, SIG_DFL);	/* reset SIGALRM */
	X	errno = esave;
	X	return(ret);
	X}
	X
	X/*
	X** Remote fgets - converts CRLF to \n, and returns NULL on `.' EOF from
	X** the remote. Otherwise it returns its first argument, like fgets(3).
	X*/
	Xchar *
	Xrfgets(buf, size, fp)
	Xchar	*buf;
	Xint	size;
	XFILE	*fp;
	X{
	X	register char	*cp = buf;
	X	register int	len;
	X
	X	if (buf == (char *)NULL || size <= 0 || fp == (FILE *)NULL)
	X		return((char *)NULL);
	X	*cp = '\0';
	X	if (sfgets(buf, size, fp) == (char *)NULL)
	X		return((char *)NULL);
	X
	X	/* <CRLF> => '\n' */
	X	if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r') {
	X		*cp++ = '\n';
	X		*cp = '\0';
	X	}
	X
	X	/* ".\n" => EOF */
	X	cp = buf;
	X	if (*cp++ == '.' && *cp == '\n') {
	X		return((char *)NULL);	/* EOF */
	X	}
	X
	X	/* Dot escaping */
	X	if (buf[0] == '.')
	X		(void) strcpy(&buf[0], &buf[1]);
	X	return(buf);
	X}
	X
	X/*
	X** send the contents of an open file descriptor to the remote,
	X** with appropriate RFC822 filtering (e.g. CRLF line termination,
	X** and dot escaping). Return FALSE if something went wrong.
	X*/
	Xsendfile(fp)
	XFILE	*fp;
	X{
	X	register int	c;
	X	register FILE	*remote = rmt_wr;
	X	register int	nl = TRUE;	/* assume we start on a new line */
	X
	X#ifdef MMAP
	X	register char *mbufr,*mptr;
	X        long msize;
	X        long offset;
	X        struct stat sbuf;
	X	char buf[BUFSIZ];
	X#endif MMAP
	X
	X/*
	X** I'm using putc() instead of fputc();
	X** why do a subroutine call when you don't have to?
	X** Besides, this ought to give the C preprocessor a work-out.
	X*/
	X#ifdef MMAP
	X#define	PUTC(c)	if (putc(c, remote) == EOF) {\
	X	(void) munmap (mbufr, sbuf.st_size);\
	X	return(FALSE); }
	X#else !MMAP
	X#define	PUTC(c)	if (putc(c, remote) == EOF) return(FALSE)
	X#endif !MMAP
	X
	X	if (fp == (FILE *)NULL)
	X		return(FALSE);
	X
	X#ifdef MMAP
	X	/* map the article into memory */
	X	(void) fstat(fileno(fp), &sbuf);
	X        mbufr = mmap (0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
	X        if(mbufr == (char *) -1){
	X		sprintf(buf, "sendfile: mmap failed: %s", errmsg(errno));
	X		log(L_NOTICE, buf);
	X                return(FALSE);
	X        }
	X
	X        mptr = mbufr;		/* start of article in memory */
	X        msize = sbuf.st_size;	/* size of article (bytes) */
	X	while(msize-- > 0) {
	X		c = *mptr++;
	X#else !MMAP
	X	/*
	X	** the second test makes no sense to me,
	X	** but System V apparently needed it...
	X	*/
	X	while((c = fgetc(fp)) != EOF && !feof(fp)) {
	X#endif !MMAP
	X		switch(c) {
	X		case '\177':			/* skip deletes... */
	X			break;
	X		case '\n':
	X			PUTC('\r');		/* \n -> \r\n */
	X			PUTC(c);
	X			nl = TRUE;		/* for dot escaping */
	X			break;
	X		case '.':
	X			if (nl) {
	X				PUTC(c);	/* add a dot */
	X				nl = FALSE;
	X			}
	X			PUTC(c);
	X			break;
	X		default:
	X			PUTC(c);
	X			nl = FALSE;
	X			break;
	X		}
	X	}
	X	if (!nl) {
	X		PUTC('\r');
	X		PUTC('\n');
	X	}
	X#ifdef MMAP
	X	(void) munmap (mbufr, sbuf.st_size);
	X#endif MMAP
	X	return( !(sendcmd(".") || ferror(fp)) );
	X}
SHAR_EOF
if test 10680 -ne "`wc -c < 'remote.c'`"
then
	echo shar: error transmitting "'remote.c'" '(should have been 10680 characters)'
fi
echo "Done with directory 'xmit'"
cd ..
fi
if test -d 'xfer'
then
echo "Entering directory 'xfer'"
cd xfer
echo shar: extracting "'nntpxfer.c'" '(12837 characters)'
sed 's/^	X//' << \SHAR_EOF > 'nntpxfer.c'
	X#ifndef lint
	Xstatic char * scsid = "@(#)$Header: nntpxfer.c,v 1.10 91/03/19 03:03:45 sob Exp $";
	X#endif
	X/*
	X * nntpxfer
	X *
	X * Connects to the specified nntp server, and transfers all new news
	X * since the last successful invocation.
	X *
	X * last successful invocation date and time are stored in a file at
	X * /usr/spool/news/nntp.<hostname> as 
	X *	groups YYMMDD HHMMSS distributions\n
	X * in case you need to edit it.  You can also override this on 
	X * the command line in the same format, in which case the file won't
	X * be updated.
	X *
	X *	Brian Kantor, UCSD 1986
	X * (some bug fixes by ambar at athena.mit.edu)
	X * Modified to use NNTP distribution conf.h file and nntpxmit's get_tcp_conn.c
	X * subroutines so that nntpxfer could be used on more systems.
	X * Stan Barber, November 7, 1989 <sob at bcm.tmc.edu>
	X *
	X */
	X
	X#include "../common/conf.h"
	X#ifdef DEBUG
	X#undef SYSLOG
	X#endif
	X
	X#include <sys/types.h>
	X#ifdef LAI_TCP
	X#include <sys/bsdtypes.h>
	X#endif
	X#ifdef NDIR
	X#ifdef M_XENIX
	X#include <sys/ndir.h>
	X#else
	X#include <ndir.h>
	X#endif
	X#else
	X#include <sys/dir.h>
	X#endif
	X#ifdef USG
	X#include <time.h>
	X#else
	X#include <sys/time.h>
	X#endif
	X
	X#include <stdio.h>
	X#include <errno.h>
	X#include <ctype.h>
	X#include <setjmp.h>
	X#ifndef NONETDB
	X#include <netdb.h>
	X#endif
	X#include <signal.h>
	X#ifdef SYSLOG
	X#ifdef FAKESYSLOG
	X#include "../server/fakesyslog.h"
	X#else
	X#include <syslog.h>
	X#endif
	X#endif
	X
	X#ifdef DBM
	X# ifdef DBZ
	X#   include <dbz.h>
	X#else /* DBZ */
	X# undef NULL
	X# include <dbm.h>
	X# undef NULL
	X# define NULL	0
	X#endif /* DBZ */
	X#endif  /* DBM */
	X
	X#ifdef NDBM
	X#include <ndbm.h>
	X#include <fcntl.h>
	Xstatic DBM *db = NULL;
	X#endif
	X#ifndef TIMEOUT
	X#define TIMEOUT (30*60)
	X#endif
	X#ifndef MAX_ARTICLES
	X#define MAX_ARTICLES 4096
	X#endif
	X
	Xchar	*malloc();
	Xchar	*strcpy();
	Xchar	*strcat();
	Xchar	*rindex();
	Xlong	time();
	Xu_long	inet_addr();
	X
	Xextern int errno;
	Xchar *artlist[MAX_ARTICLES];
	Xint server;			/* stream socket to the nntp server */
	XFILE * rd_fp, * wr_fp;
	Xint newart, dupart, misart;
	Xchar * Pname;
	X
	Xmain(argc, argv)
	Xint argc;
	Xchar *argv[];
	X{
	X	FILE *dtfile;		/* where last xfer date/time stored */
	X	char buf[BUFSIZ];
	X	char lastdate[16];
	X	char distributions[BUFSIZ];
	X	char dtname[128];
	X	char newsgroups[BUFSIZ];
	X	char lasttime[16];
	X	int i;
	X	int omitupdate = 0;		/* 1 = don't update datetime */
	X	long clock;
	X	long newdate, newtime;
	X	struct tm *now;
	X	Pname = ((Pname = rindex(argv[0], '/')) ? Pname + 1 : argv[0]);
	X	/* OPTIONS
	X		argv[1] MUST be the host name
	X		argv[2-4] MAY be "newsgroups YYMMDD HHMMSS"
	X			argv[5] MAY be distributions
	X		(otherwise use 2-4/5 from the file
	X		"/usr/spool/news/nntp.hostname")
	X	*/
	X
	X	if (argc != 2 && argc != 5 && argc != 6)
	X		{
	X		(void) printf("Usage: %s host [groups YYMMDD HHMMSS [<dist>]]\n",
	X			argv[0]);
	X		exit(1);
	X		}
	X	
	X	if (argc > 2)
	X		{
	X		omitupdate=1;
	X		(void) strcpy(newsgroups, argv[2]);
	X		(void) strcpy(lastdate, argv[3]);
	X		(void) strcpy(lasttime, argv[4]);
	X		(void) strcpy(distributions, "");
	X		if (argc > 5)
	X			(void) strcpy(distributions, argv[5]);
	X		}
	X	else
	X		{
	X		(void) sprintf(dtname, "%s/nntp.%s",SPOOLDIR,argv[1]);
	X		dtfile = fopen(dtname, "r");
	X		if (dtfile == (FILE *) 0)
	X			{
	X			(void) printf("%s not found; using * 860101 000000 \n", 
	X				dtname);
	X			(void) strcpy(newsgroups, "*");
	X			(void) strcpy(lastdate, "860101");
	X			(void) strcpy(lasttime, "000000");
	X			(void) strcpy(distributions, "");
	X			}
	X		else
	X			{
	X			if (fscanf(dtfile, "%s %s %s %s",
	X				newsgroups, lastdate, lasttime, distributions) < 3)
	X				{
	X				(void) printf("%s invalid; using * 860101 000000\n",
	X					dtname);
	X				(void) strcpy(newsgroups, "*");
	X				(void) strcpy(lastdate, "860101");
	X				(void) strcpy(lasttime, "000000");
	X				(void) strcpy(distributions, "");
	X				}
	X			(void) fclose(dtfile);
	X			}
	X		clock = time((long *)0);
	X		now = gmtime(&clock);
	X		newdate = (now->tm_year * 10000) +
	X			((now->tm_mon + 1) * 100) + now->tm_mday;
	X		newtime = (now->tm_hour * 10000) +
	X			(now->tm_min * 100) + now->tm_sec;
	X#ifdef DEBUG
	X	printf("server is %s\n",argv[1]);
	X	printf("lastdate is %s\n",lastdate);
	X	printf("lasttime is %s\n",lasttime);
	X	printf("newsgroups is '%s'\n",newsgroups);
	X	printf("distributions is '%s'\n",distributions);
	X#endif
	X		}
	X#ifdef SYSLOG
	X#ifdef BSD_42
	X	openlog("nntpxfer", LOG_PID);
	X#else
	X	openlog("nntpxfer", LOG_PID, SYSLOG);
	X#endif
	X#endif
	X
	X#ifdef DBM
	X	if (dbminit(HISTORY_FILE) < 0)
	X		{
	X#ifdef SYSLOG
	X		syslog(LOG_ERR,"couldn't open history file: %m");
	X#else
	X		perror("nntpxfer: couldn't open history file");
	X#endif
	X		exit(1);
	X		}
	X#endif
	X#ifdef NDBM
	X 	if ((db = dbm_open(HISTORY_FILE, O_RDONLY, 0)) == NULL) 
	X 		{
	X#ifdef SYSLOG
	X 		syslog(LOG_ERR,"couldn't open history file: %m");
	X#else
	X 		perror("nntpxfer: couldn't open history file");
	X#endif
	X 		exit(1);
	X 		}
	X#endif
	X	if ((server = get_tcp_conn(argv[1],"nntp")) < 0) 
	X		{
	X#ifdef SYSLOG
	X		syslog(LOG_ERR,"could not open socket: %m");
	X#else
	X		perror("nntpxfer: could not open socket");
	X#endif
	X		exit(1);
	X		}
	X	if ((rd_fp = fdopen(server,"r")) == (FILE *) 0){
	X#ifdef SYSLOG
	X		syslog(LOG_ERR,"could not fdopen socket: %m");
	X#else
	X		perror("nntpxfer: could not fdopen socket");
	X#endif
	X		exit(1);
	X		}
	X
	X#ifdef SYSLOG
	X	syslog(LOG_DEBUG,"connected to nntp server at %s", argv[1]);
	X#endif
	X#ifdef DEBUG
	X	printf("connected to nntp server at %s\n", argv[1]);
	X#endif
	X	/*
	X	* ok, at this point we're connected to the nntp daemon 
	X	* at the distant host.
	X	*/
	X	/* get the greeting herald */
	X	(void) sockread(buf);
	X#ifdef DEBUG
	X	(void) printf("%s\n", buf);
	X#endif
	X	if (buf[0] != '2')	/* uh-oh, something's wrong! */
	X		{
	X#ifdef SYSLOG
	X		syslog(LOG_NOTICE,"protocol error: got '%s'\n", buf);
	X#else
	X		(void) printf("%s: protocol error: got '%s'\n", Pname,buf);
	X#endif
	X		(void) close(server);
	X		exit(1);
	X		}
	X
	X
	X	/* first, tell them we're a slave process to get priority */
	X	sockwrite("SLAVE");
	X	(void) sockread(buf);
	X#ifdef DEBUG
	X	(void) printf("%s\n", buf);
	X#endif
	X	if (buf[0] != '2')	/* uh-oh, something's wrong! */
	X		{
	X#ifdef SYSLOG
	X		syslog(LOG_NOTICE,"protocol error: got '%s'", buf);
	X#else
	X		(void) printf("%s: protocol error: got '%s'\n", Pname,buf);
	X#endif
	X		(void) close(server);
	X		exit(1);
	X		}
	X	
	X	/* now, ask for a list of new articles */
	X	if (strlen(distributions))
	X		(void) sprintf(buf,"NEWNEWS %s %s %s GMT <%s>", 
	X			newsgroups, lastdate, lasttime, distributions);
	X	else
	X		(void) sprintf(buf,"NEWNEWS %s %s %s GMT", 
	X			newsgroups, lastdate, lasttime);
	X	sockwrite(buf);
	X	(void) sockread(buf);
	X#ifdef DEBUG
	X	(void) printf("%s\n", buf);
	X#endif
	X	if (buf[0] != '2')	/* uh-oh, something's wrong! */
	X		{
	X#ifdef SYSLOG
	X		syslog(LOG_NOTICE,"protocol error: got '%s'", buf);
	X#else
	X		(void) printf("%s: protocol error: got '%s'\n", Pname,buf);
	X#endif
	X		(void) close(server);
	X		exit(1);
	X		}
	X	/* and here comes the list, terminated with a "." */
	X#ifdef DEBUG
	X	(void) printf("data\n");
	X#endif
	X	dupart = newart = 0;
	X	while (1)
	X		{
	X		(void) sockread(buf);
	X		if (!strcmp(buf,"."))
	X			break;
	X		if (wewant(buf))
	X			{
	X			if (newart >= MAX_ARTICLES)
	X				{
	X				omitupdate=1;
	X				continue;
	X				}
	X			artlist[newart] = malloc((unsigned)(strlen(buf)+1));
	X			(void) strcpy(artlist[newart], buf);
	X			newart++;
	X			}
	X		else
	X			dupart++;
	X		}
	X#ifdef DEBUG
	X	(void) printf(".\n%d new, %d dup articles\n", newart, dupart);
	X#endif
	X
	X	/* now that we know which articles we want, retrieve them */
	X	for (i=0; i < newart; i++)
	X		(void) artfetch(artlist[i]);
	X
	X#ifdef DEBUG
	X	(void) printf("%d missing articles\n", misart);
	X#endif
	X	/* we're all done, so tell them goodbye */
	X	sockwrite("QUIT");
	X	(void) sockread(buf);
	X#ifdef DEBUG
	X	(void) printf("%s\n", buf);
	X#endif
	X	if (buf[0] != '2')	/* uh-oh, something's wrong! */
	X		{
	X#ifdef SYSLOG
	X		syslog(LOG_NOTICE,"error: got '%s'", buf);
	X#else
	X		(void) printf("%s: error: got '%s'\n", Pname,buf);
	X#endif
	X		(void) close(server);
	X		exit(1);
	X		}
	X	(void) close(server);
	X
	X	/* do we want to update the timestamp file? */
	X	if (!omitupdate)
	X		{
	X		(void) sprintf(buf, "%s %06d %06d %s\n",
	X			newsgroups, newdate, newtime, distributions);
	X#ifdef DEBUG
	X		(void) printf("updating %s:\n\t%s\n", dtname, buf);
	X#endif
	X		dtfile = fopen(dtname, "w");
	X		if (dtfile == (FILE *) 0)
	X			{
	X			perror(dtname);
	X			exit(1);
	X			}
	X		(void) fputs(buf,dtfile);
	X		(void) fclose(dtfile);
	X		}
	X	exit(0);
	X}
	X
	Xartfetch(articleid)
	Xchar *articleid;
	X	{
	X#ifdef DEBUG
	X	int lines = 0;
	X#endif
	X	char buf[BUFSIZ];
	X	FILE *inews;
	X
	X	/* now, ask for the article */
	X	(void) sprintf(buf,"ARTICLE %s", articleid);
	X	sockwrite(buf);
	X	(void) sockread(buf);
	X#ifdef DEBUG
	X	(void) printf("%s\n", buf);
	X#endif
	X	if (buf[0] == '4')	/* missing article, just skipit */
	X		{
	X		misart++;
	X		return(0);
	X		}
	X
	X	if (buf[0] != '2')	/* uh-oh, something's wrong! */
	X		{
	X#ifdef SYSLOG
	X		syslog(LOG_NOTICE,"protocol error: got '%s'", buf);
	X#else
	X		(void) printf("%s: protocol error: got '%s'\n", Pname, buf);
	X#endif
	X		(void) close(server);
	X		exit(1);
	X		}
	X#ifdef DEBUG
	X	(void) printf("command: %s\n", RNEWS);
	X#endif
	X	if ( (inews = popen(RNEWS, "w")) == (FILE *) 0)
	X		{
	X		perror(RNEWS);
	X		exit(1);
	X		}
	X
	X	/* and here comes the article, terminated with a "." */
	X#ifdef DEBUG
	X	(void) printf("data\n");
	X#endif
	X	while (1)
	X		{
	X		(void) sockread(buf);
	X		if (buf[0] == '.' && buf[1] == '\0')
	X			break;
	X#ifdef DEBUG
	X		lines++;
	X#endif
	X		(void) strcat(buf,"\n");
	X		(void) fputs(((buf[0] == '.') ? buf + 1 : buf),
	X			   inews);
	X		}
	X#ifdef DEBUG
	X	(void) printf(".\n%d lines\n", lines);
	X#endif
	X	(void) fflush(inews);
	X	(void) pclose(inews);
	X	return(0);
	X        }
	X
	Xstatic	jmp_buf	SFGstack;
	X
	Xstatic SIGRET
	Xto_sfgets()
	X{
	X	longjmp(SFGstack, 1);
	X}
	X
	Xint
	Xsockread(buf)
	Xchar *buf;
	X{
	X	int	esave, rz;
	X	char * ret;
	X	if (setjmp(SFGstack)) {
	X		(void) alarm(0);	/* reset alarm clock */
	X		(void) signal(SIGALRM, SIG_DFL);
	X		rd_fp->_flag |= _IOERR;	/* set stdio error */
	X#ifndef ETIMEDOUT
	X		errno = EPIPE;		/* USG doesn't have ETIMEDOUT */
	X#else
	X		errno = ETIMEDOUT;		/* connection timed out */
	X#endif
	X#ifdef SYSLOG
	X		syslog(LOG_ERR,"nntpxfer: read error on server socket: %m");
	X#else
	X		(void) perror("nntpxfer: read error on server socket");
	X#endif
	X		(void) close(server);
	X		exit(1);
	X	}
	X	(void) signal(SIGALRM, to_sfgets);
	X	(void) alarm(TIMEOUT);
	X	ret  = fgets(buf, BUFSIZ, rd_fp);
	X	esave = errno;
	X	(void) alarm(0);			/* reset alarm clock */
	X	(void) signal(SIGALRM, SIG_DFL);	/* reset SIGALRM */
	X	errno = esave;
	X	rz = strlen(buf);
	X	buf[rz-2] = '\0';
	X	if (ret  == (char * ) 0) {
	X#ifdef SYSLOG
	X    		syslog(LOG_ERR,"nntpxfer: read error on server socket: %m");
	X#else
	X		(void) perror("nntpxfer: read error on server socket");
	X#endif
	X		(void) fclose(rd_fp);
	X		exit(1);
	X	}
	X	return(0);
	X}
	X
	Xsockwrite(buf)
	Xchar *buf;
	X	{
	X	register int sz;
	X	char buf2[BUFSIZ];
	X#ifdef DEBUG
	X	(void) printf(">>> %s\n", buf);
	X#endif
	X	(void) strcpy(buf2,buf);
	X	(void) strcat(buf2,"\r\n");
	X	sz = strlen(buf2);
	X	if (write(server,buf2,sz) != sz)
	X		{
	X#ifdef SYSLOG
	X		syslog(LOG_ERR,"nntpxfer: write error on server socket");
	X#else
	X		(void) printf("nntpxfer: write error on server socket\n");
	X#endif
	X		(void) close(server);
	X		exit(1);
	X		}
	X	}
	X
	Xint
	Xwewant(articleid)
	Xchar *articleid;
	X	{
	X#if defined(DBM) || defined(NDBM)
	X	datum k, d;
	X#else
	X	FILE *k;
	X	char *histfile();
	X	FILE *histfp;		/* USG history file */
	X	char line[BUFSIZ];
	X	int len;
	X#endif
	X	char id[BUFSIZ];
	X	char *p;
	X
	X	/* remove any case sensitivity */
	X	(void) strcpy(id, articleid);
	X	p = id;
	X#ifndef CNEWS
	X	while (*p)
	X		{
	X		if (isupper(*p))
	X			*p = tolower(*p);
	X		p++;
	X		}
	X#endif
	X#if defined(DBM) || defined(NDBM)
	X	k.dptr = id;
	X	k.dsize = strlen(articleid) + 1;
	X
	X#ifdef DBM
	X	d = fetch(k);
	X#else
	X 	d = dbm_fetch(db, k);
	X#endif
	X	if (d.dptr)
	X		{
	X#ifdef DEBUG
	X		(void) printf("dup: '%s'\n", articleid);
	X#endif
	X		return(0);
	X		}
	X#ifdef DEBUG
	X	(void) printf("new: '%s'\n", articleid);
	X#endif
	X	return(1);
	X#else
	X	histfp = fopen(histfile(articleid), "r");
	X	if (histfp == NULL) 
	X		{
	X#ifdef DEBUG
	X		(void) printf("new: '%s'\n", articleid);
	X#endif
	X		return(1);
	X		}
	X	len = strlen(articleid);
	X	while (fgets(line, sizeof (line), histfp))
	X		if (!strncmp(articleid, line, len))
	X			break;
	X
	X	if (feof(histfp)) {
	X		(void) fclose(histfp);
	X#ifdef DEBUG
	X		(void) printf("new: '%s'\n", articleid);
	X#endif
	X		return (1);
	X	}
	X	(void) fclose(histfp);
	X#ifdef DEBUG
	X	(void) printf("dup: '%s' %s\n", articleid,line);
	X#endif
	X	return(0);
	X#endif
	X}
	X
	X#ifdef USGHIST
	X/*
	X** Generate the appropriate history subfile name
	X*/
	Xchar *
	Xhistfile(hline)
	Xchar *hline;
	X{
	X	char chr;	/* least significant digit of article number */
	X	static char subfile[BUFSIZ];
	X
	X	chr = findhfdigit(hline);
	X	sprintf(subfile, "%s.d/%c", HISTORY_FILE, chr);
	X	return subfile;
	X}
	X
	Xfindhfdigit(fn)
	Xchar *fn;
	X{
	X	register char *p;
	X	register int chr;
	X	extern char * index();
	X
	X	p = index(fn, '@');
	X	if (p != NULL && p > fn)
	X		chr = *(p - 1);
	X	else
	X		chr = '0';
	X	if (!isdigit(chr))
	X		chr = '0';
	X	return chr;
	X}
	X#endif
	Xchar *
	Xerrmsg(code)
	Xint code;
	X{
	X	extern int sys_nerr;
	X	extern char *sys_errlist[];
	X	static char ebuf[6+5+1];
	X
	X	if (code > sys_nerr || code < 0) {
	X		(void) sprintf(ebuf, "Error %d", code);
	X		return ebuf;
	X	} else
	X		return sys_errlist[code];
	X}
	X
SHAR_EOF
if test 12837 -ne "`wc -c < 'nntpxfer.c'`"
then
	echo shar: error transmitting "'nntpxfer.c'" '(should have been 12837 characters)'
fi
echo "Done with directory 'xfer'"
cd ..
fi
#	End of shell archive
exit 0



More information about the Comp.unix.sysv386 mailing list