/*
 * TCP/IP stream emulation for GNU Emacs.
 * Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc.

 * Author: Masanobu Umeda
 * Maintainer: umerin@mse.kyutech.ac.jp

 * Ported to Windows NT 3.51 rjf@infograph.com (Jun 6, 1995)

This file is part of GNU Emacs.

GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

 *
 * Yasunari, Itoh at PFU limited contributed for Fujitsu UTS and SX/A.
 *
 * Thu Apr  6 13:47:37 JST 1989
 * USG fixes by Sakaeda <saka@mickey.trad.pf.fujitsu.junet>
 *
 * For Fujitsu UTS compile with:
 * cc -O -o tcp tcp.c -DFUJITSU_UTS -lu -lsocket
 * 
 * Tue Apr 6 1995 Port to Windows NT
 * For Windows NT compile with:
 * cl -DWIN32 -W3 -Ox nttcp.c -link wsock32.lib
 *
 *  In your _emacs file, add the following lines:
 *
 *   (setq tcp-program-name "nttcp")
 *
 *  If using 19.28 and Gnus 4.x, please add these as well:
 *
 *   (setq nntp-buggy-select t)
 *   (setq nntp-maximum-request 10)
 *   
 */

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <io.h>
#include <ctype.h>
#include <sys/types.h>

#ifndef DEBUG
#undef OutputDebugString
#define OutputDebugString(a)  /* */
#endif

#define bcopy(f, t, n)    memcpy (t, f, n)
#define bcmp(b1, b2, n)   (memcmp (b1, b2, n) != 0)
#define bzero(b, n)      memset (b, 0, n)

SOCKET  server;    /* NNTP Server */
int     emacs_in;  /* Emacs intput */
int     emacs_out; /* Emacs output */

typedef enum {writing, reading} TestType;

/* Print the last socket error.  */
void 
pserror (const char *str)
{
  printf ("%s: last error %d\n", str, WSAGetLastError ());
}

/* Wait for a socket to be ready to read or write...  */
BOOL 
wait_for_socket (SOCKET s, TestType t)
{
  const int MAX_RETRIES = 100;
  
  struct timeval timeout = {60, 0};
  
  fd_set  fd;
  int     ret;
  int     result = 0;
  int     retries = 0;
  
  while (1) 
    {
      FD_ZERO (&fd);
      FD_SET (s,&fd);

      if (t == reading)
	ret = select (32, &fd, NULL, NULL, &timeout);
      else
	ret = select (32, NULL, &fd, NULL, &timeout);

      if (ret != 0)
	break;
      else
	++retries;

      if (retries > MAX_RETRIES) 
	{
	  OutputDebugString ("Too many retries waiting for socket!\n");
	  retries = 0;
	}

      Sleep(10);
    }      
  
  return (ret != 0);
}

/* This thread reads the stdin stream, which is data from Emacs, and
   sends it out the socket.  */
DWORD WINAPI 
from_emacs_thread (LPVOID parm)
{
  char    buffer[8*1024];
  int     nbuffer;       /* Number of bytes in buffer */
  int     wret;
  char    *retry;        /* retry bufferp */

  while (1) 
    {
      /* If there is some data from emacs, send it through the socket
       * to the server...  */

      nbuffer = read (emacs_in, buffer, sizeof buffer-1);
      if (nbuffer != -1) 
	{
	  OutputDebugString ("Read block from emacs...\n");
	  for (retry = buffer; nbuffer > 0; nbuffer -= wret, retry += wret) 
	    {
	      wait_for_socket (server, writing);
	      wret = send (server, retry, nbuffer, 0);
	      if (wret == SOCKET_ERROR) 
		{
		  pserror ("send(server)");
		  goto finish;
		}
	    }
	  OutputDebugString ("Write block to server...\n");
	}
      else 
	{
	  perror ("read(emacs_in)");
	  goto finish;
	}
      
      Sleep (25);
    }

finish:
  return 0;
}

/* This thread reads the socket, which is data from some server, and
   sends it back to emacs.  */
DWORD WINAPI 
from_server_thread (LPVOID parm) 
{
   char    buffer[8*1024];
   int     nbuffer;       /* Number of bytes in buffer */
   int     wret;
   char    *retry;        /* retry bufferp */

   while (1) 
     {
       /* If there is some data from the server, send it through stdout
	  to the emacs...  */

       wait_for_socket (server, reading);
       nbuffer = recv (server, buffer, sizeof buffer-1, 0);
       OutputDebugString ("Read block from server...\n");
       if (nbuffer != SOCKET_ERROR) 
	 {
	   for (retry = buffer; nbuffer > 0; nbuffer -= wret, retry += wret) 
	     {
	       wret = write (emacs_out, retry, nbuffer);
	       if (wret < 0) 
		 {
		   perror ("write(emacs_out)");
		   goto finish;
		 }
	     }
	   OutputDebugString ("Wrote block to emacs...\n");
	 }
       else 
	 {
	   pserror ("recv(server)");
	   goto finish;
	 }
       Sleep (25);
     }

finish:
   return 0;
}

int 
main (int argc, char *argv[])
{
  WSADATA               WSAData;
  HANDLE                hThreads[2];
  DWORD                 dummy = 1;

  struct  hostent       *host;
  struct  sockaddr_in   sockin, sockme;
  struct  servent       *serv;
  char                  *hostname = NULL;
  char                  *service = "nntp";
  short                 port;
  int                   false = 0;     /* FALSE flag for setsockopt () */
  int                   err;
   
  emacs_in  = fileno (stdin);
  emacs_out = fileno (stdout);
   
  if (argc < 2) 
     {
       fprintf (stderr, "Usage: %s HOST [SERVICE]\n", argv[0]);
       return (1);
     }

  if (argc >= 2)
    hostname = argv[1];
  if (argc >= 3)
    service = argv[2];

  if ((err = WSAStartup (MAKEWORD (2, 0), &WSAData)) != 0) 
    {
      printf ("WSAStartup error - %d\n", err);
      return (1);
    }

  if ((host = gethostbyname (hostname)) == NULL) 
    {
      pserror ("gethostbyname");
      return (1);
    }

  if (isdigit (service[0]))
    port = htons ((short) atoi (service));
  else 
    {
      serv = getservbyname (service, "tcp");
      if (serv == NULL) 
	{
	  pserror ("getservbyname");
	  return (1);
	}
      port = serv->s_port;
    }

  bzero (&sockin, sizeof (sockin));
  sockin.sin_family = host->h_addrtype;
  bcopy (host->h_addr, &sockin.sin_addr, host->h_length);
  sockin.sin_port = port;
  if ((server = socket (AF_INET, SOCK_STREAM, 0)) < 0) 
    {
      pserror ("socket");
      return (1);
    }

  if (setsockopt (server, SOL_SOCKET, SO_REUSEADDR, (const char *) &false, 
		  sizeof (false)) != 0) 
    {
      pserror ("setsockopt");
      return (1);
    }

  bzero (&sockme, sizeof (sockme));
  sockme.sin_family = sockin.sin_family;
  sockme.sin_addr.s_addr = htonl (INADDR_ANY);
  if (bind (server, (struct sockaddr *)&sockme, sizeof (sockme)) != 0) 
    {
      pserror ("bind");
      return (1);
    }

  if (connect (server, (struct sockaddr *)&sockin, sizeof (sockin)) != 0) 
    {
      pserror ("connect");
      close (server);
      return (1);
    }

   /* Create the theads that transfer data between emacs and the server.  */
   hThreads[0]  = CreateThread (NULL, 0, from_emacs_thread, &dummy, 0, &dummy);
   hThreads[1]  = CreateThread (NULL, 0, from_server_thread, &dummy, 0, &dummy);

   /* Wait for one or both threads to die... */
   WaitForMultipleObjects (2, hThreads, FALSE, INFINITE);

   shutdown (server,2);
   closesocket (server);

   WSACleanup ();

   OutputDebugString ("*** Exitting tcp...\n");
   return (0);
}
