/*
**++
**  FACILITY:
**      NNTP_TCPUCX
**
**  ABSTRACT:
**	This module contains the code to interface to UCX's
**	inetd server.
**	- Alan Greig <A.Greig@tay.ac.uk>

It should be run from a service set up under UCX's auxiliary
server with a wrapper .COM similar to the following:

$ define nntp_server_drvr nl:
$ run news_manager:nntp_tcpucx

**
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990
**
**  MODIFICATION HISTORY:
**	V6.1b11	   Jun-1997	Alan Greig  A.Greig@tay.ac.uk
**	  - XOVER support added
**--
**/

#ifdef vaxc
#module NNTP_TCPUCX "V6.2"
#endif

#define _NNTP_SERVER_DRIVER_C
#define _NNTP_TCPUCX_C
#define module_name "NNTP_TCPUCX"

#include "nntpinclude.h"

#define TIMEOUT     3600         /* read wait timeout  - 60 minutes */
#define X_BUF_SIZE  2048
                                /* check status bits */
#define cks(s)      if (!((c$status = (s)) & 1)) lib$signal(c$status)

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

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

int netf;			/* Socket output */

static int c$status;            /* return status */

static unsigned short iosb[4];  /* i/o status blocks */
static char nntp_buffer[X_BUF_SIZE];
static char *nntp_ptr = nntp_buffer;
static int nntp_size = 0;

#define POK_OUTPUT_BUFFER       1

#define USE_QIOS		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);
void write_out(void);
void write_net(const char *s,int unit);


/*
 *  cancel_net
 *
 *  If the read timer expires then cancel this server task (by exiting)
 */

void cancel_net(sig)
  int sig;
{
  printf("cancel_net: Disconnect\n");
  write_net("400 service discontinued - I/O timeout\r\n",0);
  log_to_file(1);
#ifdef POK_OUTPUT_BUFFER
  write_out();
#endif
  spool_flush_batch();
  close(netf);
  sys$dassgn(f_i);
  exit(1);
}
void cancel_write(sig)
  int sig;
{
  printf("cancel_write: Disconnect\n");
  log_to_file(1);
  spool_flush_batch();
  close(netf);
  sys$dassgn(f_i);
  exit(1);
}

/*
 *  read_net
 *
 *  read from the client process - if the read timer expires cancel this server
 */

#ifndef USE_QIOS
int read_net(buffer,size,unit)
  char *buffer;
  int size;
  int unit;
{
  int i;
  char c;

  signal(SIGALRM,cancel_net);
  alarm(TIMEOUT);
/*  if (!gets(buffer)) strcpy(buffer,"quit");  */
  i=0;
  c = 0;
  while (c != '\r') {    
    c=getchar();
    if (c == EOF)
      {
      log_to_file(1);
      spool_flush_batch();
      exit(1);
      }
;
/*
    printf("Got %c = %d\n",c,c); 
*/
    if (c == '\n') continue;
    if (c == '\r') continue;
    buffer[i++] = c;
    }
  
  buffer[i] = 0;
  alarm(0);
  strcat(buffer,"\n"); 
/*  printf("Returning %s",buffer);
*/
  return(strlen(buffer));
}
#else

static int nntp_fillbuf(void)
{
    signal(SIGALRM,cancel_net);
    alarm(TIMEOUT);
    c$status = sys$qiow(0,f_i,IO$_READVBLK,iosb,0,0,nntp_buffer,
        sizeof(nntp_buffer),0,0,0,0);
    alarm(0);
    if (c$status & 1) c$status = iosb[0];
    if (!(c$status & 1))
      {
      printf("nntp_fillbuf: read failed\n");
/*      lib$signal(c$status); */
      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 DEBUG
  fprintf(stderr,"Read_net:%s\n",string);
#endif
  return(cp - string);
}
#endif


/*
 *  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);

#if DEBUG
  fprintf(stderr,"Write_net:%s\n",s);
#endif

  signal(SIGALRM,cancel_write);

  if (nntp_output_size < size) write_out(); /* no space in buffer - flush */
  while (size > X_BUF_SIZE) {
    alarm(TIMEOUT);
#ifdef USE_QIOS
    c$status = sys$qiow(0,f_i,IO$_WRITEVBLK,iosb,0,0,s,X_BUF_SIZE,0,0,0,0);
    if (c$status & 1) c$status = iosb[0];
    if (!(c$status & 1))
#else
    if (write(netf,s,X_BUF_SIZE) == -1)
#endif
      {
      printf("write_net: Write error\n");
      log_to_file(1);
      spool_flush_batch();
      exit(1);
      }
    s += X_BUF_SIZE;
    size -= X_BUF_SIZE;
    alarm(0);
    }
  strncpy(nntp_output_ptr,s,size);
  nntp_output_ptr += size;
  nntp_output_size -= size;
}
    

void write_out(void)
{
  signal(SIGALRM,cancel_write);
  if (nntp_output_ptr != nntp_output_buffer) {  /* something in the buffer */
    alarm(TIMEOUT);
#ifdef USE_QIOS
    c$status = sys$qiow(0,f_i,IO$_WRITEVBLK,iosb,0,0,nntp_output_buffer,
                        nntp_output_ptr-nntp_output_buffer,0,0,0,0);
    if (c$status & 1) c$status = iosb[0];
    if (!(c$status & 1))
#else
    if (write(netf,nntp_output_buffer,nntp_output_ptr-nntp_output_buffer) == -1)
#endif
      {
      printf("write_out: Write error\n");
      log_to_file(1);
      spool_flush_batch();
      exit(1);
      }
    nntp_output_ptr = nntp_output_buffer;
    nntp_output_size = X_BUF_SIZE;
    alarm(0);
    }
}
#else


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

/*  printf("%s\r\n\n",s);   */

  if (size <= X_BUF_SIZE)
    {
    if (write(netf,s,strlen(s)) == -1)
      {
      log_to_file(1);
      spool_flush_batch();
      exit(1);
      }
    }
  else
    {
    while (size > 0)
      {
      if (write(netf,s,X_BUF_SIZE) == -1)
        {
        log_to_file(1);
        spool_flush_batch();
        exit(1);
        }
      size -= X_BUF_SIZE;
      s += X_BUF_SIZE;
      }
    }
}
#endif


/*
 *  next_call
 *
 *  Set the next function to be invoked by the server
 */

void next_call(unit,func,type)
  int unit;
  void  (*func)(int);
  int  type;
{
  next_function = func;
}

/*
 *  open_net
 *
 *  Open TCP channel
 */

int open_net1(void)
{
#ifdef USE_QIOS
        char    sck_parm[4];   /* Socket creation parameter */
        $DESCRIPTOR_CONST(inet_dev,"SYS$NET");
#endif

    printf("open_net: entered\n");

#ifdef USE_QIOS

#if !defined(INET$C_TCP) && defined(IPPROTO_TCP)
#define INET$C_TCP IPPROTO_TCP
#endif
#if !defined(INET$C_STREAM) && defined(SOCK_STREAM)
#define INET$C_STREAM SOCK_STREAM
#endif
#if !defined(INET$C_STREAM) && defined(INET_PROTYP$C_STREAM)
#define INET$C_STREAM INET_PROTYP$C_STREAM
#endif
#if !defined(INET$C_AUXS)
#define INET$C_AUXS 127
#endif
        sck_parm[0] = INET$C_TCP;                /* TCP/IP protocol */
        sck_parm[1] = 0;                        /* This is really 2 bytes */
        sck_parm[2] = INET$C_STREAM;             /* Stream type of socket */
        sck_parm[3] = INET$C_AUXS;               /* From AUXServer */

        cks(sys$assign(&inet_dev, &f_i, 0, 0));

        /* Accept handoff of the device-socket from the UCX Auxiliary Server */
        cks(sys$qiow(3,                 /* Event flag */
                f_i,                    /* Channel number */
                IO$_SETMODE,            /* I/O function */
                iosb,                   /* I/O status block */
                0, 0, &sck_parm,        /* P1 Socket creation parameter */
                0, 0, 0, 0, 0));
        cks(iosb[0]);
#else
        netf = socket(INET$C_AUXS, 0, 0);
/*	netf = socket(AF_INET, SOCK_STREAM, 0);  */
#endif

    return(1);

}

/*
 *  close_net
 *
 *  Close the TCP link
 */

void close_net()
{
  printf("close_net: entered\n");
  spool_flush_batch();
  log_to_file(1);

#ifdef POK_OUTPUT_BUFFER                        /* V6.0-2 glass@mgi.com*/
  printf("close_net: calling write_out\n");
  write_out();                                  /*  " */
#endif                                          /*  " */

  close(netf);

#ifdef USE_QIOS
  printf("close_net: calling sys$cancel\n");
  sys$cancel(f_i);
#endif
  printf("close_net: exited\n");
}


/*
 *  getremhost
 *
 *  Return the remote host name and remote username
 */

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;
      cks(sys$qiow(0,f_i,IO$_GETPEERNAME,0,0,0,&name,&namelen,0,0,0,0));
    }
#else
    getpeername(netf,&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);
    }
  printf("getremhost: %s\n",remhost);
}

/*               
 *  main
 */

int main(argc, argv)
  int argc;
  char *argv[];
{
  if (!server_init(1)) exit(1);
  if (!open_net1()) exit(1);
  server_init_unit(1);
  while (next_function)
    {
#ifdef POK_OUTPUT_BUFFER
     write_out();
#endif
    (*next_function)(1);
    }
  server_shut();
  printf("main: close_net calling\n");
  close_net();
  printf("main: close_net done\n");
  exit(0);
}
