/*
**++
**  FACILITY:
**      NNTP_TCPWINMULTINET
**
**  ABSTRACT:
**      This module contains the QIO interface to the WIN/MULTINET implementation
**      of TCP/IP, V3.0.  The module provices a single-threaded interface to
**      the NEWSSERVER module.
**	Also supports TCPware for VMS V2.2 and later.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990,1991
**
**  MODIFICATION HISTORY:
**      V5.5     7-Oct-1988     GIH
**          Included modifications for WIN TCP/IP V3.0 support by Jim Patterson
**          (jimp@cognos.uucp)
**          NOTE: I have NOT tested the WIN support modifications.
**      V5.7    21-Nov-1988     PK
**        - Changes to support WIN TCP 3.2 - Pekka Kytolaakso
**        - There still was an error in WIN support. READ_NET both in
**          nntp_tcpwin.c and in nntp_tcpcmu.c had an error.
**          change: if (!iosb[1]) return(-1);
**          to:     if (!iosb[1]) return(cancel_net(),-1);;
**          Also make nntp_getchar a bit faster by defining it as a macro
**             Pekka Kytolaakso
**      V5.9    18-Apr-1989     GIH
**        - Changes to allow compilation by GNUCC compiler
**        - Modified GET_REMOTE_HOST routine to get the remote host name in 
**          order to implement logging when receiving articles in NNTP_SERVER.
**              Michelle R. Thibault
**	  - Add code for MULTINET, Mats Sundvall
**	V6.0-1	 3-Nov-1990	(GIH added)
**	  - James Gerland: TWG changes
**	V6.0-1	 5-Nov-1990	glass@mgi.com
**	  - clean up eob problem with read_net & write_net
**	V6.0-1	 5-Nov-1990	gih
**	  - Add buffered output to the code as per CMU routines (POK code)
**	V6.0-4	25-May-1991	volz@process.com
**	  - Added TCPware support
**	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)
**--
**/

#ifdef vaxc
#module NNTP_TCPWINMULTINET "V6.1"
#endif

#define _NNTP_SERVER_DRIVER_C

#include "nntpinclude.h"

void write_out(void);
int open_net1(void);

#if defined(TWG) || defined(MULTINET) || defined(TCPWARE)
#ifdef __GNUC__
extern int _NOSHARE(h_errno);
#else
extern noshare int h_errno;
#endif
#endif

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

#define TIMEOUT     7200         /* read wait timeout  - 2 hours */
#define X_BUF_SIZE  2048        /* size of line assembly read buffer */

                                /* check status bits */
/* #define cks(s)      if (!((c$status = (s)) & 1)) lib$signal(c$status) */
/* static int c$status;          * return status */

#define nntp_getchar() (!nntp_size ? nntp_fillbuf() : (--nntp_size, *nntp_ptr++))

static unsigned short f_i;      /* tcp channel input */
static unsigned short f_o;      /* tcp channel output */

static unsigned short iosb[4];  /* i/o status blocks */
static unsigned short *i_o_sts = iosb;
static char nntp_buffer[X_BUF_SIZE];
static char *nntp_ptr = nntp_buffer;
static int nntp_size = 0;
static int logged = 0;
#define POK_OUTPUT_BUFFER	1

#ifdef POK_OUTPUT_BUFFER
static char nntp_output_buffer[X_BUF_SIZE];	/* NNTP output buffer */
static char *nntp_output_ptr = nntp_output_buffer;
static int nntp_output_size = X_BUF_SIZE;
#endif

void (*next_function)(int);

FILE *dbf = 0;


int main(void)
{
    char *debug_file;

    if (!open_net1()) exit(1);
/* NOTE: The opening of the debug file is done after the network devices are
	 opened since there seems to be an undesireable side effect (i.e. you
	 can't open a useful connection to the network device) if the C 
	 runtime library I/O routines get to initialize before we open our 
	 network connection.
Mark Pizzolato Oct 20, 1994.
*/
    if ( (debug_file=news_getenv("NNTP_SERVER_DEBUG",1)) != 0 ) {
      dbf = fopen(debug_file,"w");
      if ( dbf ) fprintf(dbf,"NNTP Server starting\n");
    }
    if (!server_init(1)) exit(1);
    server_init_unit(1);
    while (next_function) {

#ifdef POK_OUTPUT_BUFFER
      write_out();
#endif

      (*next_function)(1);
      }
    server_shut();
    close_net();
}

/*
 *  cancel_net
 *
 *  If the read timer expires then cancel this server task (by exiting)
 */

void cancel_net(sig)
    int sig;
{

    if ( dbf ) fprintf(dbf,"Cancel_net\n");

    sys$cancel(f_o);
    sys$cancel(f_i);
    logged = 1;
    log_to_file(1);
    write_net("400 service discontinued - read timeout\r\n",0);

#ifdef POK_OUTPUT_BUFFER
    write_out();
#endif

#ifndef TCPWARE
    sys$qiow(0,f_i,IO$_DELETE,iosb,0,0,0,0,0,0,0,0);
    sys$qiow(0,f_o,IO$_DELETE,iosb,0,0,0,0,0,0,0,0);
#endif
    sys$dassgn(f_i);
    sys$dassgn(f_o);
    close_batch_file();  /* clean up any outstanding batch files RRS */
    exit(1);
}

static int nntp_fillbuf(void)
{
    int status;


    if (dbf) fprintf(dbf,"nntp_fillbuf\n");

    signal(SIGALRM,cancel_net);
    alarm(TIMEOUT);
    _c$cks(sys$qiow(0,f_i,IO$_READVBLK,iosb,0,0,nntp_buffer, sizeof(nntp_buffer),0,0,0,0));
    alarm(0);
    if (!iosb[1]) return(cancel_net(SIGKILL),-1);
    nntp_ptr = nntp_buffer;
    nntp_size = iosb[1]-1;
    return(*nntp_ptr++);
}

/*
 *  read_net
 *
 *  Get a newline terminated line of text from the server. Strips CRs.
 *  Parameters:     string  has the buffer space for the line received.
 *                  size    is the size of the buffer.
 *  Returns:        length of returned string
 *  Side effects:   Talks to server, changes contents of "string"
 */

int read_net(string, size,unit)
  char *string;
  int size;
  int unit;
{
  char *cp;
  int c = 0;

  for (cp = string; size > 1; size--) {
    do c = nntp_getchar(); while (c == '\r'); /* skip all CRs */
    *cp++ = c;
    if (c == '\n') break;
    }
  *cp = '\0';

  if (dbf) fprintf(dbf,"Read_net:%s\n",string);

  return(cp - string);
}

/*
 *  write_net
 *
 *  write to the client process - catch i/o errors and abort
 */

#ifdef POK_OUTPUT_BUFFER

void write_net(s,unit)
  const char *s;
  int unit;
{
  register int size = strlen(s);
  int status;


  if (dbf) fprintf(dbf,"Write_net:%s\n",s);

  if (nntp_output_size < size) write_out(); /* no space in buffer - flush */
  while (size > X_BUF_SIZE) {
#ifdef TCPWARE
    _c$cks(sys$qiow(0,f_o,IO$_WRITEVBLK,iosb,0,0,s,X_BUF_SIZE,0,0,0,0));
#else
    _c$cks(sys$qiow(0,f_o,IO$_WRITEVBLK,iosb,0,0,s,X_BUF_SIZE,0,1,0,0));
#endif
    _c$cks(*i_o_sts);
    s += X_BUF_SIZE;
    size -= X_BUF_SIZE;
    }
  strncpy(nntp_output_ptr,s,size);
  nntp_output_ptr += size;
  nntp_output_size -= size;
}

void write_out(void)
{
    int status;

    if (dbf) fprintf(dbf,"write_out\n");

  if (nntp_output_ptr != nntp_output_buffer) {  /* something in the buffer */
#ifdef TCPWARE
    _c$cks(sys$qiow(0,f_o,IO$_WRITEVBLK,iosb,0,0,nntp_output_buffer,nntp_output_ptr - nntp_output_buffer,0,0,0,0));
#else
    _c$cks(sys$qiow(0,f_o,IO$_WRITEVBLK,iosb,0,0,nntp_output_buffer,nntp_output_ptr - nntp_output_buffer,0,1,0,0));
#endif
    _c$cks(*i_o_sts);
    nntp_output_ptr = nntp_output_buffer;
    nntp_output_size = X_BUF_SIZE;
    }
}
#else

void write_net(s,unit)
  const char *s;
  int unit;
{
  int size = strlen(s);

  if (dbf) fprintf(dbf,"Write_net:%s\n",s);

  while (size > X_BUF_SIZE) {
#ifdef TCPWARE
    _c$cks(sys$qiow(0,f_o,IO$_WRITEVBLK,iosb,0,0,s,X_BUF_SIZE,0,0,0,0));
#else
    _c$cks(sys$qiow(0,f_o,IO$_WRITEVBLK,iosb,0,0,s,X_BUF_SIZE,0,1,0,0));
#endif
    _c$cks(*i_o_sts);
    s += X_BUF_SIZE;
    size -= X_BUF_SIZE;
    }
#ifdef TCPWARE
  _c$cks(sys$qiow(0,f_o,IO$_WRITEVBLK,iosb,0,0,s,strlen(s),0,0,0,0));
#else
  _c$cks(sys$qiow(0,f_o,IO$_WRITEVBLK,iosb,0,0,s,strlen(s),0,1,0,0));
#endif
  _c$cks(*i_o_sts);
}
#endif
void next_call(unit,func,type)
    int unit;
    void (*func)(int);
    int type;
{
    next_function = func;
}

/*
 *  open_net1
 *
 *  Open TCP channel
 */

int open_net1(void)
{
    $DESCRIPTOR_CONST(in,"SYS$INPUT");
    $DESCRIPTOR_CONST(out,"SYS$OUTPUT");
    int status;

    if (dbf) fprintf(dbf,"Open_net\n");

    _c$cks(sys$assign(&in,&f_i,0,0));
    _c$cks(sys$assign(&out,&f_o,0,0));
    return(1);
}

/*
 *  close_net
 *
 *  Close the TCP link
 */

void close_net()
{
    if (dbf) fprintf(dbf,"Close_net\n");

#ifdef POK_OUTPUT_BUFFER                        /* V6.0-2 glass@mgi.com*/
  write_out();                                  /*  " */
#endif                                          /*  " */
  if (!logged) log_to_file(1);
  sys$cancel(f_i);
  sys$cancel(f_o);
  return;
}

/*

What follows is the getremhost() function for NNTP_SERVER with
WIN/TCP. It uses getpeername() to get the address of remote host
followed by gethostbyname() to convert address to hostname.
At return remhost contains the name of remote host as it is received
from nameserver (NAMED), remuser is always "nntp" because TCP/IP
doesn't provide any way to obtain remote username.
 
  Seppo Syrjanen                      Internet : syrjanen@cc.Helsinki.FI
  Computing Center                    BITNET   : syrjanen@finuha.bitnet
  University of Helsinki              Phone    : +358 0 708 4132
  Finland       "Cyborg's gotta do what cyborg's programmed to do." -ABC
 
-------------------------------------------------------------------
*/
 
/*
 * Get name of the host requesting new connection
 *
 * for NNTP_SERVER with WIN/TCP. Tested with WIN/TCP 5.0.2.
 *
 * unsigned short f_i contains socket number of the connection.
 */
 
void getremhost(remhost, remuser, unit)
  char *remhost, *remuser;
  int unit;
{
  struct hostent *hp;
  struct sockaddr name;
  int namelen;
 
  if (remuser) strcpy(remuser,"nntp");
  if (remhost) {
    namelen=sizeof(name);
#ifdef TCPWARE
    { int status;
      _c$cks(sys$qiow(0,f_i,IO$_GETPEERNAME,0,0,0,&name,&namelen,0,0,0,0));
    }
#else
    getpeername(f_i,&name,&namelen);
#endif
    hp = gethostbyaddr(&name.sa_data[2],4,AF_INET);
    if (hp == NULL) {
      sprintf(remhost,"%d.%d.%d.%d",
        (name.sa_data[2]<0 ? name.sa_data[2]+256 : name.sa_data[2]),
        (name.sa_data[3]<0 ? name.sa_data[3]+256 : name.sa_data[3]),
        (name.sa_data[4]<0 ? name.sa_data[4]+256 : name.sa_data[4]),
        (name.sa_data[5]<0 ? name.sa_data[5]+256 : name.sa_data[5]));
      }
    else
      strcpy(remhost,hp->h_name);
    }
}
