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