DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T n

⟦7e852fce7⟧ TextFile

    Length: 23526 (0x5be6)
    Types: TextFile
    Names: »nntplink.c«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦f91e15335⟧ »EurOpenD3/news/nntp/nntplink2.0.0.tar.Z« 
        └─⟦2c70c5e6b⟧ 
            └─⟦this⟧ »nntplink.c« 

TextFile

/** nntplink - transmit netnews articles across the internet with nntp,
 **	      holding the link open semi-permanently.
 **
 ** Derived from nntpxmit.c (by Erik Fair) by Warren Lavallee.
 **
 ** Copyright (c) 1989 Samsung Software America, Inc.  All rights reserved.
 ** 
 ** Permission is hereby granted to copy, reproduce, redistribute or
 ** otherwise use this software as long as: (1) there is no monetary
 ** profit gained specifically from the use or reproduction of this
 ** software, (2) it is not sold, rented, traded, or otherwise marketed,
 ** (3) the above copyright notice and this paragraph is included
 ** prominently in an copy made, and (4) that the name of Samsung
 ** is not used to endorse or promote products derived from this software
 ** without the specific prior written permission of Samsung Software America.
 ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 ** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 ** WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ** 
 **
 ** Version history:
 ** 1.01.0     - 11-02-89 - SVR3 compatible?
 ** 1.01.1     - 11-03-89 - Cnews batchfile compatibility (coolidge@cs.uiuc.edu)
 ** 1.01.2     - 11-05-89 - Removed -x option.  It is always set now.
 **				Very small change to safecopy().
 ** 1.01.3     - 11-08-89 - Fix dprintf bug (big bug) <fletcher@cs.utexas.edu>
 ** 1.01.4     - 11-10-89 - Added HP/UX support.  <icsu6000@cs.montana.edu>
 ** 1.10.0     - 11-11-89 - Initial public release
 ** 1.10.2     - 11-15-89 - Fixes major bug (dropped articles)---
 **                          linked lists removed.
 **	       - 11-19-89 - Added -L and -C options, reorganized stats
 **			    gathering. <lamy@ai.utoronto.ca>
 ** 1.10.3     - 11-20-89 - Removed one-shot feature-- doesn't apply.
 **                         Added -S option <coolidge@cs.uiuc.edu>
 ** 1.10.4     - 11-23-89 - Added -I (idling time out) <lamy@ai.utoronto.ca>
 ** 1.10.5     - 11-27-89 - Split up nntplink.c to several smaller files.
 **				Message-id is now save in rewrite()
 **				Code to check C-news .vs. B-news (spots cfg
 **								errors)
 **			    
 ** Contributors/Testers:
 **    karl@tut.cis.ohio-state.edu	(Karl Kleinpaste)
 **    fletcher@cs.utexas.edu		(Fletcher Mattox)
 **    mcooper@usc.edu			(Micheal Cooper)
 **    alden@gem.mps.ohio-state.edu	(Dave Alden)
 **    icsu6000@caesar.cs.montana.edu	(Mathisen)
 **    lamy@ai.utoronto.ca		(Jean-Francois Lamy)
 **    coolidge@cs.uiuc.edu             (John Coolidge)
 **
 ** Testers:
 **    dubois@uakari.primate.wisc.edu	(Paul Dubois)
 **    coolidge@casca.cs.uiuc.edu	(John Coolidge)
 **    lloyd@aplcen.jhu.edu		(Lloyd W. Taylor)
 **    tale@pawl.rpi.edu		(David C Lawrence)
 **    mb@rex.cs.tulane.edu		(Mark Benard)
 **    asp@uunet.uu.net			(Andrew Partan)
 **
 ** OPERATING SYSTEMS:
 **    Pyramid DualPort OSx4.4c
 **    Dynix 3.0 (Symmetry + Balance)
 **    HP-UX 6.01
 **    Sun OS {4.0,3.5}
 **    Ultrix 3.?
 **    4.3tahoe (uVAX III)
 **
 **/

#include "conf.h"

#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#ifdef	BSD4_2
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#else
#include <sys/times.h>
extern	time_t	time();
#endif	BSD4_2

#include <fcntl.h>
#include <signal.h>

#ifdef USG
#include "sysexits.h"
#else
#include <sysexits.h>
#endif

#ifdef	SYSLOG
#include <syslog.h>
#endif	SYSLOG

#include "nntp.h"

#ifdef BSD4_2
#include <sys/wait.h>
#endif

#include <sys/stat.h>

#define	MAXFNAME	BUFSIZ	/* maximum filename size - big enough? */
#define	FCLOSE(fp)	(void) fclose(fp); (fp) = (FILE *)NULL
  
  FILE	*getfp();
char	*errmsg();
void	requeue();
void	catchsig();
void	logstats();
void	log();
void    usage();
int	interrupted();
void	waitingfor();

/*
 ** Globals that certain things need.
 **
 ** Various subroutines want the program name to report errors.
 ** The queue file, queue file pointer and current article name are
 ** there to write out the state of the queue file from a signal handler
 ** (that is, the list of unsent and (possibly) failed articles) so
 ** that when next we try sending to a given remote site, we don't send
 ** stuff we've already sent.
 */
char	*Pname;			/* this program's invocation name */
char	*Host;			/* current remote host */
char    *TQfile;                /* BOGUS HACK - BETA RELEASE ONLY */
char	Qfile[MAXFNAME];	/* current queue file we're operating on */
FILE	*Qfp;			/* the (FILE *) for above */
char	Article[MAXFNAME];	/* current article filename */
char	FailedArticle[MAXFNAME];/* article to requeue */
int	resetstats = TRUE;	/* true after a logstats() */
     
unsigned short connected = 0;
char nntpfile[MAXFNAME];

#ifdef CNEWS
int cnews = 1;
#else
int cnews = 0;
#endif
     
char *safecopy();

int     Use_Log_File = FALSE;
int     In_Log_File = FALSE;
char	logline[MAXLOGLENGTH] = "";
char	old_logline[MAXLOGLENGTH] = "";
char	orig_Qfile[MAXFNAME];
char	orig_nntpfile[MAXFNAME];
char	sysname[MAXSYSNAME];

/*
 ** Some flags, toggled by arguments
 */
#define	TOGGLE(boolean)	(boolean) = !(boolean)
char	Debug = FALSE;
char	Report_Stats = TRUE;
char	ReQueue_Fails = TRUE;
     
/*
 ** Numeric arguments
 */
int	Close_After=CLOSE_AFTER;    /* close connection to remote at next
                                       pause if we've offered this many
                                       articles since last pause */
int	Log_After = LOG_AFTER;	    /* log stats at next pause if we've
                                       proposed this many aritcles since
                                       last pause */
int     Sleep_Time = SLEEP_INT;
                                    /* Sleep Sleep_Time seconds between
                                       checks of the queue file. */
char	CheckIdleTimeout = FALSE;
long	IdleTimeOut;	            /* if we've been waiting for batch file
                                       for longer than this many seconds,
                                       close link while waiting */
char    CheckExitTimeout = FALSE;
long    ExitTimeOut;
     
char *USAGE = "usage: nntplink [-d][-s][-r][-T][-F][-D][-Sn][-Cn][-Ln][-In][-En][-Xsysname] hostname|hostname:file [...]";
     
char	*Fmt = "%s localhost %s[%d]: %s\n";
char	*E_fopen = "fopen(%s, \"%s\"): %s";
char	*E_unlk = "unlink(%s): %s";
#ifdef	USELOG
char	*NNTPlog = USELOG;	/* yet another external log file */
FILE	*Logfp = (FILE *)NULL;
int	fd;
#endif	USELOG
     
struct {
  u_long	offered;
  u_long	accepted;
  u_long	rejected;
  u_long	failed;
  u_long	since_close;
} Stats = {0L, 0L, 0L, 0L, 0L};
     
double Tbegin, Tend;		/* transfer timestamps */
double ouser = 0.0, osys = 0.0;
double user, sys;

extern	int	errno;
extern 	int	strncmp();
extern	char	*rindex();
extern	char	*index();
extern	char	*mktemp();
extern	char	*strcpy();

#ifdef	USG
void
bzero(s, l)
     register caddr_t s;
     register int	l;
{
  while(l-- > 0) *s++ = 0;
}
#endif	USG

extern char *compiled, *version;

/*
 ** Former parts of main(), now externals.
 */
int	i;
int	transport = T_IP_TCP;	/* default is IP/TCP */
#ifdef	USELOG
char	*amode = "a";
#endif	USELOG
#ifdef	BSD4_2
struct timeval tod;
struct timezone tz;
#endif	BSD4_2

main(ac, av)
     int	ac;
     char	*av[];
{
  
#ifdef	BSD4_2
  (void) gettimeofday(&tod, &tz);
  Tend = Tbegin = tod.tv_sec + (double)tod.tv_usec/1000000.;
#else
  Tend = Tbegin = (double) time((time_t *)NULL);
#endif	BSD4_2
  
#ifdef BATCH_DIR
  chdir (BATCH_DIR);
#endif
  
  Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]);
  
  if (ac < 2) {
    usage();
  }
  
#ifdef	SYSLOG
  /* 4.2 BSD openlog has only two args */
#ifdef	LOG_LOCAL7
  (void) openlog(Pname, LOG_PID, LOG_LINK);
#else
  (void) openlog(Pname, LOG_PID);
#endif	LOG_LOCAL_7
#endif	SYSLOG
#ifdef	USELOG
  fd = open(NNTPlog,O_APPEND|O_CREAT|O_WRONLY);
  if ((Logfp = fdopen(fd, amode)) == (FILE *)NULL) {
    char	buf[BUFSIZ];
    
    sprintf(buf, E_fopen, NNTPlog, amode, errmsg(errno));
    log(L_NOTICE, buf);
  }
#endif	USELOG
  
  for(i = 1; i < ac; i++) {
    if (av[i][0] == '-') {
      switch(av[i][1]) {
      case 'T':
	transport = T_IP_TCP;
	break;
      case 'D':
	transport = T_DECNET;
	break;
      case 'F':
	transport = T_FD;
	break;
      case 's':
	TOGGLE(Report_Stats);
	break;
      case 'd':
	TOGGLE(Debug);
	break;
      case 'r':
	TOGGLE(ReQueue_Fails);
	break;
      case 'C':
	Close_After = atoi(av[i]+2);
	break;
      case 'L':
	Log_After = atoi(av[i]+2);
        break;
      case 'S':
        Sleep_Time = atoi(av[i]+2);
        break;
      case 'I':
	CheckIdleTimeout = TRUE;
	IdleTimeOut = atoi(av[i]+2);
	break;
      case 'E':
	CheckExitTimeout = TRUE;
	ExitTimeOut = atoi(av[i]+2);
	break;
      case 'X':
	strcpy(sysname, " ");		/* We want to make sure that we */
	strcat(sysname, av[i]+2);	/* don't match part of another */
	strcat(sysname, " ");           /* sysname, so we pad with spaces */
	Use_Log_File = TRUE;
	break;
      case 'v':

      default:
	fprintf(stderr, "%s: no such option: -%c\n",
	      Pname, av[i][1]);
	usage();
      }
      continue;
    }
    
    /*
     ** OK, it wasn't an option, therefore it must be a
     ** hostname, filename pair.
     **
     ** If the user typed host::file, then it's DECNET,
     ** whether they remembered the "-D" option or not.
     */
    Host = av[i];
    if ((TQfile = index(Host, ':')) != (char *)NULL) {
      if (TQfile[1] == ':') {
	transport = T_DECNET;
	*TQfile++ = '\0';
      } else if (transport != T_FD)
	transport = T_IP_TCP;
      *TQfile++ = '\0';
    } else
      TQfile = Host;

    strcpy(Qfile, TQfile);
    strcpy(orig_Qfile, TQfile);

    /*
     * If we're not debugging, then we should fade into the
     * background completely, disconnecting from the terminal,
     * and shutting off signals.
     */
    if (!Debug)
      {
#ifdef TIOCNOTTY
	int fd;
#endif
#ifdef AUTOBACKGROUND
	int p;

	if ((p = fork()) < 0)		/* ah-oo-gah!  Dive, dive! */
	  {
	    (void) fprintf(stderr, "%s: fork failed\n", Pname);
	    exit(1);
	  }
	if (p != 0)
	  exit(0);			/* parent exit, child continues */
	
	(void) signal(SIGHUP, SIG_IGN);	/* shut down signals */
	(void) signal(SIGINT, SIG_IGN);
	(void) signal(SIGQUIT, SIG_IGN);
#ifdef SIGTSTP
	(void) signal(SIGTSTP, SIG_IGN);
#endif /* SIGTSTP */
#endif
	
#ifdef TIOCNOTTY
	fd = open("/dev/tty", 2);	/* disconnect from the tty */
	if (fd >= 0)
	  {
	    (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
	    (void) close(fd);
	  }
#endif /* TIOCNOTTY */
	(void) setpgrp(0, 0);
      } else {
	fprintf(stderr, "%s\n", version+4);
	fprintf(stderr, "%s\n", compiled+4);
	fprintf(stderr, "\nConfiguration info:\n\n");
	fprintf(stderr, "Host:  %s    Queue file: %s\n",
		Host, Qfile);
	fprintf(stderr, "Connection type: ");
	switch (transport) {
	case T_IP_TCP:
	  fprintf(stderr, "TCP/IP\n");
	  break;
	case T_DECNET:
	  fprintf(stderr, "DECNET\n");
	  break;
	case T_FD:
	  fprintf(stderr, "FD\n");
	  break;
	default:
	  fprintf(stderr, "Unknown\n");
	  break;
	}
	if (Report_Stats) fprintf(stderr, "Statistics will %s.\n",
#ifdef USELOG
				  "be reported via UNIX file"
#else
#ifdef LOG_LINK
				  "be reported via 4.3BSD syslog"
#else
				  "not be reported"
#endif
#endif
				  );
	if (ReQueue_Fails)
	  fprintf(stderr, "Failed articles will be requeued.\n");
	else
	  fprintf(stderr, "Failed articles will NOT be requeued.\n");
	
	if (Close_After) {
	  fprintf(stderr, "Connection will be closed after %d articles have been offered\n",
		  Close_After);
	}
	
	if (Log_After)
	  fprintf(stderr, "Will log stats locally after %d articles have been processed.\n",
		  Log_After);
	else
	  fprintf(stderr, "Will log xfer stats after each queue file is finished.\n");
	
	if (Sleep_Time)
	  fprintf(stderr, "Will sleep %d second%s between checks for queue file.\n", Sleep_Time, (Sleep_Time == 1) ? "" : "s");
	
	if (CheckIdleTimeout)
	  fprintf(stderr, "Will close the connection when idle for more than %d seconds.\n",
		  IdleTimeOut);
	if (CheckExitTimeout)
	  fprintf(stderr, "Will exit when idle for more than %d seconds.\n",
		  ExitTimeOut);
	fprintf(stderr, "\n");
	fflush(stderr);
      }

    bzero((caddr_t)&Stats, sizeof(Stats));
#ifndef DONT_WATCH
    {
      register ret;
      int pid;
      
    retry:
      ret = fork();
      
      if (!ret)		/** child startup **/
	{
	  sleep(2);	/* allow some time for debugging msgs to flush */
#endif
	  while(1)
	    {
	      dprintf(stderr, "Looping forever\n");
	      think();
	    }
#ifndef DONT_WATCH
	}
      
      if (ret == -1)	/** fork failed **/
	{
	  sleep(15);	/* back off */
	  goto retry;
	}
      
      dprintf(stderr, "Waiting for child process to die.\n");
      pid = wait(0);
      fprintf(stderr, "child process no longer alive.\n");
      goto retry;
    }
#endif
  }
  exit(EX_OK);
}

void
usage()
{
  (void) fprintf(stderr, "%s: %s\n", Pname, USAGE);
  
  exit(EX_USAGE);
}


think () 
{
  int naptime = NAP_TIME;

  catchsig(interrupted);

  for (;;) 
    {
      if (!sendnews(Host, transport, safecopy(Qfile)))
	{
	  dprintf(stderr, "sendnews returned an error... sleeping for %d seconds...\n", naptime);
	  sleep(naptime);
	  if (naptime < 600)
	    naptime *= 2;
	}
      else
	naptime = NAP_TIME;
      dprintf(stderr, "Back from Sendnews. %s\n",
	      connected ? "Still Connected." : "Not connected");
    }
}

char *
  errmsg(code)
int code;
{
  extern int sys_nerr;
  extern char *sys_errlist[];
  static char ebuf[6+5+1];
  
  if (code > sys_nerr || code < 0) {
    (void) sprintf(ebuf, "Error %d", code);
    return ebuf;
  } else
    return sys_errlist[code];
}

/*
 ** strip leading and trailing spaces
 */
char *
  sp_strip(s)
register char	*s;
{
  register char	*cp;
  
  if (s == NULL)
    return(NULL);
  
  if (*s == '\0')
    return(s);
  
  cp = &s[strlen(s) - 1];
  while(cp > s && isspace(*cp))
    cp--;
  
  *++cp = '\0';	/* zap trailing spaces */
  
  for(cp = s; *cp && isspace(*cp); cp++)
    continue;
  
  return(cp);	/* return pointer to first non-space */
}

/*
 ** convert `s' to lower case
 */
char *
  lcase(s)
register char	*s;
{
  register char	*cp;
  
  if (s == (char *)NULL)
    return(s);
  
  for(cp = s; *cp != '\0'; cp++)
    if (isupper(*cp))
      *cp = tolower(*cp);
  return(s);
}

/*
 ** Get the message-id header field data with a minimum of fuss.
 */
char *
  getmsgid(fp)
FILE *fp;
{
  static	char	buf[BUFSIZ];
  static	char	*msgid = "message-id";
  register char	*cp, *cp2;
  
  while(fgets(buf, sizeof(buf), fp) != (char *)NULL) {
    switch(buf[0]) {
    case '\n':
      return((char *)NULL);	/* EOH, we failed */
    case 'M':
    case 'm':
      if ((cp = index(buf, ':')) == (char *)NULL)
	continue;
      *cp++ = '\0';
      if (strncmp(lcase(buf), msgid, sizeof(*msgid)) == 0) {
	/* dump extraneous trash - umass.bitnet */
	/* hope nobody quotes an '>' in a msgid */
	if ((cp2 = index(cp, '>')) != (char *)NULL)
	  *++cp2 = '\0';
	return(sp_strip(cp));
      }
      break;
    }
  }
  return((char *)NULL);	/* EOF, we failed */
}

#ifdef	notdef	/* nobody obeys the triply damned protocol anyway! */
/*
 ** Special characters, see RFC822, appendix D.
 */
isspecial(c)
     char	c;
{
  char	*specials = "()<>@,;:\\\".[]";
  
  return(index(specials, c) != (char *)NULL ? TRUE : FALSE);
}

/*
 ** Check on the validity of an RFC822 message-id
 **
 ** By The Book, RFC822 Appendix D.
 **	msg-id		= "<" addr-spec ">"
 **	addr-spec	= local-part "@" domain
 **	local-part	= word *("." word)
 **	word		= atom / quoted-string
 **	domain 		= sub-domain *("." sub-domain)
 **	sub-domain	= domain-ref / domain-literal
 **	domain-ref	= atom
 **	domain-literal	= "[" *(dtext / quoted-pair) "]"
 **
 ** NOTE: close reading of the RFC822 spec indicates that a fully
 **	qualified domain name (i.e. one with at least one dot) is
 **	NOT required in the domain part of the addr-spec. However,
 **	I've decided to be an asshole and require them, since we'll 
 **	all die a slow death later on if I don't at this juncture.
 **	To disable, if you disagree with me, see the last return
 **	statement. - Erik E. Fair <fair@ucbarpa.berkeley.edu>
 **	May 30, 1986
 */
msgid_ok(id)
     register char	*id;
{
  register Langle = FALSE;
  register Rangle = FALSE;
  register local_part = FALSE;
  register at = FALSE;
  register dot = FALSE;
  
  /* skip up to the opening angle bracket */
  if (id == (char *)NULL || (id = index(id, '<')) == (char *)NULL)
    return(FALSE);		/* don't waste my time! */
  
  for(; *id != '\0'; id++) {
    switch(*id) {
    case '<':
      if (Langle) return(FALSE);
      Langle = local_part = TRUE;
      break;
    case '>':
      if (Rangle || !Langle || !at) return(FALSE);
      else Rangle = TRUE;
      break;
    case '@':		/* should be a domain spec */
      at = TRUE;
      local_part = FALSE;
      break;
    case '.':
      dot = at;
      break;
    case '\\':
      /*
       ** quoted pair; this disallows NULs, but how
       ** many mailers would die if someone used one?
       */
      if (!local_part || (*++id) == '\0') return(FALSE);
      break;
    case '"':
      /*
       ** quoted string
       */
      if (!local_part) return(FALSE);
      do {
	switch(*++id) {
	case '\\':
	  if ((*++id) == '\0') return(FALSE);
	  break;
	case '\r':
	  return(FALSE);
	}
      } while(*id != '\0' && *id != '"');
      break;
    case '[':
      /*
       ** domain literal
       */
      if (local_part) return(FALSE);
      do {
	switch(*++id) {
	case '\\':
	  if ((*++id) == '\0') return(FALSE);
	  break;
	case '\r':
	  return(FALSE);
	}
      } while(*id != '\0' && *id != ']');
      break;
    default:
      if (!isascii(*id) || iscntrl(*id) || isspace(*id) || isspecial(*id))
	return(FALSE);	/* quit immediately */
      break;
    }
  }
  return(at && dot && Langle && Rangle);
}
#else notdef

/*
 ** Simpleton's check for message ID syntax.
 ** A concession to the realities of the ARPA Internet.
 */
msgid_ok(s)
     register char *s;
{
  register char	c;
  register in_msgid = FALSE;
  
  if (s == (char *)NULL)
    return(FALSE);
  
  while((c = *s++) != '\0') {
    if (!isascii(c) || iscntrl(c) || isspace(c))
      return(FALSE);
    switch(c) {
    case '<':
      in_msgid = TRUE;
      break;
    case '>':
      return(in_msgid);
    }
  }
  return(FALSE);
}
#endif	notdef

/*
 ** Read the header of a netnews article, snatch the message-id therefrom,
 ** and ask the remote if they have that one already.
 */
ihave(fp, mesgid)
     FILE	*fp;
     char	*mesgid;
     
{
  register int	code;
  register char	*id;
  char	buf[BUFSIZ];
  
  dprintf(stderr,"%s: entering ihave\n", Pname);fflush(stderr);
  if (strlen(mesgid) > 0) {
    id = mesgid;
  } else {
    if ((id = getmsgid(fp)) == (char *)NULL || *id == '\0') {
      /*
       ** something botched locally with the article
       ** so we don't send it, but we don't break off
       ** communications with the remote either.
       */
      sprintf(buf, "%s: message-id missing!", Article);
      log(L_DEBUG, buf);
      return(ERR_GOTIT);
    }
    strcpy(mesgid,id);
  }

  if (!msgid_ok(id)) {
    sprintf(buf, "%s: message-id syntax error: %s", Article, id);
    log(L_DEBUG, buf);
    return(ERR_GOTIT);
  }

  sprintf(buf, "IHAVE %s", id);
  Stats.offered++;
  Stats.since_close++;

  switch(code = converse(buf, sizeof(buf))) {
  case CONT_XFER:
    Stats.accepted++;
    rewind(fp);
    return(code);
  case ERR_GOTIT:
    Stats.rejected++;
    return(code);
  default:
    strcpy(mesgid,id);
    return(code);
  }
}

/*
 ** Given that fp points to an open file containing filenames,  
 ** open and return a file pointer to the next filename in the file.
 ** Don't you love indirection?
 **
 ** Returns a valid FILE pointer or NULL if end of file.
 */
FILE *
  getfp(fp, filename, fnlen, mesgid, host)
register FILE	*fp;
char	*filename;
register int	fnlen;
char	*mesgid;
char *host;
{
  register FILE	*newfp = (FILE *)NULL;
  register char	*cp;
  char	*mode = "r";
  char	buffer[255];
  char shortfn[255];
  char *p;
  
  if (Stats.offered >= Log_After && Report_Stats) {
    logstats();
    resetstats = FALSE;
    dprintf(stderr, "resetting timers\n");
    Tbegin = Tend;
    ouser = user;
    osys = sys;
    Stats.offered = Stats.accepted = Stats.rejected = Stats.failed = 0L;
  }

  if ((Stats.since_close >= Close_After) && connected) {
    dprintf(stderr,
	    "%s: %d articles sent; closing link for remote stats.\n",
	    Pname, Stats.since_close); fflush(stderr);
    goodbye(WAIT);
    connected = 0;
    Stats.since_close = 0L;
  }

  if( In_Log_File ) {

    do {

      do {

	strcpy(old_logline, logline);

	(void)my_fgets(logline, MAXLOGLENGTH, fp);

	for (p = logline; *p && *p != '<'; p++);

      } while( !*p || (*(p-2) == '-') || (strnindex(p, sysname, 0) == -1));

      for (i = 0; *p && *p != '>'; mesgid[i++] = *p, p++);

      mesgid[i++] = *p;   /* go ahead and add the ">" */
      mesgid[i] = '\0';

      for (p += 2, i = 0; *p && (*p !=' '); shortfn[i++] = *p, p++);

      shortfn[i] = '\0';

      strcpy(filename, SPOOL_DIR);
      strcat(filename, shortfn);

      dprintf(stderr, "fn=%s, id=%s\n", filename, mesgid);

      if ((newfp = fopen(filename, mode)) == (FILE *)NULL) {
	/*
	 ** The only permissible error is `file non-existant'
	 ** anything else indicates something is seriously
	 ** wrong, and we should go away to let the shell
	 ** script clean up.
	 */
	if (errno != ENOENT) {
	  char	buf[BUFSIZ];

	  sprintf(buf, E_fopen, filename, mode, errmsg(errno));
	  /*
	    log(L_WARNING, buf);
	    */
	  goodbye(DONT_WAIT);
	  exit(EX_OSERR);
	}
      }

    } while (newfp == (FILE *)NULL);

  } else while(newfp == (FILE *)NULL) {
    if (fgets(filename, fnlen, fp) == (char *)NULL) {
      dprintf(stderr,"%s: getfp: at EOF\n", Pname);
/*      return((FILE *)NULL);		/* EOF, tell caller */
      break;
    }
    filename[fnlen - 1] = '\0';	/* make sure */
    
    /* if fgets() ever forgets the '\n', we're fucked */
    if (*(cp = &filename[strlen(filename) - 1]) == '\n')
      *cp = '\0';
    
    if (filename[0] == '\0')
      continue;
    
     /**
      ** I don't care if it's separated by a space, or a tab, but I want
      **   the message-Id if it's in there.
      **/

    if (index(filename, ' ') != NULL) {		  /** Ala C-news **/
      sscanf(filename, "%s %s", buffer, mesgid);
      strcpy(filename, buffer);
      if (!cnews) {
        ++cnews;
	dprintf(stderr, "Hmmm... Config error, configed for B-news, but\n");
	dprintf(stderr, "   running C-news.\n");
      }
    } else if (index(filename, '\t') != NULL) {
      sscanf(filename, "%s\t%s", buffer, mesgid); /** Ala B-news **/
      strcpy(filename, buffer);
      if (cnews) {
        cnews = 0;
	dprintf(stderr, "Hmmm... Config error, configed for C-news, but\n");
	dprintf(stderr, "   running B-news.\n");
      }
    } else strcpy(mesgid, "");

    if ((newfp = fopen(filename, mode)) == (FILE *)NULL) {
      /*
       ** The only permissible error is `file non-existant'
       ** anything else indicates something is seriously
       ** wrong, and we should go away to let the shell
       ** script clean up.
       */
      if (errno != ENOENT) {
	char	buf[BUFSIZ];
	
	sprintf(buf, E_fopen, filename, mode, errmsg(errno));
	/*
	  log(L_WARNING, buf);
	    */
	goodbye(DONT_WAIT);
	exit(EX_OSERR);
      }
    }
  }

  if (!connected)
    if (hello(host, transport) == FAIL) {
      sleep(Sleep_Time);
      return(NULL);
    } else
      connected = TRUE;

  return(newfp);
}