nntp@tmc.edu (Stan Barber) (03/27/91)
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@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@ucbarpa.berkeley.edu>, Dec 4, 1987 X** Stan Barber <sob@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@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@elroy.jpl.nasa.gov) and X** Steve Groom (stevo@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@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@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@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