/*
**++
**  FACILITY:
**	NNTP_XMIT
**
**  ABSTRACT:
**	Use the NNTP IHAVE command to manage NEWS distribution
**
**  AUTHOR:
**	Geoff Huston
**
**  COPYRIGHT:
**	Copyright © 1988,1989,1990,1991,1992
**
**  CONTRIBUTIONS:
**	UCX Support botched in by D.J.Young (CNBR10@UK.AC.STRATH.VAXA)
**	16th August 1990
**	Numerous bug fixes & improvements: Bill Glass glass@vixvax.mgi.com
**	Report on xfer rates: Earle Ake ake@dayton.saic.com
**
**  VERSION:
**	V6.0-4	25-May-1991	volz@process.com
**	  - Added TCPware support
**	V6.1	 8-Feb-1992	Mark Wood - imhw400@indyvax.iupui.edu
**	  - Bug fix to command line parse
**	V6.1	 9-Feb-1992	David E. Bellamy - bellamy@commerce.uq.oz.au
**	  - Bug fix to UCX socket port assignments
**      V6.1     21-Feb-1992	sloane@kuhub.cc.ukans.edu
**        - Fix memory leak
**	V6.1b7	 8-Aug-1993	Charles Bailey  bailey@genetics.upenn.edu
**	  - add mem_fail() routine as part of updated memory management
**	V6.1b8	 14-Dec-1993	Mark Pizzolato mark@infocomm.com
**	  - included proper usage/check of the news lock mechanisms.
**	  - Accomodate incorrect specification of the type of the input ID
**	    file.  The behaviour is now changed, and the structure of the file
**	    determines the "default" processing mode.
**	  - Upon successful processing of an indexed article ID file, if the 
**	    resulting file is now empty, the indexed article ID file is
**	    deleted.  This effectively avoids the potentially messy RMS
**	    maintenance operations that would be needed on this indexed file
**	    which merely gets records inserted and deleted all day long, and 
**	    ends up never reclaiming the index buckets that will end up
**	    floating around.
**        - Cleanup of use of sysprv context to cover all cases where the id
**	    file is opened, closed, renamed, or deleted.
**	V6.1b8	 26-Jan-1994 	Mark Martinec  mark.martinec@ijs.si
**        - check status returns from calls to RMS
**	V6.1b9	17-Aug-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to make it compile under gcc 2.6.0 with full
**	    warnings reporting turned on - with no or very few harmless warnings
**	V6.1b9	17-Sep-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to preserve the read-only nature of string literals
**	    (strategically placed 'const' attribute to string parameters)
**      V6.1b10 30-Mar-1995     Bob Sloane      sloane@kuhub.cc.ukans.edu
**        - fixed problem where message id needs to be truncated to IDLEN-1
**          characters. Also removed changing message id to all lower case,
**          since lower case ids haven't been supported for a long time.
**	  - fix the year 2000 problem when generating log
**	V6.1b11	 3-Jun-1997	Mark Martinec   mark.martinec@ijs.si
**	  - replace routines nntp_read and nntp_read_line with newer
**	    from module NEWSREMCLIENT.C
**--
*/

#ifdef vaxc
#module NNTP_XMIT "V6.2"
#endif

#define _NNTP_XMIT_C
#define _NNTP_MAIN_PROGRAM

#include "nntpinclude.h"

#if NOGLOBALREF
extern void nntpxmitcmd();
#else
globalref int nntpxmitcmd;
#endif

#define LJP		1
#define LOG_TABLE	"LNM$PROCESS"

#ifdef TCPWARE
#define  TCPWARE	1
#define __TYPES_H_DEFINED
#endif

#if defined(TWG) || defined(MULTINET) || defined(TCPWARE)
struct hostent *dest_host;
struct sockaddr_in data_socket = {0};
#endif

#ifdef UCX
struct mysockaddr {
  short inet_family;
  short inet_port;
  int inet_adrs;
  char bklb[8];
  };

struct itlist { int lgth; struct mysockaddr *hst; };

static short sck_parm[2];
static struct mysockaddr local_host, remote_host;
struct itlist lhst_adrs, rhst_adrs;
#endif
					/* compile time constants */
#define NNTP_PORT	(119)

#define CLIENT_TIMER	250
#define RESP_TIMER	30
#define NNTP_MAX_OFFER	5000

					/* program constants */
#define X_BUF_SIZE	2048
#define NNTP_STRLEN	1024
#define OUT_BUF_TERM	"\n"

#define ERROR		0
#define MAX_OFFERED	1
#define MANAGER_STOP	2
#define NOMEM	3

					/* premature exit - clean up as far as possible */
#define GO_DIE(v) {					\
		    if (fpw) {				\
		      fprintf(fpw,"%s\n",inpline);	\
		      ++lines_written;			\
		      }					\
		    premature_break(v);\
		  }

					/* program variables */

char net_open_chan[128] = {""},
     ibuf[X_BUF_SIZE],
     obuf[512],
     xbuf[X_BUF_SIZE + 1],
     *rptr,
     rem_node[128],
     datum[32],
     logfile[256],
     dbgfile[256],
     idfile[256],
     inpline[MAXIDLEN],
     tmpf[256],
     taskstr[132];

unsigned short net_chan = 0,
	       trm;

int net_read_status = 0,
    hiber_state = 0,
    cmd_code,
    net_proto = 0,
    debugging = 0,
    decnetstream = 0,
    status,
    failed = 0,
    expired = 0,
    accepted = 0,
    rejected = 0,
    offered = 0,
    logging = 0,
    lines_written = 0,
    idx = -1,
    ibuf_l = 0;  /* strlen(ibuf) */

long bytes_sent = 0,
     total_bytes_sent = 0;
int  total_elapsed_time = 0;

FILE *fpd, *fpr, *fpw, *fpi;

struct iosb {
	unsigned short iostatus;
	unsigned short iosize;
	int netinfo;
	} read_iosb,
	  write_iosb;


/* Return pointer to a statically allocated string
   containing VMS error message associated with the given status.
   Stat1 and stat2 should belong to the facility SYSTEM.
   Usually stat1 is status returned by the system call (e.g. $QIOW)
   as the function value and stat2 is the IOSB status.
 */
char *getsyserrmsg(int stat1, int stat2)
{
  static char msg[128];
  static struct dsc$descriptor_s msg_dsc =
    { 128-1,DSC$K_DTYPE_T,DSC$K_CLASS_S,msg };
  unsigned short msg_l;
  int status;

  _c$cks(sys$getmsg((stat1&1)?stat2:stat1, &msg_l, &msg_dsc, 3, 0));
  msg[msg_l] = '\0';
  return msg;
}


void closefiles()
{
  return;
}

/*
 *  idx_file routines - RMS indexed identifier file support
 */

struct FAB  idxfab;
struct RAB  idxrab;
struct XABKEY  idxkey_1;
struct XABPRO  idxpro_1;

struct idx  {
  time_t t;
  char idx_id[MAXIDLEN+1];
}	newsidx;

/*
 *  open_id_file
 *
 *  Open an identifier file - validate that it is an indexed file
 */

int open_id_file(idx_name, idx)
  char  *idx_name; int *idx;
{
  idxfab = cc$rms_fab;
  idxfab.fab$b_fac = FAB$M_GET | FAB$M_DEL;
  idxfab.fab$l_fna = idx_name;
  idxfab.fab$b_fns = strlen(idxfab.fab$l_fna);
  idxfab.fab$l_dna = (char *) "NEWS_MANAGER:IHAVE.IDX";
  idxfab.fab$b_dns = strlen(idxfab.fab$l_dna);
  idxfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;

  sysprv();
  if (!sys_open_nofnf(&idxfab)) return(nosysprv(),0);

  if (idxfab.fab$b_org == FAB$C_SEQ) {
    sys_close(&idxfab);
    nosysprv();
    if (*idx == 1) {
      printf("NNTP_XMIT - Requested Processing of Sequential ID file in index mode: %s\n",
	      idx_name);
      printf("NNTP_XMIT - Processing will be done in Sequential mode.\n");
    }
    *idx = 0;
    return(0);
    }
  if (*idx == 0) {
    sys_close(&idxfab);
    nosysprv();
    printf("NNTP_XMIT Warning - Processing Indexed ID file in sequential mode: %s\n",
	    idx_name);
    printf("NNTP_XMIT Warning - If the contents of this file are only partially transmitted,\n");
    printf("NNTP_XMIT Warning - the remnants will be left as a sequential file.\n");
    printf("NNTP_XMIT Warning - This may interfere with subsequent attempts at recording\n");
    printf("NNTP_XMIT Warning - article IDs.\n");
    return(0);
  }
  *idx = 1;
  idxrab = cc$rms_rab;
  idxrab.rab$b_krf = 0;
  idxrab.rab$l_fab = &idxfab;
  idxrab.rab$l_ubf = (char *) &newsidx;
  idxrab.rab$w_usz = sizeof newsidx;
  idxrab.rab$l_rbf = (char *) &newsidx;
  idxrab.rab$w_rsz = sizeof newsidx;

  if (!sys_connect(&idxrab)) {
    sys_close(&idxfab);
    nosysprv();
    return(0);
    }
  nosysprv();
  idxrab.rab$b_rac = RAB$C_SEQ;
  idxrab.rab$b_krf = 0;
  sys_rewind(&idxrab);
  return(1);
}

/*
 * close_id_file
 *
 * close the index file - use sysprv - cause that was used to open it
 */

void close_id_file(void)
{
  sysprv();
  sys_close(&idxfab);
  nosysprv();
}

/*
 * read_id
 *
 * Get the next entry from the ID file
 */

int read_id(id)
  char *id;
{
  if (sys_get_nornf(&idxrab)) {
    strcpy(id,newsidx.idx_id);
    return(1);
    }
  return(*id = '\0');
}

/*
 * delete_id
 *
 * Delete the entry from the ID file
 */

void delete_id(void)
{
  sys_delete(&idxrab);
}

/*
 * dat
 *
 * Convert the time into a timestring
 */

void dat(void)
{
  struct tm *stm;
  time_t cur_time;
  char *p;

  time(&cur_time);
  p = ctime(&cur_time);
  p += 4;
  stm = localtime(&cur_time);
  sprintf(datum,"%d %.3s %d %02d:%02d:%02d ",
	stm->tm_mday, p, (stm->tm_year)+1900,
        stm->tm_hour, stm->tm_min, stm->tm_sec);
}

/*
 * elapsed_time
 *
 * get the elapsed time in hundredths (0.01) of seconds
 *
 * usage:
 *
 *	elasped_time(1); will perform a 'start-the-clock'
 *
 *	elapsed_time(0); returns the number of centi-seconds since the last
 *		'start-the-clock' command
 */

/*#include timeb   This is included from time.h in NEWSINCLUDE.H*/

static int elapsed_time(update)
  int update;
{
  int result;
  static struct timeb rbefore, rafter;

  ftime(&rafter);
  result = 100 * (rafter.time - rbefore.time) +
	((int) rafter.millitm - (int) rbefore.millitm) / 10;
  if (update) rbefore = rafter;
  return(result);
}

/*
 *  nosysprv
 *
 *  Turn off installed sysprv - BUT if user already had sysprv from AUTHORIZE
 *  then leave it on (as there is no change in user functionality)
 */

void nosysprv()
{
  unsigned int authprivs[2], msysprv[2] ;
  int item = JPI$_PROCPRIV;

  msysprv[0] = PRV$M_SYSPRV;
  msysprv[1] = 0;
  c$cks(lib$getjpi(&item,0,0,&authprivs,0,0));
  if (!(authprivs[0] & PRV$M_SYSPRV)) c$cks(sys$setprv(0,msysprv,0,0));
}

/*
 *  sysprv
 *
 *  Turn sysprv on as a temp priv - assumes image was installed with SYSPRV
 */

void sysprv()
{
  unsigned int msysprv[2];

  msysprv[0] = PRV$M_SYSPRV;
  msysprv[1] = 0;
  c$cks(sys$setprv(1,msysprv,0,0));
}

/*
 *  nntp_write
 *
 *  Synchronous write of a string to the net channel
 */

int nntp_write(b)
  char *b;
{
  static char obuf[NNTP_STRLEN+1];
  struct iosb write_iosb;
  int sts;

  strcpy(obuf,b);
  strcat(obuf,"\r\n");
  if ((net_chan == 0) && (debugging)) {
    fprintf(fpd,"Attempting Network Write when network is not opened\n");
    fprintf(fpd,"Untransmitted data: %s\n", b);
    }
  sts = sys$qiow(0,net_chan,IO$_WRITEVBLK,&write_iosb,0,0,obuf,strlen(obuf),0,(net_proto == CMUTCP),0,0);
  if (!(sts & 1) || (!(write_iosb.iostatus & 1))) {
    close_net();
    printf("NNTP_XMIT Lost connection to Server: %s\n",net_open_chan);
    net_read_status = 0;
    return(0);
    }
  return(1);
}

/*
 *  nntp_read
 *
 *  Timed read of the net channel
 */

jmp_buf nntp_env;

void cancel_nntp_read(sig)
  int sig;
{
  close_net();
  printf("NNTP_XMIT cancel_nntp_read: Lost connection to server: %s\n",net_open_chan);
  longjmp(nntp_env,1);
}

static int nntp_read(b,b_m,b_len,timer)
  char *b;
  int b_m;    /* sizeof(b) */
  int *b_len; /* returns strlen(b) */
  int timer;
{
  struct iosb r_iosb;
  int sts;
  int b_l; /* strlen(b) */

  if (b_len) *b_len = 0;
  *b = '\0'; b_l = 0;

#ifdef UCXSOCKETS
  if ((r_iosb.iosize = recv(socket_1,b,b_m-1,0)) <= 0) {
    printf("NNTP_XMIT nntp_read: recv error - %s\n",strerror(errno));
    close_net();
    return(0);
    }
  else {
    alarm(0);
    sts = 1; r_iosb.iostatus = 1;
    }
#else
  if (setjmp(nntp_env)) return(0);
  signal(SIGALRM,cancel_nntp_read);
  alarm(timer);
  sts = sys$qiow(0,net_chan,
#if defined(DECNETSTREAM)
                 IO$_READVBLK | IO$M_MULTIPLE,
#elif defined(EXOS)
                 EX__READ,
#else
                 IO$_READVBLK,
#endif
                 &r_iosb, 0,0, b,b_m-1, 0,0,0,0);
  alarm(0);
  if (!(sts & 1) || !(r_iosb.iostatus & 1)) {
    printf("NNTP_XMIT nntp_read: %s\n", getsyserrmsg(sts, r_iosb.iostatus) );
    close_net();
    return(0);
    }
  if (!r_iosb.iosize) {
    printf("NNTP_XMIT nntp_read: null response, connection dropped\n");
    close_net();
    return(0);
    }
#endif
  b_l = r_iosb.iosize; b[b_l] = '\0';

  { char *from, *cp;
    int from_l, ll;

 /* replace all <CR><LF> pairs with <LF> */
    for (from=b, from_l=b_l, b_l=0;  from_l>0; ) {
      cp = memchr(from,'\n',from_l);  ll = !cp ? from_l : cp-from+1;
      if (cp && ll>1 && *(cp-1)=='\r') { *(cp-1) = '\n'; ll--; }
      if (b+b_l < from) memmove(b+b_l,from,ll);
      b_l += ll;
      if (!cp) { from_l = 0; from = NULL; }
      else { from_l -= (cp-from)+1; from = cp+1; }
      }
 /* cheating a bit: if the last char in the buffer is <CR> discard it
    in the hope that <LF> will follow at the start of the next read   */
    if (b_l>0 && b[b_l-1]=='\r') b_l--;

    b[b_l] = '\0';
    if (b_len) *b_len = b_l;
    return(1);
  }
}

/*
 * nntp_read_line
 *
 * Read one line (to '\n') from the net channel
 */

static char lbuf[2048] = {""};  /* works with any size, bigger for efficiency */
static int  lbuf_l = 0;     /* actual length of string in lbuf */
static int  lbuf_ind = 0;   /* index of the next character in lbuf to be read */

static int nntp_read_line(b,b_m,b_len,timer)
  char *b;
  int b_m;    /* sizeof(b) */
  int *b_len; /* returns strlen(b) */
  int timer;
{
  char *cp;
  int b_l,ll;

  *b = '\0';
  for (b_l = 0;  b_l < b_m-1; ) {
    if (lbuf_l-lbuf_ind <= 0) {
      lbuf_ind = 0;
      if (!nntp_read(lbuf,sizeof(lbuf), &lbuf_l, timer)) {
        if (b_len) *b_len = b_l;
        return(0);
        }
      }
    cp = memchr(&lbuf[lbuf_ind], '\n',
                ll = min(lbuf_l-lbuf_ind, b_m-b_l-1) );
    if (cp) ll = cp - &lbuf[lbuf_ind] + 1;   /* take only up to and incl '\n' */
    memcpy(&b[b_l],&lbuf[lbuf_ind],ll); b_l += ll; b[b_l] = '\0';
    lbuf_ind += ll;
    if (cp) {                            /* return if we have a complete line */
      if (b_len) *b_len = b_l;
      return(1);
      }
    }
  if (b_len) *b_len = b_l;
  return(1);/* return full buffer even if it does not contain a complete line */
}

/*
 *  wait_net_response
 *
 *  Wait for a response from remote system - cmd indicates that all text
 *  should be skipped until NNTP command response is obtained
 */

int wait_net_response(secs,cmd)	int secs,cmd;
{
  if (nntp_read_line(ibuf,sizeof(ibuf),&ibuf_l,secs)) {
    if (!cmd) return(1);
    if (sscanf(ibuf,"%d",&cmd_code) == 1) {
      if ((cmd_code == 400) || (cmd_code == 205)) {
	close_net();
	return(0);
	}
      else return(cmd_code);
      }
    else return(1);		/* glass@vixvax.mgi.com 6/3/91 */
    }
  return(0);			/* glass@vixvax.mgi.com 6/3/91 */
}

#ifdef UCX
static unsigned int get_local_adrs(void)
{
  static unsigned int localhost = 0;

  if (!localhost) {
    const char *gotenv;
    short nam_chan, retlen;
    int comm = INETACP$C_TRANS * 256 + INETACP_FUNC$C_GETHOSTBYNAME;
    struct dsc$descriptor command = {4,DSC$K_CLASS_S, DSC$K_DTYPE_T,(char *)&comm};
    struct dsc$descriptor host_ad = {4,DSC$K_CLASS_S, DSC$K_DTYPE_T,(char *)&localhost};
    struct iosb nam_iosb;

    if (!(gotenv = news_getenv("UCX$INET_HOST",1))) gotenv = "localhost";
    if (sys$assign(c$dsc("BG0:"),&nam_chan,0,0) & 1) {
      if (!(   (sys$qiow(0,nam_chan,IO$_ACPCONTROL,&nam_iosb,0,0,&command,c$dsc(gotenv),&retlen,&host_ad,0,0) & 1)
            && (nam_iosb.iostatus & 1)))
        localhost = (1 << 24) + 127;
      sys$dassgn(nam_chan);
      }
    }
  return(localhost);
}

static unsigned int get_host_adrs(host_name)
  char *host_name;
{
  unsigned int hostadrs = 0;
  short nam_chan, retlen;
  int comm = INETACP$C_TRANS * 256 + INETACP_FUNC$C_GETHOSTBYNAME;
  struct dsc$descriptor command = {4,DSC$K_CLASS_S, DSC$K_DTYPE_T,(char *)&comm};
  struct dsc$descriptor host_ad = {4,DSC$K_CLASS_S, DSC$K_DTYPE_T,(char *)&hostadrs};
  struct iosb nam_iosb;

  if (sys$assign(c$dsc("BG0:"),&nam_chan,0,0) & 1) {
    if (!(   (sys$qiow(0,nam_chan,IO$_ACPCONTROL,&nam_iosb,0,0,&command,c$dsc(host_name),&retlen,&host_ad,0,0) & 1)
          && (nam_iosb.iostatus & 1))) hostadrs = 0;
      sys$dassgn(nam_chan);
    }
  else printf("Could not open BG0:\n");
  return(hostadrs);
}
#endif

/*
 *  open_net
 *
 *  Open network link
 */

int open_net(node,proto)
  const char *node; int proto;
{
  char netobj[128];

  strcpy(netobj,node);
  if (proto == CMUTCP) {
    if (sys$assign(c$dsc("IP:"),&net_chan,0,0) & 1) {
      if (   (sys$qiow(0,net_chan,IO$_CREATE,&read_iosb,0,0,netobj,119,0,1,0,300) & 1)
	  && (read_iosb.iostatus & 1))
	return(1);
      sys$dassgn(net_chan);
      }
    return(0);
    }

#ifdef UCX
  else if (proto == UCXTCP) {
    if (sys$assign(c$dsc("BG:"),&net_chan,0,0) & 1) {
      sck_parm[0] = INET$C_TCP;
      sck_parm[1] = INET_PROTYP$C_STREAM;
      local_host.inet_family = INET$C_AF_INET;
      local_host.inet_port = 0;  /* dynamic port assignment */
      local_host.inet_adrs = get_local_adrs();
      lhst_adrs.lgth = sizeof local_host;
      lhst_adrs.hst = &local_host;
      if (   (sys$qiow(0,net_chan,IO$_SETMODE,&read_iosb,0,0,&sck_parm,0,&lhst_adrs,0,0,0) & 1)
          && (read_iosb.iostatus & 1)) {
        remote_host.inet_family = INET$C_AF_INET;
        remote_host.inet_port = htons(NNTP_PORT);
        if (!(remote_host.inet_adrs = get_host_adrs(netobj))) {
          char *c, *cd, resnum[4];
          unsigned int indx = 0, *i;

          c = netobj;
          while (c && (indx <= 3)) {
            if ( (cd = strchr(c,'.')) ) *cd = '\0';
            resnum[indx++] = atoi(c);
            if (cd) {
              *cd++ = '.';
              c = cd;
              }
            else c = 0;
            }
          if (c || indx != 4) {
            sys$qiow(0,net_chan, IO$_DEACCESS | IO$M_SHUTDOWN,read_iosb,0,0,0,0,0,INET$C_DSC_ALL,0,0);
            sys$dassgn(net_chan);
            return(0);
            }
          i = (unsigned int *) &resnum[0];
          remote_host.inet_adrs = *i;
          }
        rhst_adrs.lgth = sizeof remote_host;
        rhst_adrs.hst = &remote_host;
        printf("Calling %08x\n",remote_host.inet_adrs);
        if (   (sys$qiow(0,net_chan,IO$_ACCESS,&read_iosb,0,0,0,0,&rhst_adrs,0,0,0) & 1)
            && (read_iosb.iostatus & 1)) return(1);
        printf("iosb.iostatus=%08x\n",read_iosb.iostatus);
        printf("Failed open_net\n");
        sys$qiow(0,net_chan, IO$_DEACCESS | IO$M_SHUTDOWN,read_iosb,0,0,0,0,0,INET$C_DSC_ALL,0,0);
        }
      sys$dassgn(net_chan);
      }
    return(0);
    }
#endif

#if defined(TWG) || defined(MULTINET) || defined(TCPWARE)
  else if ((proto == WINTCP) || (proto == MULTINETTCP) || (proto == TCPWARETCP)) {
    if (   ((proto == WINTCP) && (sys$assign(c$dsc("INET:"),&net_chan,0,0) & 1))
	|| ((proto == MULTINETTCP) && (sys$assign(c$dsc("INET0:"),&net_chan,0,0) & 1))
	|| ((proto == TCPWARETCP) && (sys$assign(c$dsc("INET0:"),&net_chan,0,0) & 1))) {
      if (   (sys$qiow(0,net_chan,IO$_SOCKET,&read_iosb,0,0,
		    AF_INET,SOCK_STREAM,0,0,0,0) & 1)
	  && (read_iosb.iostatus & 1)) {

	if (!(dest_host = gethostbyname(netobj))) {
	  int h[4], i;

	  if (sscanf(netobj,"%d.%d.%d.%d",&h[0],&h[1],&h[2],&h[3]) != 4) {
	    if (debugging) fprintf(fpd,"open_net: gethostbyname returned null\n");
	    sys$dassgn(net_chan);
	    return(0);
	    }
	  for (i = 0 ; i < 4 ; ++i)
	    if (h[i] < 0 || h[i] > 255) {
	      if (debugging) fprintf(fpd,"Incorrect format of internet address\n");
	      return(0);
	      }
	  data_socket.sin_addr.s_addr = (h[3] << 24) + (h[2]  << 16) + (h[1] << 8) + h[0];
	  }
	else data_socket.sin_addr.s_addr = *((u_long *)(dest_host->h_addr));
	data_socket.sin_family = AF_INET;
	data_socket.sin_port = htons(NNTP_PORT);
	if (   (sys$qiow(0,net_chan,IO$_CONNECT,&read_iosb,0,0,
			 &data_socket,sizeof(data_socket),0,0,0,0) & 1)
	    && (read_iosb.iostatus & 1))
	  return(1);
	}
      }
    if (debugging) fprintf(fpd,"open_net: io$_connect or io$_Socket error\n");
    if (debugging)
      fprintf(fpd,"open_net: read_iosb.iostatus %x \n",read_iosb.iostatus);
    sys$dassgn(net_chan);
    return(0);
    }
#endif

  else {
    sprintf(netobj,"%s::\"%s\"",node,taskstr);
    return(sys$assign(c$dsc(netobj),&net_chan,0,0) & 1);
    }
  return(0);  /* should not happen */
}

/*
 *  close_net
 *
 *  Close network link
 */

void close_net()
{
  if (net_proto) {
    sys$cancel(net_chan);
    sys$qiow(0,net_chan,IO$_DELETE,0,0,0,0,0,0,0,0,0);
    }
  sys$dassgn(net_chan);
  net_chan = 0;
}

/*
 *  toggle_link
 *
 *  Write a non-command to the net chan and wait for the command-not-recognised
 *  response from the server.
 */

int toggle_link()
{
  if (!nntp_write("STAT") || !(wait_net_response(RESP_TIMER,1))) {
    if (*net_open_chan) {
      close_net();
      printf("Lost connection to NEWS SERVER ([%c] %s)",
		    (net_proto ? 'T' : 'D'),net_open_chan);
      *net_open_chan = 0;
      }
    return(0);
    }
  return(1);
}

/*
 *  open_rem_chan
 *
 *  Open channel to remote NNTP server on host name node, using protocol
 *  proto_num (6 == TCPWARETCP, 3 == MULTINETTCP, 2 == WINTCP,
 *  1 == CMUTCP, 0 = DECNET)
 */

int open_rem_chan(node,proto_num)
  const char *node;
  int proto_num;
{
  int reply;

  if (*net_open_chan) {
    if ((proto_num != net_proto) || (news_strncasecmp(node,net_open_chan,0))) {
      close_net();
      net_read_status = 0;
      *net_open_chan = 0;
      }
    }

  if (!*net_open_chan) {
    printf("\tConnecting to NEWS SERVER on node %s ...\n",node);
    if (debugging) fprintf(fpd,"\tConnecting to NEWS SERVER on node %s ...\n",node);
    strcpy(net_open_chan,node);
    lower_case(net_open_chan);
    if (!open_net(net_open_chan,proto_num)) {
      net_read_status = 0;
      *net_open_chan = 0;
      if (debugging) fprintf(fpd,"Net: Could not open network channel\n");
      return(0);
      }
    else net_proto = proto_num;
    if (!(reply = wait_net_response(CLIENT_TIMER,1))) {
      close_net();
      net_read_status = 0;
      *net_open_chan = 0;
      if (debugging) fprintf(fpd,"Net: No reply received in time\n");
      return(0);
      }
    if (reply!=200) {
      printf("Postings not allowed to NEWS SERVER: %s",net_open_chan);
      if (reply==201) {
	printf(",only transfer allowed.\n");
	return(1);
	}
      printf("\n");
      return(0);
      }
    }
  return(1);
}

/*
 * premature_break
 *
 * Cleanup up following error or forced stop of the process.
 */

void premature_break(type)
  int type;
{
  char inpstr[132],
       type_text[4][43]={ {"Error: Connection lost"},
			  {"Information: Max. items offered"},
			  {"Information: Exit forced by system manager"},
			  {"Error: Out of memory"} };
  FILE *fpl;

  if (logging){
    dat();
    fpl = fopen(logfile,"a+");
    if(offered != 0) {
      fprintf(fpl,"%s %s: nntpxmit: %s %d offered, %d accepted, %d rejected, %d failed, %d expired",
		datum, news_node, rem_node, offered, accepted, rejected, failed, expired);
		/**	Report on xfer rates: Earle Ake ake@dayton.saic.com */
      if (total_elapsed_time > 0)
        fprintf(fpl,", %ld bytes, %.2f secs (%ld bytes/sec)",
         total_bytes_sent,(float) total_elapsed_time/100.0,
	 (long)(((float)total_bytes_sent*100.0)/(float)total_elapsed_time));
      fprintf(fpl,"\n");
      }
    fprintf(fpl,"%s %s: nntpxmit: %s, %s, transfer aborted.\n",datum, news_node, rem_node,type_text[type]);
    fprintf(fpl,"\n");
    fclose(fpl);
    }
  if (debugging) fprintf(fpd,"%s %s: nntpxmit: %s, %s, transfer aborted.\n",datum, news_node, rem_node,type_text[type]);
  if (!idx) {
    if (lines_written && fpw) {
      while (fgets(inpstr,132,fpr)) fprintf(fpw,"%s",inpstr);
      sysprv();
      fclose(fpw);
      delete(idfile);
      fclose(fpr);
      rename(tmpf,idfile);
      nosysprv();
      }
    else {
      sysprv();
      fclose(fpw);
      delete(tmpf);
      nosysprv();
      }
    }
  if (type == MAX_OFFERED) {
    c$cks(lib$set_logical(c$dsc("NNTP_MORE_TO_OFFER"),c$dsc("YES"),0,0,0));
    }
  if (debugging) fclose(fpd);
  if (idx) close_id_file();
  else {
    sysprv();
    fclose(fpr);
    nosysprv();
    }
  fclose(fpi);
  exit(1);
}

/*
 *  util_dir
 *
 *  Convert a newsgroup name to a VMS directory string.
 *
 *  Allow group name size to be at most GRPNAMELEN_F characters,
 *  thus accepting also the not necessarily '\0'-terminated newsgrp.grp_name
 *  as a parameter.
 */

char dir_result[GRPNAMELEN_F + GRPNAMELEN_F + 1];

char *util_dir(input)
    const char *input;
{
    char *p = dir_result;
    const char *in;
    int j;

    for (in=input, j=0;  j<GRPNAMELEN_F && *in;  in++, j++) {
      if (isalnum(*in) || (*in == '-') || (*in == '.'))
        *p++ = *in;
      else {
        *p++ = '_';
        if     (*in == '_') *p++ = '_';
        else if (*in < '0') *p++ = (*in - '!') + 'A';
        else if (*in < 'A') *p++ = (*in - ':') + '0';
        else if (*in < 'a') *p++ = (*in - '[') + 'P';
        else                *p++ = (*in - '{') + 'V';
        }
      }
    *p = '\0';
    return(dir_result);
}

/*
 *  open_server_files
 *
 *  Open the item and newsgroup files for sharing
 */

int open_server_files(void)
{
  itmfab = cc$rms_fab;
  itmfab.fab$b_fac = FAB$M_GET;
  itmfab.fab$l_fna = (char *) ITM_FILENAME;
  itmfab.fab$b_fns = strlen(itmfab.fab$l_fna);
  itmfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;

  itmrab = cc$rms_rab;
  itmrab.rab$l_fab = &itmfab;
  itmrab.rab$b_krf = 0;
  itmrab.rab$b_ksz = 8;
  itmrab.rab$l_ubf = (char *) &newsitm;
  itmrab.rab$w_usz = sizeof newsitm;
  itmrab.rab$l_rbf = (char *) &newsitm;
  itmrab.rab$w_rsz = sizeof newsitm;

  grpfab = cc$rms_fab;
  grpfab.fab$b_fac = FAB$M_GET;
  grpfab.fab$l_fna = (char *) GRP_FILENAME;
  grpfab.fab$b_fns = strlen(grpfab.fab$l_fna);
  grpfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;

  grprab = cc$rms_rab;
  grprab.rab$l_fab = &grpfab;
  grprab.rab$l_ubf = (char *) &newsgrp;
  grprab.rab$w_usz = sizeof newsgrp;
  grprab.rab$l_rbf = (char *) &newsgrp;
  grprab.rab$w_rsz = sizeof newsgrp;

  if (	 !sys_open(&itmfab)
      || !sys_connect(&itmrab)
      || !sys_open(&grpfab)
      || !sys_connect(&grprab)) return(0);
  return(1);
}

/*
 *  main
 */

int  main(argc,argv)
  int argc; char *argv[];
{
  char proto[32], l_id[IDLEN + 4], inpline0[512],
       qual[512], itm_fname[256], cmd[132], *cp;
  int  proto_num = 0, have_open_channel_OK = 0, reply = 0, i;
  unsigned short qual_len;
  FILE *fpl;
  $DESCRIPTOR(qual_dsc,qual);
  int try;

  init_mem();

  *taskstr = '\0';
  *rem_node = *proto = *idfile = *logfile = *dbgfile = '\0';
				/* interpret program arguments */
  if (argc < 2) {
    printf("Usage NNTP_XMIT [-s] [-x] [-t decnet-task] node proto idfile [log] [dbgfile]\n");
    printf("  -or-\n");
    printf("Usage: NNTP_XMIT/NODE=node/PROTOCOL=proto/IDFILE=idfile[/LOGFILE=log]\n");
    printf("            [/TASK=task][/DEBUGFILE=dbgfile][/INDEXID][/STREAM]\n");
    exit(1);
    }
  if (*argv[1] == '/') {
    char mcommand[1024];
    register int argindex;

    strcpy(mcommand,"INVOKENNTP");
    for (argindex = 1; argindex < argc; argindex++ )
    {
	strcat(mcommand," ");
	strcat(mcommand,argv[argindex]);
    }
    if (!(cli$dcl_parse(c$dsc(mcommand),(int *)&nntpxmitcmd,0,0,0) & 1)) exit(1);

    decnetstream = (cli$present(c$dsc("STREAM")) & 1);
    idx = (cli$present(c$dsc("INDEXID")) & 1);
    if (cli$get_value(c$dsc("NODE"),&qual_dsc,&qual_len) & 1) {
      qual[qual_len] = '\0';
      strcpy(rem_node,qual);
      }
    if (cli$get_value(c$dsc("PROTOCOL"),&qual_dsc,&qual_len) & 1) {
      qual[qual_len] = '\0';
      strcpy(proto,qual);
      }
    if (cli$get_value(c$dsc("IDFILE"),&qual_dsc,&qual_len) & 1) {
      qual[qual_len] = '\0';
      strcpy(idfile,qual);
      }
    if (cli$get_value(c$dsc("LOGFILE"),&qual_dsc,&qual_len) & 1) {
      qual[qual_len] = '\0';
      strcpy(logfile,qual);
      logging = 1;
      }
    if (cli$get_value(c$dsc("DEBUGFILE"),&qual_dsc,&qual_len) & 1) {
      qual[qual_len] = '\0';
      strcpy(dbgfile,qual);
      debugging = 1;
      }
    if (cli$get_value(c$dsc("TASK"),&qual_dsc,&qual_len) & 1) {
      qual[qual_len] = '\0';
      if (qual_len) {
        if (!strchr(qual,'=')) strcpy(taskstr,"TASK=");
        else *taskstr = '\0';
        strcat(taskstr,qual);
	cp = taskstr;  --cp; while (*++cp) *cp = toupper(*cp);
        }
      }
    }
  else {
    for (i = 1 ; i < argc ; ++i) {
      if (*argv[i] == '-') {
        cp = argv[i];
        ++cp;
        if (tolower(*cp) == 'h') {
          printf("NNTP_XMIT\n");
          printf("Usage NNTP_XMIT [-s] [-x] [-t decnet-task] node proto idfile [log] [dbgfile]\n");
          printf("  -s - used with DECnet protocol to use stream Input Qio calls\n");
          printf("       (used to connect to Ultrix NNTP over DECnet)\n");
          printf("       default is ok for remote VMS systems\n");
          printf("\n");
          printf("  -x - used to read indexed message-id files rather than the default\n");
          printf("       sequential files\n");
          printf("       default is sequential access routines\n");
          printf("\n");
          printf("  -t decnet-task   - the default task connect string is TASK=NNTP\n");
          printf("       other strings may be specified instead\n");
          printf("\n");
          printf("  node - name of node to connect to\n");
          printf("\n");
          printf("  proto - protocol to use \"tcp\" for tcp/ip, \"decnet\" for DECnet\n");
          printf("\n");
          printf("  idfile - name of message-id file to read\n");
          printf("\n");
          printf("  log - optional - name of log file to record activity of the run\n");
          printf("\n");
          printf("  dbgfile - optional - name of debug output file\n");
          printf("\n");
          printf("\n");
          printf("  e.g. NNTP_XMIT -x tcp.node.domain tcp tcpid.idx\n");
          printf("\n");
          printf("       NNTP_XMIT -s -x -t NNTP= ultrixnode decnet ultrix.idx\n");
          printf("\n");
          exit(1);
          }
        if (tolower(*cp) == 't') {
          if (++i < argc) {
            strcpy(taskstr,argv[i]);
            cp = taskstr;  --cp; while (*++cp) *cp = toupper(*cp);
            }
          else {
            *idfile = 0;
            break;
            }
          }
        else {
          while (*cp) {
            if (tolower(*cp) == 'x') idx = 1;
            if (tolower(*cp) == 's') decnetstream = 1;
            ++cp;
            }
          }
        }
      else if (!*rem_node) strcpy(rem_node,argv[i]);
      else if (!*proto) strcpy(proto,argv[i]);
      else if (!*idfile) strcpy(idfile,argv[i]);
      else if (!*logfile) {
        strcpy(logfile,argv[i]);
        logging = 1;
        }
      else if (!*dbgfile) {
        strcpy(dbgfile,argv[i]);
        debugging = 1;
        }
      else {
        *idfile = 0;
        break;
        }
      }
    }

  if (!*idfile) {
    printf("Usage: NNTP_XMIT [-s] [-x] [-t decnet-task] node proto idfile [log] [dbgfile]\n");
    exit(1);
    }

  if (init_lock()) exit(1);

  if (!open_server_files()) {
    printf("FileError: Cannot open NEWS datafiles\n");
    exit(1);
    }

  init_names();  /* get local node name */

#ifdef CMU
  if (tolower(*proto) == 't') proto_num = CMUTCP;
  if (tolower(*proto) == 'c') proto_num = CMUTCP;
#endif
#ifdef UCX
  if (tolower(*proto) == 't') proto_num = UCXTCP;
  if (tolower(*proto) == 'u') proto_num = UCXTCP;
#endif
#ifdef TWG
  if (tolower(*proto) == 't') proto_num = WINTCP;
  if (tolower(*proto) == 'w') proto_num = WINTCP;
#endif
#ifdef MULTINET
  if (tolower(*proto) == 't') proto_num = MULTINETTCP;
  if (tolower(*proto) == 'm') proto_num = MULTINETTCP;
  if (tolower(*proto) == 's') proto_num = MULTINETTCP;
#endif
#ifdef TCPWARE
  if (tolower(*proto) == 't') proto_num = TCPWARETCP;
  if (tolower(*proto) == 'p') proto_num = TCPWARETCP;
#endif

  if (proto_num != DECNET) decnetstream = 0;
  else if (!*taskstr) {
    char logname[132],
	 *gotenv;

    sprintf(logname,"NEWS_%s_TASK",rem_node);
    cp = logname;  --cp; while (*++cp) *cp = toupper(*cp);
    if ( (gotenv = (char *) news_getenv(logname,0)) ) strcpy(taskstr,gotenv);
    else strcpy(taskstr,"TASK=NNTP");
    cp = taskstr;  --cp; while (*++cp) *cp = toupper(*cp);
    }

  sysprv();
  if (0 == open_id_file(idfile, &idx)) {
    if (idx == 1) {
      exit(1);
      }
    else {
      sysprv();
      if (NULL == (fpr = fopen(idfile,"r", "shr=get", "mbc=64"))) {
	nosysprv();
	printf("FileError: Cannot open file: %s\n",idfile);
	exit(1);
	}
      idx = 0;
      nosysprv();
      }
    }
  if (0 == idx) {
    fgetname(fpr,idfile);
    strcpy(tmpf,idfile);
    if ( (cp = strchr(tmpf,';')) ) *cp = '\0';
    if ( (cp = strrchr(tmpf,']')) ) cp++;
    else if ( (cp = strrchr(tmpf,':')) ) cp++;
    else cp = tmpf;
    sprintf(cp,"NNTP_%X.TMP",getpid());
    sysprv();
    fpw = fopen(tmpf,"w", "mbc=64");
    nosysprv();
    }

  if (debugging) fpd = fopen(dbgfile,"w");

  while (   (idx && read_id(inpline))
	 || (!idx && fgets(inpline,MAXIDLEN,fpr))) {
    if ( (cp = strchr(inpline,'\n')) ) *cp = '\0';

    if ((accepted + failed) > NNTP_MAX_OFFER) {
      nntp_write("QUIT");
      wait_net_response(CLIENT_TIMER,1);
      GO_DIE(MAX_OFFERED)
      }
    if (news_lock_alarm) {
      nntp_write("QUIT");
      wait_net_response(CLIENT_TIMER,1);
      GO_DIE(MANAGER_STOP)
      }

    c$free_tmp();   /* fix memory leak RRS 21-Feb-1992 */
    for (i = 0; i < (IDLEN + 4); ++i) l_id[i] = '\0';
    strncpy(l_id,inpline,IDLEN-1); l_id[IDLEN-1] = '\0'; try = 1;

    itmrab.rab$l_kbf = l_id;
    itmrab.rab$b_ksz = IDLEN + 4;
    itmrab.rab$b_krf = 1;
    itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;

    grprab.rab$l_kbf = (char *) &(newsitm.itm_grp);
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
    grprab.rab$b_rac = RAB$C_KEY;

    if (   !sys_get_nornf(&itmrab)
	|| strcmp(l_id,newsitm.itm_id)) {
      strncpy(l_id,inpline,IDLEN);   /* RRS: try one longer just in case */
      l_id[IDLEN] = '\0';
      try = 2;
      if (   !sys_get_nornf(&itmrab)
	  || strcmp(l_id,newsitm.itm_id)) {
	if (debugging) fprintf(fpd,"ItemAccess: Cannot locate item %s\n",inpline);
	if (idx) delete_id();
	expired++;
	continue;
	}
      }
    if (!sys_get_nornf(&grprab)) {
      if (debugging) fprintf(fpd,"GroupAccess: Cannot locate group %s\n",inpline);
      if (idx) delete_id();
      continue;
      expired++;
      }
    sprintf(itm_fname,Itm_template,util_dir(newsgrp.grp_name),newsitm.itm_num);
    if (!(fpi = fopen(itm_fname,"r", "mbc=64"))) {
      if (debugging) {
	fprintf(fpd,"FileAccess: Cannot open item file %s\n",itm_fname);
	fprintf(fpd, "Retry: %s\n",inpline);
	}
      if (!idx) fprintf(fpw,"%s\n",inpline);
      ++lines_written;
      continue;
      }

    if (debugging && !have_open_channel_OK)
      fprintf(fpd,"X> Try open channel to remote node\n");
    if (!open_rem_chan(rem_node,proto_num)) {
      if (debugging) fprintf(fpd,"Cannot connect to node, retry: %s\n",inpline);
      GO_DIE(ERROR)
      }

    sprintf(cmd,"IHAVE %s",inpline);
    have_open_channel_OK = 1;

    if (debugging) fprintf(fpd,"X> %s(%d)<",cmd,try);
    offered++;
    if (   !nntp_write(cmd)
	|| !(reply = wait_net_response(CLIENT_TIMER,1))) {
      if(debugging) fprintf(fpd, "No reply received, retry: %s\n",inpline);
      GO_DIE(ERROR)
      }
    if (debugging) fprintf(fpd,"%d>", reply);
    if ((reply == 435) || (reply == 437)) {
      if (debugging) fprintf(fpd," (Rejected: dont send");
      if (reply == 437) fprintf(fpd," and dont retry");
      rejected++;
      if (idx) delete_id();
      fclose(fpi);
      if (debugging) fprintf(fpd,")\n");
      continue;
      }
    if (reply != 335) {
      failed++;
      fclose(fpi);
      if (!idx) {
	fprintf(fpw,"%s\n",inpline);
	++lines_written;
	}
      if (debugging) fprintf(fpd,"\n");
      if (reply == 502) {
	printf("502 access restriction or permission denied\n");
	}
      continue;
      }
    if (debugging) fprintf(fpd,"(send article)");
    bytes_sent = 0;
    elapsed_time(1);
    while (fgets(inpline0,512,fpi)) {
      char *t;
/* HUJI change - do not send Relay-version since some Unix servers die with it */
        if((strncmp(inpline0, "Relay-Version:", 14) == 0) &&
           (bytes_sent == 0))   /* First line */
                continue;       /* Do not send this line */
/* End of HUJI change */

      if (*inpline0 == '.') memmove(inpline0 + 1,inpline0,strlen(inpline0) + 1);
      if ( (t = strchr(inpline0,'\n')) ) *t = '\0';
      if (!nntp_write(inpline0)) GO_DIE(ERROR)
      bytes_sent += strlen(inpline0);
      }
    fclose(fpi);
    if (!nntp_write(".") || (!(reply = wait_net_response(CLIENT_TIMER,1)))) GO_DIE(ERROR)
    total_bytes_sent += bytes_sent;
    total_elapsed_time += elapsed_time(0);
    if (debugging) fprintf(fpd," %d",reply);
    if (reply == 435  || reply == 437) {
      if (debugging) fprintf(fpd," (Rejected: dont send");
      if (reply == 437) fprintf(fpd," and dont retry");
      if (debugging) fprintf(fpd,"\n");
      rejected++;
      if (idx) delete_id();
      continue;
      }
    if (reply == 235) {
      accepted++;
      if (debugging) fprintf(fpd,"(accepted)\n");
      if (idx) delete_id();
      continue;
      }
    if (!idx) {
      fprintf(fpw,"%s\n",inpline);
      ++lines_written;
      }
    if (debugging) fprintf(fpd,"(failed)\n");
    failed++;
    }
  nntp_write("QUIT");
  wait_net_response(CLIENT_TIMER,1);
  if (idx) {
    close_id_file();
    sysprv();
    if (NULL != (fpr = fopen(idfile, "r", "shr=get", "mbc=64"))) {
      if (!fgets(inpline0,512,fpr))
	delete(idfile);
      fclose(fpr);
      }
    nosysprv();
    }
  else {
    sysprv();
    delete(idfile);
    fclose(fpr);
    fclose(fpw);
    if (lines_written) rename(tmpf,idfile);
    else delete(tmpf);
    nosysprv();
    }
  if (logging){
    dat();
    fpl = fopen(logfile,"a+");
    fprintf(fpl,"%s %s: nntpxmit: %s %d offered, %d accepted, %d rejected, %d failed, %d expired",
		datum, news_node, rem_node, offered, accepted, rejected, failed, expired);
		/**	Report on xfer rates: Earle Ake ake@dayton.saic.com */
    if (total_elapsed_time > 0)
      fprintf(fpl,", %ld bytes, %.2f secs (%ld bytes/sec)",
        total_bytes_sent,(float) total_elapsed_time/100.0,(total_bytes_sent*100)/total_elapsed_time);
    fprintf(fpl,"\n");
    fclose(fpl);
    }
  if (debugging) fclose(fpd);
}

/*
 * mem_fail - called by memory allocation routines in NewsRTL when allocation
 * request fails.  Free memory reserve to get a little scratch space, and
 * try to exit gracefully.
 */

void *mem_fail(bytesize)
  size_t bytesize;
{  

  if (mem_reserve) {
    mem_reserve = 0;
    if (debugging) fprintf(fpd,"mem_fail: Out of memory - exiting\n");
    nntp_write("QUIT");
    wait_net_response(CLIENT_TIMER,1);
    GO_DIE(NOMEM);
    }
  exit(LIB$_INSVIRMEM);
  return((void *) 0);  /* dead code to keep DECC happy */
}
