/*
**++
**  FACILITY:
**	NNTP_CLIENT
**
**  ABSTRACT:
**	Implementation of RFC977 client as an interactive program. This is
**	provided as an example of using the NNTP protocols. It is not
**	intended as a interactive user facility, but as an example program
**	which communicates with the remote server using NNTP.
**	
**	The utility may use either DECnet or TCP as the transport protocol
**	to access the server. TELNET/PORT=119 is an alternative to this
**	program when using a TCP connection.
**
**  AUTHOR:
**	Geoff Huston
**
**  COPYRIGHT:
**	Copyright © 1988,1990,1991,1992
**
**  VERSION:
**	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_CLIENT "V6.1"
#endif

#define _NNTP_CLIENT_C
#define _NNTP_SERVER_DRIVER_C

#include "nntpinclude.h"

#define NNTP_PORT	(119)

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

#ifdef UCX

#ifdef UCXQIOS
/*
 * struct sockaddr {
 *   short inet_family;
 *   short inet_port;
 *   int inet_adrs;
 *   char bklb[8];
 *   };
 */
struct itlist { int lgth; struct sockaddr *hst; };

/* static short sck_parm[2]; */
/* static struct sockaddr local_host, remote_host; */
struct itlist lhst_adrs, rhst_adrs;
#endif

#ifdef UCXSOCKETS
static int socket_1;
static struct sockaddr_in socket_2_name;
static struct in_addr inetadd;
static struct hostent hostentstruct, *hostentptr;
#endif

#endif
			/* i/o channels, and status blocks */
unsigned short chan,
	       net_chan;

IOSB_DEF iosb, read_iosb;

			/* input and output buffers */
char inpline[256],
     outl[1048],
     *nr_pos = outl;

			/* system call return status */
int status,
    net_chan_tcp = 0,
    decnetstream = 0;

int *c$_tmphead = (int *) 0;    /* head of side list */

int *c$alloc_tmp(size)
  int size;
{
  int *tmp;

  tmp = (int *) malloc(size + 4);
  *tmp = (int) c$_tmphead;
  c$_tmphead = tmp;
  return(c$_tmphead + 1);
}

void c$free_tmp(void)
{
  int *c$_tmp;

  while (c$_tmphead) {
    c$_tmp = (int *) *c$_tmphead;
    free(c$_tmphead);
    c$_tmphead = c$_tmp;
    }
}

struct dsc$descriptor_const_s *c$dsc(c$_str)
  const char *c$_str;
{
  struct dsc$descriptor_const_s *c$_tmpdesc;

  c$_tmpdesc = (struct dsc$descriptor_const_s *) c$alloc_tmp(8);
  c$_tmpdesc->dsc$w_length = strlen(c$_str);
  c$_tmpdesc->dsc$b_dtype = DSC$K_DTYPE_T;
  c$_tmpdesc->dsc$b_class = DSC$K_CLASS_S;
  c$_tmpdesc->dsc$a_pointer = c$_str;
  return(c$_tmpdesc);
}

int c$cks_routine(status, module, line_number)
  int status;
  const char *module;
  int line_number;
{
  if (!(status & 1)) {
    lib$stop(status);
    exit(status);
    }
  if (c$_tmphead) c$free_tmp();
  return(status);
}

/*
 *  read_complete
 *
 *  AST routine from tty read. Add crlf and call sync write to server
 *  object.  Re-establish outstanding async tty read call.
 */

void read_complete(void)
{
  if (!iosb.count && (inpline[0] == 26)) {
#ifdef UCXSOCKETS
    shutdown(socket_1,2);
    close(socket_1);
#else
    c$cks(sys$cancel(net_chan));
    c$cks(sys$qiow(0,net_chan,IO$_DELETE,&iosb,0,0,0,0,0,0,0,0));
    c$cks(iosb.status);
    c$cks(sys$dassgn(net_chan));
#endif
    c$cks(sys$dassgn(chan));
    sys$exit(1);
    }
  c$cks((unsigned int) iosb.status);
  inpline[iosb.count] = '\0';
  strcat(inpline,"\r\n");
#ifdef UCXSOCKETS
  if (send(socket_1,inpline,strlen(inpline),0) < 0) {
    shutdown(socket_1,2);
    close(socket_1);
    perror("send");
    exit(0);
    }
#else
  c$cks(sys$qiow(0,net_chan,IO$_WRITEVBLK,&iosb,0,0,inpline,strlen(inpline),0,net_chan_tcp,0,0));
  c$cks(iosb.status);
#endif
  c$cks(sys$qio(0,chan,IO$_READVBLK,&iosb,read_complete,0,inpline,132,0,0,0,0));
}

/*
 *  net_read
 *
 *  AST routine from net read - check on carriage control then write
 *  out to screen - add additional line feed if this is a break line.
 *  Recall outstanding async net read routine.
 */

void net_read(void)
{
  char *cp, *il = outl;
  int bsize = 1024;
  int recv_value;

#ifdef UCXSOCKETS
  for (;;) {
    if ((recv_value = recv(socket_1,nr_pos,bsize,0)) <= 0) {
      shutdown(socket_1,2);
      close(socket_1);
      perror("recv");
      exit(0);
      }
    read_iosb.count = recv_value;
#else
  c$cks((unsigned int) read_iosb.status);
#endif

  il = outl;
  bsize = 1024;
  nr_pos[read_iosb.count] = '\0';

  cp = il;
  while (cp) {
    if ( (cp = strchr(il,'\n')) ) *cp = '\0';
    if ((cp) && (*(cp - 1) == '\r')) *(cp - 1) = '\0';
    if (cp) {
      cp++;
      printf("%s\n",il);
      if (!strcmp(il,".")) printf("\n");
      else if (	  (strlen(il) > 5) && isdigit(il[0]) && isdigit(il[1])
	       && isdigit(il[2]) && (il[3] == ' '))
	printf("\n");
      il = cp;
      }
    }
  cp = outl;
  while ( (*cp++ = *il++) ) --bsize;
  nr_pos = cp - 1;
#ifdef UCXSOCKETS
  }
#else
  if (decnetstream) c$cks(sys$qio(0,net_chan,IO$_READVBLK|IO$M_MULTIPLE,&read_iosb,net_read,0,nr_pos,bsize,0,0,0,0));
  else c$cks(sys$qio(0,net_chan,IO$_READVBLK,&read_iosb,net_read,0,nr_pos,bsize,0,0,0,0));
#endif
}

/*
 *  main
 *
 *  Open channels to tty and decnet server. Request node name and channel
 *  protocol (DECNET or TCP). Call async readers (using ast completion
 *  routines), then sit back and wait for the asts to fire.
 */

int main(void)
{
  char *c, taskstr[132], newsserver[80], chan_type[80], stream[80];

  c$cks(sys$assign(c$dsc("TT:"),&chan,0,0));
  printf("Node: ");
  c$cks(sys$qiow(0,chan,IO$_READVBLK,&iosb,0,0,inpline,80,0,0,0,0));
  c$cks((unsigned int) iosb.status);
  inpline[iosb.count] = '\0';
  if ( (c = strchr(inpline,':')) ) *c = '\0';
  printf("Channel (DECNET");
#if defined(MULTINET)
  printf(", MULTINETtcp");
#elif defined(TWG)
  printf(", WINtcp");
#elif defined(CMU)
  printf(", CMUtcp");
#elif defined(UCX)
  printf(", UCXtcp");
#elif defined(TCPWARE)
  printf(", TCPwaretcp");
#endif
  printf(")? [DECNET]: ");
  c$cks(sys$qiow(0,chan,IO$_READVBLK,&iosb,0,0,chan_type,80,0,0,0,0));
  c$cks((unsigned int) iosb.status);
  chan_type[iosb.count] = '\0';
#if defined(CMU)
  if (	 (*chan_type == 'C') || (*chan_type == 'c')
      || (*chan_type == 'T') || (*chan_type == 't')) {
    net_chan_tcp = 1;
    c$cks(sys$assign(c$dsc("IP:"),&net_chan,0,0));
    c$cks(sys$qiow(0,net_chan,IO$_CREATE,&iosb,0,0,inpline,119,0,1,0,300));
    if (!(iosb.status & 1)) {
      if (iosb.status == SS$_ABORT) lib$signal((unsigned int) iosb.status);
      }
    }
  else
#elif defined(TWG)
  if (	 (*chan_type == 'W') || (*chan_type == 'w')
      || (*chan_type == 'T') || (*chan_type == 't')) {
    c$cks(sys$assign(c$dsc("INET:"),&net_chan,0,0));
    c$cks(sys$qiow(0,net_chan,IO$_SOCKET,&iosb,0,0,AF_INET,SOCK_STREAM,0,0,0,0));
    if ((dest_host = gethostbyname(inpline)) == NULL) {
      int h[4], i;
      if (sscanf(inpline,"%d.%d.%d.%d",&h[0],&h[1],&h[2],&h[3]) != 4) {
	fprintf(stderr,"Host %s not defined by TCP\n",inpline);
	exit(1);
	}
      for (i = 0 ; i < 4 ; ++i)
	if (h[i] < 0 || h[i] > 255) {
	  fprintf(stderr,"Incorrect format of internet address\n");
	  exit(1);
	  }
      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);
    c$cks(sys$qiow(0,net_chan,IO$_CONNECT,&iosb,0,0,&data_socket,sizeof(data_socket),0,0,0,0));
    }
  else
#elif defined(MULTINET) || defined(TCPWARE)
#ifdef MULTINET
  if (	 (*chan_type == 'M') || (*chan_type == 'm')
#else
  if (	 (*chan_type == 'P') || (*chan_type == 'p')
#endif
      || (*chan_type == 'T') || (*chan_type == 't')) {
    c$cks(sys$assign(c$dsc("INET0:"),&net_chan,0,0));
    c$cks(sys$qiow(0,net_chan,IO$_SOCKET,&iosb,0,0,AF_INET,SOCK_STREAM,0,0,0,0));
    if ((dest_host = gethostbyname(inpline)) == NULL) {
      int h[4], i;
      if (sscanf(inpline,"%d.%d.%d.%d",&h[0],&h[1],&h[2],&h[3]) != 4) {
	fprintf(stderr,"Host %s not defined by TCP\n",inpline);
	exit(1);
	}
      for (i = 0 ; i < 4 ; ++i)
	if (h[i] < 0 || h[i] > 255) {
	  fprintf(stderr,"Incorrect format of internet address\n");
	  exit(1);
	  }
      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);
    c$cks(sys$qiow(0,net_chan,IO$_CONNECT,&iosb,0,0,&data_socket,sizeof(data_socket),0,0,0,0));
    }
  else
#elif defined(UCXSOCKETS)
  if (	 (*chan_type == 'U') || (*chan_type == 'u')
      || (*chan_type == 'T') || (*chan_type == 't')) {
    if ((socket_1 = socket(AF_INET,SOCK_STREAM,0)) == -1) {
      perror("socket");
      exit(0);
      }
    socket_2_name.sin_port = htons(NNTP_PORT);
    if ((inetadd.S_un.S_addr = inet_addr(inpline)) != -1) {
      socket_2_name.sin_family = AF_INET;
      socket_2_name.sin_addr = inetadd;
      }
    else {
      if ((hostentptr = gethostbyname(inpline)) == NULL) {
        perror("ethostbyname");
        close(socket_1);
        exit(0);
        }
      hostentstruct = *hostentptr;
      socket_2_name.sin_family = hostentstruct.h_addrtype;
      socket_2_name.sin_addr = *((struct in_addr *) hostentstruct.h_addr);
      }
    if (connect(socket_1,&socket_2_name,sizeof(socket_2_name))) {
      perror("connect");
      close(socket_1);
      exit(0);
      }
    }
  else
#endif
    {
    printf("Decnet NNTP Objects are commonly of the form:\n");
    printf("\tTASK=NNTP\tused by most VMS NNTP server implementations\n");
    printf("\tNNTP=\t\tused by Ultrix NNTP servers\n");
    printf("\tNEWS=\t\tused by some VMS NNTP server implementations\n");
    printf("Decnet Object string? [TASK=NNTP] : ");
    c$cks(sys$qiow(0,chan,IO$_READVBLK,&iosb,0,0,taskstr,80,0,0,0,0));
    c$cks((unsigned int) iosb.status);
    taskstr[iosb.count] = '\0';
    if (!*taskstr) strcpy(taskstr,"TASK=NNTP");
    c = taskstr;  --c; while (*++c) *c = toupper(*c);
    sprintf(newsserver,"%s::\"%s\"",inpline,taskstr);
    printf("Use DECnet stream input calls (normally only with Ultrix NNTP server)? [N]: ");
    c$cks(sys$qiow(0,chan,IO$_READVBLK,&iosb,0,0,stream,80,0,0,0,0));
    c$cks((unsigned int) iosb.status);
    stream[iosb.count] = '\0';
    decnetstream = ((*stream == 'Y') || (*stream == 'y'));
    c$cks(sys$assign(c$dsc(newsserver),&net_chan,0,0));
    }
#ifdef UCXSOCKETS
  c$cks(sys$qio(0,chan,IO$_READVBLK,&iosb,read_complete,0,inpline,132,0,0,0,0));
  net_read();
#else
  if (decnetstream)
    c$cks(sys$qio(0,net_chan,IO$_READVBLK|IO$M_MULTIPLE,&read_iosb,net_read,0,outl,1024,0,0,0,0));
  else
    c$cks(sys$qio(0,net_chan,IO$_READVBLK,&read_iosb,net_read,0,outl,1024,0,0,0,0));
  c$cks(sys$qio(0,chan,IO$_READVBLK,&iosb,read_complete,0,inpline,132,0,0,0,0));
  c$cks(sys$hiber());
#endif
  }
