/*
**++
**  FACILITY:
**      NNTP_TCPCMU
**
**  ABSTRACT:
**      This module contains the QIO interface to the CMU/Tek implementation
**	of TCP/IP.  The module provices a single-threaded interface to
**	the NEWSSERVER module.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988
**
**  MODIFICATION HISTORY:
**      V5.3    14-Jun-1988 POK     (PEKKA KYTOLAAKSO VTKK/TLP)
**                                  Made some changes in READ_NET, Compile with
**                                  CC/DEFINE=DEBUG to get trace of the TCP/IP
**                                  traffic in file sy$manager:sys$error.
**
**	V5.5	 7-Oct-1988	GIH
**          Revised call interface to server added
**
**      V5.7    13-Dec-1988     GIH
**        - 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.8    22-Feb-1989     GIH
**        - "While testing NEWS 5.7 I noticed that the CMU NNTP server did not
**          work properly.  When attempting to use NNTP_CLIENT to test the
**          server, it continually aborted on the READ sys$qiow in
**          nntp_fillbuf() in NNTP_TCPCMU.C.  After looking at all of the other
**          CMU code, the patch below seems to fix the problem.  I do not know
**          what other effects this might have, but it does seem to make the
**          server work. " Scott Allendorf, Astronomy System Manager,
**          The University of Iowa, sca@ceres.physics.uiowa.edu
**
**          #define X_BUF_SIZE  1024    - size of line assembly read buffer
**                    Changed from 2048 to 1024 - SCA 890128
**        - Add getremhost function
**      V5.9    18-Apr-1989
**        - Minor changes to allow complication by GNUCC compiler
**	V6.0-1	 5-Nov-1990	glass@mgi.com
**	  - clean up eob problem with read_net & write_net
**--
**/

#ifdef vaxc
#module NNTP_TCPCMU "V6.1"
#endif

#define _NNTP_SERVER_DRIVER_C

#if NAKED_INCLUDES
#include descrip
#include iodef
#include signal
#include stdio
#else
#include <descrip.h>
#include <iodef.h>
#include <signal.h>
#include <stdio.h>
#endif

#define DEBUG       0
#define TIMEOUT     7200         /* read wait timeout  - 10 minutes */
#define X_BUF_SIZE  1024        /* size of line assembly read buffer */

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

#define nntp_getchar() (!nntp_input_size ? nntp_fillbuf() : (--nntp_input_size, *nntp_input_ptr++))

static unsigned short f;               /* tcp i/o channel */

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

static unsigned short iosb[4];    /* i/o status blocks */
static unsigned short *i_o_sts = iosb;
static char nntp_input_buffer[X_BUF_SIZE];
static char *nntp_input_ptr = nntp_input_buffer;
static int nntp_input_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

int (*next_function)();

#if DEBUG
FILE *flog;
#define LOGFILE "NEWS_MANAGER:NNTP.LOG"
#endif

main()
{
#if DEBUG
    int cur_time;

    time(&cur_time);
    flog = fopen(LOGFILE,"a+");
    fprintf(flog,"TCP CMU open NNTP Connection %s",ctime(&cur_time));
    fclose(flog);
#endif

    if (!server_init(1)) exit(1);
#if DEBUG
    flog = fopen(LOGFILE,"a+");
    fprintf(flog,"server init\n");
    fclose(flog);
#endif
    if (!open_net()) exit(1);
#if DEBUG
    flog = fopen(LOGFILE,"a+");
    fprintf(flog,"opened net\n");
    fclose(flog);
#endif
    server_init_unit(1);
#if DEBUG
    flog = fopen(LOGFILE,"a+");
    fprintf(flog,"server unit inited\n");
    fclose(flog);
#endif
    while (next_function) {

#ifdef POK_OUTPUT_BUFFER
      write_out();
#endif
      (*next_function)(1);
      }
    server_shut();
    close_net();
#if DEBUG
    flog = fopen(LOGFILE,"a+");
    fprintf(flog,"net closed\n");
    fclose(flog);
#endif
}

/*
 *  cancel_net
 *
 *  If the read timer expires then cancel this server task (by exiting)
 */

void cancel_net(sig)
  int sig;
{
#if DEBUG
    fprintf(stderr,"Cancel_net\n");
#endif
    sys$cancel(f);
    logged = 1;
    log_to_file(1);
    write_net("400 service discontinued - read timeout\r\n");

#ifdef POK_OUTPUT_BUFFER
    write_out();
#endif

    cks(sys$qiow(0,f,IO$_DELETE,iosb,0,0,0,0,0,0,0,0));
    cks(*i_o_sts);
    sys$dassgn(f);
    exit(1);
}

static int nntp_fillbuf()
{
  signal(SIGALRM,cancel_net);
  alarm(TIMEOUT);
  cks(sys$qiow(0,f,IO$_READVBLK,iosb,0,0,nntp_input_buffer,sizeof(nntp_input_buffer),0,0,0,0));
  alarm(0);
  if (!iosb[1]) return(cancel_net(SIGKILL),-1);
  nntp_input_ptr = nntp_input_buffer;
  nntp_input_size = iosb[1]-1;
  return(*nntp_input_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 string
 *  Side effects:   Talks to server, changes contents of "string"
 */

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);
}

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

#ifdef POK_OUTPUT_BUFFER

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

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

  if (nntp_output_size < size) write_out(); /* no space in buffer - flush */
  while (size > X_BUF_SIZE) {
    cks(sys$qiow(0,f,IO$_WRITEVBLK,iosb,0,0,s,X_BUF_SIZE,0,1,0,0));
    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;
}

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

write_net(s,unit)
  char *s;
  int unit;
{
  int size = strlen(s);
#if DEBUG
  fprintf(stderr,"Write_net:%s\n",s);
#endif

  while (size > X_BUF_SIZE) {
    cks(sys$qiow(0,f,IO$_WRITEVBLK,iosb,0,0,s,X_BUF_SIZE,0,1,0,0));
    cks(*i_o_sts);
    s += X_BUF_SIZE;
    size -= X_BUF_SIZE;
    }
  cks(sys$qiow(0,f,IO$_WRITEVBLK,iosb,0,0,s,strlen(s),0,1,0,0));
  cks(*i_o_sts);
}
#endif

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

open_net()
{
    $DESCRIPTOR(ipdsc,"INET$DEVICE");
		/* was    $DESCRIPTOR(ipdsc,"IP:"); */

#if DEBUG
    fprintf(stderr,"Open_net\n");
#endif
    cks(sys$assign(&ipdsc,&f,0,0));
    cks(sys$qiow(0,f,IO$_CREATE,iosb,0,0,0,0,119,0,0,0));
    cks(*i_o_sts);
    return(1);
}

/*
 *  close_net
 *
 *  Close the TCP link
 */

close_net()
{
#if DEBUG
  fprintf(stderr,"Close_net\n");
#endif

#ifdef POK_OUTPUT_BUFFER 
  write_out();
#endif
  if (!logged) log_to_file(1); 
  sys$cancel(f);
  cks(sys$qiow(0,f,IO$_DELETE,iosb,0,0,0,0,0,0,0,0));
  cks(*i_o_sts);
  sys$dassgn(f);
}

/*
 *  getremhost
 *
 *  Return the remote host name
 */

struct ib {
    unsigned char fhost_len;
    unsigned char lhost_len;
    char fhost[128];
    short fill;
    unsigned short fport;
    short fill_1;
    char lhost[128];
    unsigned short lport;
    short fill_2;
    unsigned int linet_addr;
    unsigned int finet_addr;
    } _align(quadword) info_buffer;

void  getremhost(remhost,remuser,unit)
  char *remhost, *remuser; int unit;
{
  if (remhost) {

/* I have had some trouble with this call - it is reinserted in the
   distribution verion - BUT - take it out if you cannot get it to work,
   and replace it with the single line following!

    strcpy(remhost,"tcp");
*/

    cks(sys$qiow(0,f,IO$_MODIFY,iosb,0,0,&info_buffer,sizeof(info_buffer),0,0,0,0));
    cks(*i_o_sts);
    strncpy(remhost,info_buffer.fhost,info_buffer.fhost_len);
    remhost[info_buffer.fhost_len] = '\0';

    if (!*remhost)	/* the name resolver may have failed with a timeout */
      sprintf(remhost,"%d.%d.%d.%d",
                (info_buffer.finet_addr) & 0xff,
                (info_buffer.finet_addr >> 8) & 0xff,
                (info_buffer.finet_addr >> 16) & 0xff,
                (info_buffer.finet_addr >> 24) & 0xff);
    

/* if you cannot get it to work comment out the above block and put back the
   strcpy line! */

    }
  if (remuser) strcpy(remuser,"nntp");
}
