[comp.sources.unix] v14i052: Network News Transfer Protocol, version 1.5, Part06/09

rsalz@bbn.com (Rich Salz) (04/21/88)

Submitted-by: Phil Lapsley <phil@ucbvax.berkeley.edu>
Posting-number: Volume 14, Issue 52
Archive-name: nntp1.5/part06

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 6 (of 9)."
# Contents:  ./CHANGES ./common/clientlib.c ./server/newnews.c
#   ./xfer/nntpxfer.c ./xmit/remote.c
# Wrapped by rsalz@fig.bbn.com on Tue Apr 19 18:16:45 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f './CHANGES' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./CHANGES'\"
else
echo shar: Extracting \"'./CHANGES'\" \(9242 characters\)
sed "s/^X//" >'./CHANGES' <<'END_OF_FILE'
X
X    This file describes the changes which have been made in NNTP
since the initial release.  Individuals who either reported or
inspired the bug/bug fix are in square brackets.
X
X1.5	February 26, 1988
X
X	New top level Makefile.  [Casey Leedom, casey@lll-crg.llnl.gov]
X
X	Now using strcasecmp/strncasecmp instead of streql/strneql.
X	Due credit is given to the University of California at [sic]
X	Berkeley for the use of this software.  :-)
X
X	Combined common/response_codes.h and common/rfc977.h into
X	common/nntp.h.
X
X	Better fix for getifnetmask ioctl returning 0 netmask.
X
X	Patch to Configure to handle domains for non-portable code.
X	[Brad Smith, brad@saturn.ucsc.edu]
X
X	New version of nntpxmit with article retransmission.
X	[Erik Fair, fair@ucbarpa.Berkeley.EDU].
X
X	System V compatability; will now compile on Hockey-Pux (HPUX).  
X	[Stan Barber, sob%soma.tmc.edu@tmc.edu]
X
X	EXCELAN TCP support.  [Stan Barber, sob%soma.tmc.edu@tmc.edu]
X
X	server/subnet.c now supports compiled-in netmasks.
X	This is useful if you want subnet support on machines
X	that don't have the necessary ioctls for determining
X	network masks on a per-interface basis.
X
X	Fake syslog support included for real this time.
X
X1.4	October 15, 1987
X
X	Reorganized documentation directory.  Thanks for the
X	extraction stuff, Stan.  [Stan Barber, sob%%soma.uucp@rice.edu]
X
X	Added transfer timeouts.  [Steve Schoch, schoch@ames.arpa]
X
X	Fixed a problem with IHAVE which allowed a remote machine to
X	repeatedly feed you articles that you expired (although all
X	you'd do with them is throw them away).
X	[Fred Avolio, avolio@decuac.dec.com]
X
X	DECNet support (see server/dnet_access.c and common/clientlib.c).
X        [Matt Thomas, thomas%syrah.dec@decwrl.dec.com]
X
X	Fixed serious joe code in distribution checks in NEWNEWS.
X
X	NEWNEWS statistics.
X
X	Newsgroup security.
X
X	Performance enhancements (about 2x better for article xfers).
X
X	xhdr command added to improve performance on subject searches.
X
X	Compiled-in server name no longer supported.
X
X	common/clientlib.c/getserverbyfile() now looks at the
X	environment variable NNTPSERVER before checking the file.
X
X	inews/inews.c now limits .signature files to MAX_SIGNATURE lines.
X
X	server/misc.c/spawn() now returns the error output of rnews/inews
X	alone with the posting failed code.  This is in turn printed by
X	inews/inews.c so the user has some idea of why his article wasn't
X	accepted.
X
X	rn patches now for patchlevel #40
X		Bug fix: rrn no longer leaves droppings in /tmp
X		"Skipping unavailable article" problems fixed
X		Support for 4.3 TIOCGWINSZ ioctl [sam@okeeffe.berkeley.edu]
X		Configure asks for domains
X		Pnews/Rnmail understand hostnames with .'s in them.
X		Makefile fixes [harvard!lownlab!kiely]
X
X	PYRAMID #defines removed, as it is all being done by default now.
X
X	inews/inews.c now exits 0, since before it had a random exit
X	status, causing pyramids to choke.  [forys@boulder.colorado.edu]
X
X	server/server.c now logs user/system/elapsed time as floats
X	instead of ints.  [rick@seismo.css.gov]
X
X	server/ihave.c no longer logs every message id transfered but
X	instead keeps statistics which are logged at the end.
X	[rick@seismo.css.gov]
X
X	server/serve.c now times out after TIMEOUT seconds of idleness.
X
X	server/access.c converts remote hostname to lower case
X	when logging, in case you have a nameserver which is helping you.
X
X	server/misc.c/getartbyid now reports message-id's when
X	it encounters a malformed line in the history file.
X	[gds@spam.istc.sri.com]
X
X	inews/inews.c had an uninitialized variable, which
X	could cause trouble.  [jwp%chem@sdcsvax.ucsd.edu]
X
X	common/clientlib.c now understands 4.3 nameserver
X	multiple addresses, and tries them all before
X	giving up.
X
X	common/clientlib.c has has 2 new functions:
X	"getserverbyfile" opens a given file and returns
X	the name of the server given in the file to use
X	for news.  "handle_server_response" prints informative
X	messages based on the initial connection response code.
X
X	server/access.c now is case insensitive when checking
X	for host read/xfer permissions.
X
X	server/misc.c/spawn didn't check for a closed connection
X	while receiving input from client.  As a result, truncated
X	news articles could be received.
X
X	server/newnews.c had a printf which was missing an
X	argument.  [louie@trantor.umd.edu]
X
X	Added fake syslog facility to server.  Code is in
X	server/fakesyslog.c.  [mckenny@sri-unix.arpa]
X
X	Fixed length argument to accept() in server/main.c
X	[mckenny@sri-unix.arpa]
X
X	Now uses pipe to rnews so as to get rnews output for debugging.
X	Also chowns temporary file to POSTER's uid and gid.
X	[mckenny@sri-unix.arpa]
X
X	Fixed bugs in server/netaux.c to close syslog fd.
X	[mckenny@sri-unix.arpa]
X
X	Made bcopy() standard in server/misc.c  [kre@munnari.OZ]
X
X	Documentation changes to make certain things about client
X	installation clearer.  [munnari!charlie.oz!craig]
X
X1.3	30 June 1986
X
X	rrn is no longer included as complete source, but
X	rather as a set of context diffs and a program to
X	apply them to your rn source.  Many thanks go to
X	Gene Spafford for an outstanding job doing this.
X	[spaf@gatech.csnet]
X
X	The dreaded kill/save bug is fixed; rn was passing
X	/bin/sh too many open file descriptors.  Thanks and a tip of the
X	proverbial hat to Chris Maio!  Change to rrn/util.c.
X	[chris@columbia.edu]	
X
X	Fixed a bug in rrn/artio.c which caused an assertion
X	failure on line 114 of artio.c; artopen was returning
X	Nullfp without closing the fp associated with the
X	bogus article.  [genrad!ens@eddie.mit.edu, beorn@ic.berkeley.edu]
X
X	Added #define PYRAMID in common/conf.h, added some
X	#ifdef PYRAMID code in server/misc.c to deal with
X	Pyramids not initializing static data to 0, as well
X	as an fseek problem.  [spaf@gatech.CSNET]
X
X	Another wait bug fixed in spawn() in server/misc.c.
X
X	Added a required \r in post.c.
X
X	Added signal(SIGCHLD, SIG_IGN) to server/serve.c,
X	to fix exit status problem with ALONE defined.
X
X	Statistics logging now returns sum of the nntpd and
X	its children for process time.  server/main.c
X	[fair@ucbarpa.berkeley.edu]
X
X	Subnet support for access file finally added.
X	server/subnet.c added, common/conf.h now has
X	#defines for SUBNET, DAMAGED_NETMASK.
X
X	inews/inews.c now generates a from line with the UUCP
X	name instead of always using gethostname().  common/conf.h
X	changed to add #defines for UUNAME, GHNAME.
X	[jwang@atrp.mit.edu]
X
X	Added LIBS to Makefile. [soma!sob@rice.edu]
X
X1.2c	17 May 1986
X
X	Support for Masscomp added (#define MASSCOMP in ../common/conf.h).
X	[soma!sob@rice.edu]
X
X	Syslog output now requires SYSLOG to be defined in ../common/conf.h.
X	This is handy on systems which, for some reason or another,
X	don't have syslog.  [soma!sob@rice.edu]
X
X	server/post.c had arguments reversed in a printf.  [salex@rice.edu]
X
X	rrn/common.h had PIPESAVER misdefined. [cspencer@bbncc5.arpa]
X
X	server/group.c was missing a \r in a printf.  [lear@rutgers.edu]
X
X	xmit/nntpxmit.c is a new version.  Highlights include
X	improved error reactions and logging info.  [fair@ucbarpa.berkeley.edu]
X
X	xmit/nntpsend is a shell script for sending news via nntp
X	in a sane manner, with locking.  [pleasant@topaz.rutgers.edu,
X	fair@ucbarpa.berkeley.edu]  The locking mechanism is provided
X	courtesy of Mr. Fair's "shlock.c", in xmit/shlock.c.
X
X	support/nntp_awk produces weekly reports from the nntp server
X	logging output.  [fair@ucbarpa.berkeley.edu]
X
X	Makefile (in this directory) would do a "make install" as
X	the default action; it now prints a helpful message.
X	[arnold@cgl.ucsf.edu]
X
X	server/Makefile and support/Makefile had needless dependencies
X	in them; if you didn't do a make depend, you'd have problems
X	on a 4.2 system.  The server and support stuff now depend only
X	on their own .h files.  [arnold@cgl.ucsf.edu]
X
X1.2b	13 April 1986
X
X	common/clientlib.c stupidly had some debugging printfs
X	enabled.
X
X	rrn/{artio.c,head.c} had sprintf("... %d", foo) where "foo"
X	was a long.  %d -> %ld.  [cspencer@bbncc5.arpa]
X
X	server/time.c had an order of evaluation problem in the
X	macro "twodigtoi".  [fletcher@tzec.cs.utexas.edu, among others.]
X
X	server/common.h included <dbm.h> if DBM was defined,
X	caused multiply-defined NULL's.  [cda@opal.berkeley.edu,
X	pleasant@topaz.rutgers.edu, among others.]
X
X	server/active.c would lose because variable "i" would be
X	at the end of the group array if it was called on a timer
X	interrupt.  "i" now set to zero properly.  This only occurs
X	if FASTFORK is defined.  [cda@opal.berkeley.edu]
X
X1.2a	20 March 1986
X
X	common/conf.h defined MAX_GROUPS as 300; this was too low on
X	some machines.  Upped to 450.  [solex@rice.edu, beorn@ic.berkeley.edu]
X
X	rrn/Makefile.sh had .c instead of .o for OBJS and SRCS
X	respectively.  Also had cc -o ../common/clientlib.o (see below).
X
X	inews/inews.c had (char *) 0 for gets(), which made SUN's upset.
X	Changed to simply NULL. [brian@sdcsvax.ucsd.edu]
X
X	inews/Makefile had cc -o ../common/clientlib.o which some
X	machines don't do.  [brian@sdcsvax.ucsd.edu]
X
X	common/clientlib.c has "untp" instead of "nntp".
X
X	server/active.c made more robust about reading active file
X	if active file is longer than MAX_GROUPS.
X
X	server/common.h included common/conf.h after checking for
X	DBM, which caused some problems.  [soma!sob@rice.edu]
X
X1.2	15 March 1986
X
X	Released.
END_OF_FILE
if test 9242 -ne `wc -c <'./CHANGES'`; then
    echo shar: \"'./CHANGES'\" unpacked with wrong size!
fi
# end of './CHANGES'
fi
if test -f './common/clientlib.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./common/clientlib.c'\"
else
echo shar: Extracting \"'./common/clientlib.c'\" \(10189 characters\)
sed "s/^X//" >'./common/clientlib.c' <<'END_OF_FILE'
X#ifndef lint
static char	*sccsid = "@(#)clientlib.c	1.9	(Berkeley) 2/25/88";
X#endif
X
X/*
X * NNTP client routines.
X */
X
X/*
X * Include configuration parameters only if we're made in the nntp tree.
X */
X
X#ifdef NNTPSRC
X#include "../common/conf.h"
X#endif NNTPSRC
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#ifndef EXCELAN
X# include <netdb.h>
X#endif not EXCELAN
X
X#ifdef USG
X# define	index	strchr
X#endif USG
X
X#ifdef EXCELAN
X# define	IPPORT_NNTP	119
X#endif
X
X#ifdef DECNET
X#include <netdnet/dn.h>
X#include <netdnet/dnetdb.h>
X#endif DECNET
X
X#include "nntp.h"
X
FILE	*ser_rd_fp = NULL;
FILE	*ser_wr_fp = NULL;
X
X/*
X * getserverbyfile	Get the name of a server from a named file.
X *			Handle white space and comments.
X *			Use NNTPSERVER environment variable if set.
X *
X *	Parameters:	"file" is the name of the file to read.
X *
X *	Returns:	Pointer to static data area containing the
X *			first non-ws/comment line in the file.
X *			NULL on error (or lack of entry in file).
X *
X *	Side effects:	None.
X */
X
char *
getserverbyfile(file)
char	*file;
X{
X	register FILE	*fp;
X	register char	*cp;
X	static char	buf[256];
X	char		*index();
X	char		*getenv();
X	char		*strcpy();
X
X	if (cp = getenv("NNTPSERVER")) {
X		(void) strcpy(buf, cp);
X		return (buf);
X	}
X
X	if (file == NULL)
X		return (NULL);
X
X	fp = fopen(file, "r");
X	if (fp == NULL)
X		return (NULL);
X
X	while (fgets(buf, sizeof (buf), fp) != NULL) {
X		if (*buf == '\n' || *buf == '#')
X			continue;
X		cp = index(buf, '\n');
X		if (cp)
X			*cp = '\0';
X		(void) fclose(fp);
X		return (buf);
X	}
X
X	(void) fclose(fp);
X	return (NULL);			 /* No entry */
X}
X
X
X/*
X * server_init  Get a connection to the remote news server.
X *
X *	Parameters:	"machine" is the machine to connect to.
X *
X *	Returns:	-1 on error
X *			server's initial response code on success.
X *
X *	Side effects:	Connects to server.
X *			"ser_rd_fp" and "ser_wr_fp" are fp's
X *			for reading and writing to server.
X */
X
server_init(machine)
char	*machine;
X{
X	int	sockt_rd, sockt_wr;
X	char	line[256];
X	char	*index();
X#ifdef DECNET
X	char	*cp;
X
X	cp = index(machine, ':');
X
X	if (cp && cp[1] == ':') {
X		*cp = '\0';
X		sockt_rd = get_dnet_socket(machine);
X	} else
X		sockt_rd = get_tcp_socket(machine);
X#else
X	sockt_rd = get_tcp_socket(machine);
X#endif
X
X	if (sockt_rd < 0)
X		return (-1);
X
X	/*
X	 * Now we'll make file pointers (i.e., buffered I/O) out of
X	 * the socket file descriptor.  Note that we can't just
X	 * open a fp for reading and writing -- we have to open
X	 * up two separate fp's, one for reading, one for writing.
X	 */
X
X	if ((ser_rd_fp = fdopen(sockt_rd, "r")) == NULL) {
X		perror("server_init: fdopen #1");
X		return (-1);
X	}
X
X	sockt_wr = dup(sockt_rd);
X	if ((ser_wr_fp = fdopen(sockt_wr, "w")) == NULL) {
X		perror("server_init: fdopen #2");
X		ser_rd_fp = NULL;		/* from above */
X		return (-1);
X	}
X
X	/* Now get the server's signon message */
X
X	(void) get_server(line, sizeof(line));
X	return (atoi(line));
X}
X
X
X/*
X * get_tcp_socket -- get us a socket connected to the news server.
X *
X *	Parameters:	"machine" is the machine the server is running on.
X *
X *	Returns:	Socket connected to the news server if
X *			all is ok, else -1 on error.
X *
X *	Side effects:	Connects to server.
X *
X *	Errors:		Printed via perror.
X */
X
get_tcp_socket(machine)
char	*machine;
X{
X	int	s;
X	struct	sockaddr_in sin;
X#ifndef EXCELAN
X	struct	servent *getservbyname(), *sp;
X	struct	hostent *gethostbyname(), *hp;
X#ifdef h_addr
X	int	x = 0;
X	register char **cp;
X#endif h_addr
X
X	if ((sp = getservbyname("nntp", "tcp")) ==  NULL) {
X		fprintf(stderr, "nntp/tcp: Unknown service.\n");
X		return (-1);
X	}
X
X	if ((hp = gethostbyname(machine)) == NULL) {
X		fprintf(stderr, "%s: Unknown host.\n", machine);
X		return (-1);
X	}
X
X	bzero((char *) &sin, sizeof(sin));
X	sin.sin_family = hp->h_addrtype;
X	sin.sin_port = sp->s_port;
X#else EXCELAN
X	bzero((char *) &sin, sizeof(sin));
X	sin.sin_family = AF_INET;
X	sin.sin_port = htons(IPPORT_NNTP);
X#endif EXCELAN
X
X	/*
X	 * The following is kinda gross.  The name server under 4.3
X	 * returns a list of addresses, each of which should be tried
X	 * in turn if the previous one fails.  However, 4.2 hostent
X	 * structure doesn't have this list of addresses.
X	 * Under 4.3, h_addr is a #define to h_addr_list[0].
X	 * We use this to figure out whether to include the NS specific
X	 * code...
X	 */
X
X#ifdef	h_addr
X
X	/* get a socket and initiate connection -- use multiple addresses */
X
X	for (cp = hp->h_addr_list; cp && *cp; cp++) {
X		s = socket(hp->h_addrtype, SOCK_STREAM, 0);
X		if (s < 0) {
X			perror("socket");
X			return (-1);
X		}
X	        bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
X		
X		if (x < 0)
X			fprintf(stderr, "trying %s\n", inet_ntoa(sin.sin_addr));
X		x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
X		if (x == 0)
X			break;
X                fprintf(stderr, "connection to %s: ", inet_ntoa(sin.sin_addr));
X		perror("");
X		(void) close(s);
X	}
X	if (x < 0) {
X		fprintf(stderr, "giving up...\n");
X		return (-1);
X	}
X#else	/* no name server */
X#ifdef EXCELAN
X	if ((s = rresvport(SO_KEEPALIVE)) < 0)
X	{
X		/* Get the socket */
X		perror("socket");
X		return (-1);
X	}
X	/* set up addr for the connect */
X	sin.sin_addr.s_addr = rhost(machine);
X	if (sin.sin_addr.s_addr < 0){
X		fprintf(stderr, "%s: Unknown host.\n", machine);
X		return (-1);
X	}
X	/* And then connect */
X
X	if (connect(s, &sin) < 0) {
X		perror("connect");
X		(void) close(s);
X		return (-1);
X	}
X#else not EXCELAN
X	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
X		perror("socket");
X		return (-1);
X	}
X
X	/* And then connect */
X
X	bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
X	if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
X		perror("connect");
X		(void) close(s);
X		return (-1);
X	}
X
X#endif not EXCELAN
X#endif
X
X	return (s);
X}
X
X#ifdef DECNET
X/*
X * get_dnet_socket -- get us a socket connected to the news server.
X *
X *	Parameters:	"machine" is the machine the server is running on.
X *
X *	Returns:	Socket connected to the news server if
X *			all is ok, else -1 on error.
X *
X *	Side effects:	Connects to server.
X *
X *	Errors:		Printed via nerror.
X */
X
get_dnet_socket(machine)
char	*machine;
X{
X	int	s, area, node;
X	struct	sockaddr_dn sdn;
X	struct	nodeent *getnodebyname(), *np;
X
X	bzero((char *) &sdn, sizeof(sdn));
X
X	switch (s = sscanf( machine, "%d%*[.]%d", &area, &node )) {
X		case 1: 
X			node = area;
X			area = 0;
X		case 2: 
X			node += area*1024;
X			sdn.sdn_add.a_len = 2;
X			sdn.sdn_family = AF_DECnet;
X			sdn.sdn_add.a_addr[0] = node % 256;
X			sdn.sdn_add.a_addr[1] = node / 256;
X			break;
X		default:
X			if ((np = getnodebyname(machine)) == NULL) {
X				fprintf(stderr, 
X					"%s: Unknown host.\n", machine);
X				return (-1);
X			} else {
X				bcopy(np->n_addr, 
X					(char *) sdn.sdn_add.a_addr, 
X					np->n_length);
X				sdn.sdn_add.a_len = np->n_length;
X				sdn.sdn_family = np->n_addrtype;
X			}
X			break;
X	}
X	sdn.sdn_objnum = 0;
X	sdn.sdn_flags = 0;
X	sdn.sdn_objnamel = strlen("NNTP");
X	bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel);
X
X	if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) {
X		nerror("socket");
X		return (-1);
X	}
X
X	/* And then connect */
X
X	if (connect(s, (struct sockaddr *) &sdn, sizeof(sdn)) < 0) {
X		nerror("connect");
X		close(s);
X		return (-1);
X	}
X
X	return (s);
X}
X#endif
X
X
X
X/*
X * handle_server_response
X *
X *	Print some informative messages based on the server's initial
X *	response code.  This is here so inews, rn, etc. can share
X *	the code.
X *
X *	Parameters:	"response" is the response code which the
X *			server sent us, presumably from "server_init",
X *			above.
X *			"server" is the news server we got the
X *			response code from.
X *
X *	Returns:	-1 if the error is fatal (and we should exit).
X *			0 otherwise.
X *
X *	Side effects:	None.
X */
X
handle_server_response(response, server)
int	response;
char	*server;
X{
X    switch (response) {
X	case OK_NOPOST:		/* fall through */
X    		printf(
X	"NOTE: This machine does not have permission to post articles.\n");
X		printf(
X	"      Please don't waste your time trying.\n\n");
X
X	case OK_CANPOST:
X		return (0);
X		break;
X
X	case ERR_ACCESS:
X		printf(
X   "This machine does not have permission to use the %s news server.\n",
X		server);
X		return (-1);
X		break;
X
X	default:
X		printf("Unexpected response code from %s news server: %d\n",
X			server, response);
X		return (-1);
X		break;
X    }
X	/*NOTREACHED*/
X}
X
X
X/*
X * put_server -- send a line of text to the server, terminating it
X * with CR and LF, as per ARPA standard.
X *
X *	Parameters:	"string" is the string to be sent to the
X *			server.
X *
X *	Returns:	Nothing.
X *
X *	Side effects:	Talks to the server.
X *
X *	Note:		This routine flushes the buffer each time
X *			it is called.  For large transmissions
X *			(i.e., posting news) don't use it.  Instead,
X *			do the fprintf's yourself, and then a final
X *			fflush.
X */
X
void
put_server(string)
char *string;
X{
X#ifdef DEBUG
X	fprintf(stderr, ">>> %s\n", string);
X#endif
X	fprintf(ser_wr_fp, "%s\r\n", string);
X	(void) fflush(ser_wr_fp);
X}
X
X
X/*
X * get_server -- get a line of text from the server.  Strips
X * CR's and LF's.
X *
X *	Parameters:	"string" has the buffer space for the
X *			line received.
X *			"size" is the size of the buffer.
X *
X *	Returns:	-1 on error, 0 otherwise.
X *
X *	Side effects:	Talks to server, changes contents of "string".
X */
X
get_server(string, size)
char	*string;
int	size;
X{
X	register char *cp;
X	char	*index();
X
X	if (fgets(string, size, ser_rd_fp) == NULL)
X		return (-1);
X
X	if ((cp = index(string, '\r')) != NULL)
X		*cp = '\0';
X	else if ((cp = index(string, '\n')) != NULL)
X		*cp = '\0';
X#ifdef DEBUG
X	fprintf(stderr, "<<< %s\n", string);
X#endif
X
X	return (0);
X}
X
X
X/*
X * close_server -- close the connection to the server, after sending
X *		the "quit" command.
X *
X *	Parameters:	None.
X *
X *	Returns:	Nothing.
X *
X *	Side effects:	Closes the connection with the server.
X *			You can't use "put_server" or "get_server"
X *			after this routine is called.
X */
X
void
close_server()
X{
X	char	ser_line[256];
X
X	if (ser_wr_fp == NULL || ser_rd_fp == NULL)
X		return;
X
X	put_server("QUIT");
X	(void) get_server(ser_line, sizeof(ser_line));
X
X	(void) fclose(ser_wr_fp);
X	(void) fclose(ser_rd_fp);
X}
X
X#ifdef USG
bzero(p, l)
X	register char	*p;
X	register int	l;
X{
X	while (l-- > 0)
X		*p++ = 0;
X}
X#endif USG
END_OF_FILE
if test 10189 -ne `wc -c <'./common/clientlib.c'`; then
    echo shar: \"'./common/clientlib.c'\" unpacked with wrong size!
fi
# end of './common/clientlib.c'
fi
if test -f './server/newnews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./server/newnews.c'\"
else
echo shar: Extracting \"'./server/newnews.c'\" \(10834 characters\)
sed "s/^X//" >'./server/newnews.c' <<'END_OF_FILE'
X#ifndef lint
static char	*sccsid = "@(#)newnews.c	1.19	(Berkeley) 2/6/88";
X#endif
X
X#include "common.h"
X#include "time.h"
X
X#ifdef LOG
int	nn_told = 0;
int	nn_took = 0;
X#endif
X
X
X/*
X * NEWNEWS newsgroups date time ["GMT"] [<distributions>]
X *
X * Return the message-id's of any news articles past
X * a certain date and time, within the specified distributions.
X *
X */
X
newnews(argc, argv)
X	register int	argc;
X	char		*argv[];
X{
X	register char	*cp, *ngp;
X	char		*key;
X	char		datebuf[32];
X	char		line[MAXBUFLEN];
X	char		**distlist, **histlist;
X	static char	**nglist;
X	int		distcount, ngcount, histcount;
X	int		all;
X	FILE		*fp;
X	long		date;
X	long		dtol();
X	char		*ltod();
X#ifdef USG
X	FILE		*tmplst;
X	int		i;
X	char		*tmpfile;
X#endif USG
X
X	if (argc < 4) {
X		printf("%d Usage: NEWNEWS newsgroups yymmdd hhmmss [\"GMT\"] [<distributions>].\r\n",
X			ERR_CMDSYN);
X		(void) fflush(stdout);
X		return;
X	}
X
X#ifdef LOG
X	sprintf(line, "%s newnews %s %s %s %s %s",
X		hostname,
X		argv[1],
X		argv[2],
X		argv[3],
X		(argc >= 5 && *argv[4] == 'G') ? "GMT" : "local",
X		(argc >= 5 && *argv[argc-1] == '<') ? argv[argc-1] : "none");
X	syslog(LOG_INFO, line);
X#endif
X
X	all = (argv[1][0] == '*' && argv[1][1] == '\0');
X	if (!all) {
X		ngcount = get_nglist(&nglist, argv[1]);
X		if (ngcount == 0) {
X			printf("%d Bogus newsgroup specifier: %s\r\n",
X				ERR_CMDSYN, argv[1]);
X			(void) fflush(stdout);
X			return;
X		}
X	}
X
X	/*	    YYMMDD		    HHMMSS	*/
X	if (strlen(argv[2]) != 6 || strlen(argv[3]) != 6) {
X		printf("%d Date/time must be in form YYMMDD HHMMSS.\r\n",
X			ERR_CMDSYN);
X		(void) fflush(stdout);
X		return;
X	}
X
X	(void) strcpy(datebuf, argv[2]);
X	(void) strcat(datebuf, argv[3]);
X
X	argc -= 4;
X	argv += 4;
X
X	/*
X	 * Flame on.  The history file is not stored in GMT, but
X	 * in local time.  So we have to convert GMT to local time
X	 * if we're given GMT, otherwise we need only chop off the
X	 * the seconds.  Such braindamage.
X	 */
X
X	key = datebuf;		/* Unless they specify GMT */
X
X	if (argc > 0) {
X		if (!strcasecmp(*argv, "GMT")) { /* Which we handle here */
X			date = dtol(datebuf);
X			if (date < 0) {
X				printf("%d Invalid date specification.\r\n",
X					ERR_CMDSYN);
X				(void) fflush(stdout);
X				return;
X			}
X			date = gmt_to_local(date);
X			key = ltod(date);
X			++argv;
X			--argc;
X		}
X	}
X
X	/* So, key now points to the local time, but we need to zap secs */
X
X	key[10] = '\0';
X
X	distcount = 0;
X	if (argc > 0) {
X		distcount = get_distlist(&distlist, *argv);
X		if (distcount < 0) {
X			printf("%d Bad distribution list: %s\r\n", ERR_CMDSYN,
X				*argv);
X			(void) fflush(stdout);
X			return;
X		}
X	}
X
X#ifdef USG
X    if ((tmpfile = mktemp("/tmp/listXXXXXX")) == NULL ||
X		(tmplst = fopen(tmpfile, "w+")) == NULL) {
X	printf("%d Cannot process history file.\r\n", ERR_FAULT);
X	(void) fflush(stdout);
X	return;
X    }
X
X    for (i = 0; i < 9; i++) {
X		sprintf(historyfile, "%s.d/%d", HISTORY_FILE, i);
X#endif USG
X
X	fp = fopen(historyfile, "r");
X	if (fp == NULL) {
X#ifdef SYSLOG
X		syslog(LOG_ERR, "newnews: fopen %s: %m", historyfile);
X#endif
X#ifndef USG
X		printf("%d Cannot open history file.\r\n", ERR_FAULT);
X		(void) fflush(stdout);
X		return;
X#else USG
X		continue;
X#endif USG
X	}
X
X#ifndef USG
X	printf("%d New news by message id follows.\r\n", OK_NEWNEWS);
X#endif not USG
X
X	if (seekuntil(fp, key, line, sizeof (line)) < 0) {
X#ifndef USG
X		printf(".\r\n");
X		(void) fflush(stdout);
X#endif not USG
X		(void) fclose(fp);
X#ifndef USG
X		return;
X#else USG
X		continue;
X#endif USG
X	}
X
X/*
X * History file looks like:
X *
X * <1569@emory.UUCP>	01/22/86 09:19	net.micro.att/899 ucb.general/2545 
X *		     ^--tab            ^--tab		 ^--space         ^sp\0
X * Sometimes the newsgroups are missing; we try to be robust and
X * ignore such bogosity.  We tackle this by our usual parse routine,
X * and break the list of articles in the history file into an argv
X * array with one newsgroup per entry.
X */
X
X	do {
X		if ((cp = index(line, '\t')) == NULL)
X			continue;
X
X		if ((ngp = index(cp+1, '\t')) == NULL)	/* 2nd tab */
X			continue;
X		++ngp;			/* Points at newsgroup list */
X		if (*ngp == '\n')
X			continue;
X		histcount = get_histlist(&histlist, ngp);
X		if (histcount == 0)
X			continue;
X
X		/*
X		 * For each newsgroup on this line in the history
X		 * file, check it against the newsgroup names we're given.
X		 * If it matches, then see if we're hacking distributions.
X		 * If so, open the file and match the distribution line.
X		 */
X
X		if (!all)
X			if (!ngmatch(restreql, 0, nglist, ngcount,
X			    histlist, histcount))
X				continue;
X
X		if (distcount)
X			if (!distmatch(distlist, distcount, histlist, histcount))
X				continue;
X
X		*cp = '\0';
X#ifdef USG
X		fputs(line, tmplst);
X		fputc('\n', tmplst);
X#else not USG
X		putline(line);
X#endif not USG
X#ifdef LOG
X		nn_told++;
X#endif
X	} while (fgets(line, sizeof(line), fp) != NULL);
X
X#ifndef USG
X	putchar('.');
X	putchar('\r');
X	putchar('\n');
X	(void) fflush(stdout);
X#endif
X	(void) fclose(fp);
X#ifdef USG
X    }
X    printf("%d New news by message id follows.\r\n", OK_NEWNEWS);
X    rewind(tmplst);
X    while (fgets(line, sizeof(line), tmplst) != NULL)
X            putline(line);
X    putchar('.');
X    putchar('\r');
X    putchar('\n');
X    (void) fflush(stdout);
X    (void) fclose(tmplst);
X    (void) unlink(tmpfile);
X#endif USG
X}
X
X
X/*
X * seekuntil -- seek through the history file looking for
X * a line with date "key".  Get that line, and return.
X *
X *	Parameters:	"fp" is the active file.
X *			"key" is the date, in form YYMMDDHHMM (no SS)
X *			"line" is storage for the first line we find.
X *
X *	Returns:	-1 on error, 0 otherwise.
X *
X *	Side effects:	Seeks in history file, modifies line.
X */
X
seekuntil(fp, key, line, linesize)
X	FILE		*fp;
X	char		*key;
X	char		*line;
X	int		linesize;
X{
X	char		datetime[32];
X	register int	c;
X	register long	top, bot, mid;
X
X	bot = 0;
X	(void) fseek(fp, 0L, 2);
X	top = ftell(fp);
X	for(;;) {
X		mid = (top+bot)/2;
X		(void) fseek(fp, mid, 0);
X		do {
X			c = getc(fp);
X			mid++;
X		} while (c != EOF && c!='\n');
X		if (!getword(fp, datetime, line, linesize)) {
X			return (-1);
X		}
X		switch (compare(key, datetime)) {
X		case -2:
X		case -1:
X		case 0:
X			if (top <= mid)
X				break;
X			top = mid;
X			continue;
X		case 1:
X		case 2:
X			bot = mid;
X			continue;
X		}
X		break;
X	}
X	(void) fseek(fp, bot, 0);
X	while(ftell(fp) < top) {
X		if (!getword(fp, datetime, line, linesize)) {
X			return (-1);
X		}
X		switch(compare(key, datetime)) {
X		case -2:
X		case -1:
X		case 0:
X			break;
X		case 1:
X		case 2:
X			continue;
X		}
X		break;
X	}
X
X	return (0);
X}
X
X
compare(s, t)
X	register char *s, *t;
X{
X	for (; *s == *t; s++, t++)
X		if (*s == 0)
X			return(0);
X	return (*s == 0 ? -1:
X		*t == 0 ? 1:
X		*s < *t ? -2:
X		2);
X}
X
X
getword(fp, w, line, linesize)
X	FILE		*fp;
X	register char	*w;
X	char		*line;
X	int		linesize;
X{
X	register char	*cp;
X
X	if (fgets(line, linesize, fp) == NULL)
X		return (0);
X	if (cp = index(line, '\t')) {
X/*
X * The following gross hack is present because the history file date
X * format is braindamaged.  They like "mm/dd/yy hh:mm", which is useless
X * for relative comparisons of dates using something like atoi() or
X * strcmp.  So, this changes their format into yymmddhhmm.  Sigh.
X *
X * 12345678901234	("x" for cp[x])
X * mm/dd/yy hh:mm 	(their lousy representation)
X * yymmddhhmm		(our good one)
X * 0123456789		("x" for w[x])
X */
X		*cp = '\0';
X		(void) strncpy(w, cp+1, 15);
X		w[0] = cp[7];		/* Years */
X		w[1] = cp[8];
X		w[2] = cp[1];		/* Months */
X		w[3] = cp[2];
X		w[4] = cp[4];		/* Days */
X		w[5] = cp[5];
X		w[6] = cp[10];		/* Hours */
X		w[7] = cp[11];
X		w[8] = cp[13];		/* Minutes */
X		w[9] = cp[14];
X		w[10] = '\0';
X	} else
X		w[0] = '\0';
X	return (1);
X}
X
X
X/*
X * distmatch -- see if a file matches a set of distributions.
X * We have to do this by (yech!) opening the file, finding
X * the Distribution: line, if it has one, and seeing if the
X * things match.
X *
X *	Parameters:	"distlist" is the distribution list
X *			we want.
X *			"distcount" is the count of distributions in it.
X *			"grouplist" is the list of groups (articles)
X *			for this line of the history file.  Note that
X *			this isn't quite a filename.
X *			"groupcount" is the count of groups in it.
X *			
X *	Returns:	1 if the article is in the given distribution.
X *			0 otherwise.
X */
X
distmatch(distlist, distcount, grouplist, groupcount)
X	char		*distlist[];
X	int		distcount;
X	char		*grouplist[];
X	int		groupcount;
X{
X	register char	c;
X	register char	*cp;
X	register FILE	*fp;
X	register int	i, j;
X	char		buf[MAXBUFLEN];
X
X	(void) strcpy(buf, spooldir);
X	(void) strcat(buf, "/");
X	(void) strcat(buf, grouplist[0]);
X
X	for (cp = buf; *cp; cp++)
X		if (*cp == '.')
X			*cp = '/';
X
X	fp = fopen(buf, "r");
X	if (fp == NULL) {
X#ifdef SYSLOG
X		syslog(LOG_ERR, "distmatch: fopen %s: %m", buf);
X#endif
X		return (0);
X	}
X
X	while (fgets(buf, sizeof (buf), fp) != NULL) {
X		if ((c = buf[0]) == '\n')		/* End of header */
X			break;
X		if (c != 'd' && c != 'D')
X			continue;
X		cp = index(cp + 1, '\n');
X		if (cp)
X			*cp = '\0';
X		cp = index(buf, ':');
X		if (cp == NULL)
X			continue;
X		*cp = '\0';
X		if (!strcasecmp(buf, "distribution")) {
X			for (i = 0; i < distcount; ++i) {
X				if (!strcasecmp(cp + 2, distlist[i])) {
X					(void) fclose(fp);
X					return (1);
X				}
X			}
X			(void) fclose(fp);
X			return (0);
X		}
X	}
X
X	(void) fclose(fp);
X
X	/*
X	 * We've finished the header with no distribution field.
X	 * So we'll assume that the distribution is the characters
X	 * up to the first dot in the newsgroup name.
X	 */
X
X	for (i = 0; i < groupcount; i++) {
X		cp = index(grouplist[i], '.');
X		if (cp)
X			*cp = '\0';
X		for (j = 0; j < distcount; j++)
X			if (!strcasecmp(grouplist[i], distlist[j]))
X				return (1);
X	}
X		
X	return (0);
X}
X
X
X/*
X * get_histlist -- return a nicely set up array of newsgroups
X * (actually, net.foo.bar/article_num) along with a count.
X *
X *	Parameters:		"array" is storage for our array,
X *				set to point at some static data.
X *				"list" is the history file newsgroup list.
X *
X *	Returns:		Number of group specs found.
X *
X *	Side effects:		Changes static data area.
X */
X
get_histlist(array, list)
X	char		***array;
X	char		*list;
X{
X	register int	histcount;
X	register char	*cp;
X	static	char	**hist_list = (char **) NULL;
X
X	cp = index(list, '\n');
X	if (cp)
X		*cp-- = '\0';
X	histcount = parsit(list, &hist_list);
X	*array = hist_list;
X	return (histcount);
X}
X
X
X/*
X * get_nglist -- return a nicely set up array of newsgroups
X * along with a count, when given an NNTP-spec newsgroup list
X * in the form ng1,ng2,ng...
X *
X *	Parameters:		"array" is storage for our array,
X *				set to point at some static data.
X *				"list" is the NNTP newsgroup list.
X *
X *	Returns:		Number of group specs found.
X *
X *	Side effects:		Changes static data area.
X */
X
get_nglist(array, list)
X	char		***array;
X	char		*list;
X{
X	register char	*cp;
X	register int	ngcount;
X
X	for (cp = list; *cp != '\0'; ++cp)
X		if (*cp == ',')
X			*cp = ' ';
X
X	ngcount = parsit(list, array);
X
X	return (ngcount);
X}
END_OF_FILE
if test 10834 -ne `wc -c <'./server/newnews.c'`; then
    echo shar: \"'./server/newnews.c'\" unpacked with wrong size!
fi
# end of './server/newnews.c'
fi
if test -f './xfer/nntpxfer.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./xfer/nntpxfer.c'\"
else
echo shar: Extracting \"'./xfer/nntpxfer.c'\" \(10068 characters\)
sed "s/^X//" >'./xfer/nntpxfer.c' <<'END_OF_FILE'
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@athena.mit.edu)
X */
X
X#define DEBUG
X
X/* you'd think that 4096 articles at one go is enough.... */
X#define MAXARTS	4096
X
X#include <sys/types.h>
X#include <sys/dir.h>
X#include <sys/socket.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include <sys/file.h>
X#include <sys/time.h>
X#include <sys/wait.h>
X#include <sys/resource.h>
X
X#include <net/if.h>
X#include <netinet/in.h>
X
X#include <stdio.h>
X#include <errno.h>
X#include <ctype.h>
X#include <netdb.h>
X#include <signal.h>
X#include <dbm.h>
X
X#define INEWS	"/usr/lib/news/inews -p"
X#define HIST	"/usr/lib/news/history"
X
char	*malloc();
char	*strcpy();
char	*strcat();
long	time();
u_long	inet_addr();
X
extern int errno;
char *artlist[MAXARTS];
int server;			/* stream socket to the nntp server */
int newart, dupart, misart;
X
main(argc, argv)
int argc;
char *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 connected = 0;		/* 1 = connected */
X	int i;
X	int omitupdate = 0;		/* 1 = don't update datetime */
X	long clock;
X	long newdate, newtime;
X	struct hostent *hp;
X	struct servent *sp;
X	struct sockaddr_in sin;
X	struct tm *now;
X
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++;
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) strcpy(dtname, "/usr/spool/news/nntp.");
X		(void) strcat(dtname, argv[1]);
X		dtfile = fopen(dtname, "r");
X		if (dtfile == NULL)
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		}
X
X#ifdef DEBUG
X	(void) printf("newsgroups = '%s'\n", newsgroups);
X	(void) printf("date = '%s'\n", lastdate);
X	(void) printf("time = '%s'\n", lasttime);
X	(void) printf("distributions = '%s'\n", distributions);
X	(void) printf("now is = %06d %06d\n", newdate, newtime);
X#endif
X
X	if (dbminit(HIST) < 0)
X		{
X		perror("couldn't open history file");
X		exit(1);
X		}
X
X	sin.sin_addr.s_addr = inet_addr(argv[1]);
X	if (sin.sin_addr.s_addr != -1) 
X		{
X		sin.sin_family = AF_INET;
X		}
X	else 
X		{
X		hp = gethostbyname(argv[1]);
X		if (hp == NULL) 
X			{
X			(void) printf("%s: unknown host\n", argv[1]);
X			exit(1);
X			}
X
X		sin.sin_family = hp->h_addrtype;
X#ifdef	BSD43
X		bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr,
X			hp->h_length);
X#else	BSD43
X		bcopy(hp->h_addr, (caddr_t)&sin.sin_addr,
X			hp->h_length);
X#endif	BSD43
X		}
X	
X	sp = getservbyname("nntp", "tcp");
X	if (sp == NULL)
X		{
X		perror("nntp/tcp");
X		exit(1);
X		}
X
X	sin.sin_port = sp->s_port;
X
X	do	{
X		server = socket(AF_INET, SOCK_STREAM, 0);
X		if (server < 0) 
X			{
X			perror("nntpxfer: socket");
X			exit(1);
X			}
X
X		if (connect(server, (struct sockaddr *)&sin, sizeof (sin)) < 0) 
X			{
X#ifdef	BSD43
X			if (hp && hp->h_addr_list[1]) 
X				{
X				hp->h_addr_list++;
X				bcopy(hp->h_addr_list[0],
X				    (caddr_t)&sin.sin_addr, hp->h_length);
X				(void) close(server);
X				continue;
X				}
X#endif	BSD43
X			perror("nntpxfer: connect");
X			exit(1);
X			}
X		connected++;
X		}
X	while (connected == 0);
X
X#ifdef DEBUG
X	(void) 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
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		(void) printf("protocol error: got '%s'\n", buf);
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		(void) printf("protocol error: got '%s'\n", buf);
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		(void) printf("protocol error: got '%s'\n", buf);
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	while (1)
X		{
X		(void) sockread(buf);
X		if (!strcmp(buf,"."))
X			break;
X		if (wewant(buf))
X			{
X			if (newart > MAXARTS)
X				{
X				omitupdate++;
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=1; 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		(void) printf("error: got '%s'\n", buf);
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 == NULL)
X			{
X			perror(dtname);
X			exit(1);
X			}
X		(void) fputs(buf,dtfile);
X		(void) fclose(dtfile);
X		}
X	exit(0);
X}
X
artfetch(articleid)
char *articleid;
X	{
X	int lines = 0;
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		(void) printf("protocol error: got '%s'\n", buf);
X		(void) close(server);
X		exit(1);
X		}
X#ifdef DEBUG
X	(void) printf("command: %s\n", INEWS);
X#endif
X	if ( (inews = popen(INEWS, "w")) == NULL)
X		{
X		perror(INEWS);
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		lines++;
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
int
sockread(buf)
char *buf;
X	{
X	char c;
X	int j = 0;
X#ifdef BSD43
X	fd_set rf;
X#else BSD43
X	int rf;
X#endif BSD43
X	struct timeval tv;
X	int r;
X	char *p = buf;
X
X	while ( 1 )
X		{
X		tv.tv_sec = 1800;	/* 15 minutes */
X		tv.tv_usec = 0L;
X#ifdef BSD43
X		FD_ZERO(&rf);
X		FD_SET(server, &rf);
X#else BSD43
X		rf = 1 << server;
X#endif BSD43
X		r = select(20, (fd_set *)&rf, (fd_set *)0, (fd_set *)&rf, &tv);
X
X		if (r < 0)
X			{
X			if (errno == EINTR)
X				continue;
X			perror("getsock select");
X			exit(1);
X			}
X		if (r == 0)
X			{
X			printf("read timed out.\n");
X			exit(1);
X			}
X
X		if (read(server, &c, 1) <= 0)
X			break;
X
X		/* mask off any chance parity bits */
X		*p = c & 0x7f;
X
X		/* look for end of line (== LF) */
X		if (c == 0x0a)
X			{
X			if (j > 0 && *(p-1) == 0x0d)
X				*(p-1) = '\0';
X			else
X				*p = '\0';
X			return(strlen(buf));
X			}
X		j++; p++;
X		}
X	perror("sockread");
X	(void) close(server);
X	exit(1);
X	/* NOTREACHED */
X	}
X
sockwrite(buf)
char *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		(void) printf("write error on server socket\n");
X		(void) close(server);
X		exit(1);
X		}
X	}
X
int
wewant(articleid)
char *articleid;
X	{
X	datum k, d;
X	char id[BUFSIZ];
X	char *p;
X
X	/* remove any case sensitivity */
X	(void) strcpy(id, articleid);
X	p = id;
X	while (*p)
X		{
X		if (isupper(*p))
X			*p = tolower(*p);
X		p++;
X		}
X
X	k.dptr = id;
X	k.dsize = strlen(articleid) + 1;
X
X	d = fetch(k);
X
X	if (d.dptr)
X		{
X#ifdef DEBUG
X		(void) printf("dup: '%s'\n", articleid);
X#endif
X		return(0);
X		}
X
X#ifdef DEBUG
X	(void) printf("new: '%s'\n", articleid);
X#endif
X	return(1);
X	}
END_OF_FILE
if test 10068 -ne `wc -c <'./xfer/nntpxfer.c'`; then
    echo shar: \"'./xfer/nntpxfer.c'\" unpacked with wrong size!
fi
# end of './xfer/nntpxfer.c'
fi
if test -f './xmit/remote.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./xmit/remote.c'\"
else
echo shar: Extracting \"'./xmit/remote.c'\" \(9250 characters\)
sed "s/^X//" >'./xmit/remote.c' <<'END_OF_FILE'
X/*
X** remote communication routines for NNTP/SMTP style communication.
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@ucbarpa.berkeley.edu>
X*/
X
X#include "nntpxmit.h"
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <errno.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <setjmp.h>
X#include <signal.h>
X#ifdef SYSLOG
X#include <syslog.h>
X#endif
X#include "get_tcp_conn.h"
X#include "nntp.h"
X
static	jmp_buf	SFGstack;
FILE	*rmt_rd;
FILE	*rmt_wr;
char	*sfgets();
char	*rfgets();
X
extern	int	errno;
extern	char	Debug;
extern	char	*errmsg();
extern	char	*strcpy();
extern	void	log();
X
X/*
X** send cmd to remote, terminated with a CRLF.
X*/
sendcmd(cmd)
char	*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*/
readreply(buf, size)
register char	*buf;
int	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*/
converse(buf, size)
char	*buf;
int	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*/
hello(host, transport)
char	*host;
int	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*/
goodbye(wait_for_reply)
int	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
static
to_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*/
char *
sfgets(buf, size, fp)
char	*buf;
int	size;
FILE	*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*/
char *
rfgets(buf, size, fp)
char	*buf;
int	size;
FILE	*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*/
sendfile(fp)
FILE	*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/*
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#define	PUTC(c)	if (putc(c, remote) == EOF) return(FALSE)
X
X	if (fp == (FILE *)NULL)
X		return(FALSE);
X
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		switch(c) {
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	return( !(sendcmd(".") || ferror(fp)) );
X}
END_OF_FILE
if test 9250 -ne `wc -c <'./xmit/remote.c'`; then
    echo shar: \"'./xmit/remote.c'\" unpacked with wrong size!
fi
# end of './xmit/remote.c'
fi
echo shar: End of archive 6 \(of 9\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 9 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.