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