/* babel - News transport agent for STiK
 *
 * socket.c - Network stuff
 *
 * (c)1996 Mark Baker. Distributable under the terms of the GNU
 *                     general public licence
 *
 * $Id: socket.c,v 1.9 1996/10/01 18:35:32 mnb20 Exp $
 */

/* Only supporting one connection at a time makes the code a bit
 * simpler, and we don't need more than that here.
 */

#include <stdio.h> 
#include <string.h>
#include <osbind.h>

/* STiK header */
#define cdecl /* We get loads of errors without this */
#include "include/transprt.h"

#include "stik-interface.h"

#include "babel.h"

/* Prototype for private function */
static long init_drivers(void);

/* Necessary for STiK */
DRV_LIST *drivers = (DRV_LIST *)NULL;
TPL *tpl = (TPL *)NULL;

/* Cookie jar entry structure */
typedef struct {
    long cktag;
    long ckvalue;
} ck_entry;

/* STiK doesn't have a services database, so this is hardcoded */
#define NNTP 119

/* Maximum line length */
#define MAXLINELENGTH 200

/* Connection time out in seconds */
#define TIMEOUT 120

/* Retry time in seconds */
#define RETRY_TIMEOUT 15

/* STiK developers docs recommend between 500 and 2000 for this */
#define TCPBUFSIZ 1024

/* Socket descriptor (global) */
short cn ;

/* Logging command line option */
extern int opt_logging ;
FILE *logfile ;

/*
 * initialise_stik() initialises STiK. Never have guessed, would you?
 */

void initialise_stik( void )
{
  /* Set up drivers table */
  Supexec(init_drivers);
  
  /* Check if that worked */
  if (drivers == (DRV_LIST *)NULL)
    {
      alert( strings.nostik ) ;
      byebye(1) ;
    }
  
  /* Get transport layer information */
  tpl = (TPL *)get_dftab(TRANSPORT_DRIVER);
  
  if (tpl == (TPL *)NULL)
    {
      alert( strings.nostik ) ;
      byebye(1) ;
    }

  /* Open log file */
  if( opt_logging )
    logfile = fopen( "babel.log", "w" ) ;
}

/*
 * init_drivers() is called by initialise. It needs to be a separate
 *    function as it has to run in supervisor mode
 */

static long init_drivers(void)
{
  long i = 0;
  ck_entry *jar = *((ck_entry **) 0x5a0);
  
  while (jar[i].cktag)
    {
      if (!strncmp((char *)&jar[i].cktag, CJTAG, 4))
	{
	  drivers = (DRV_LIST *)jar[i].ckvalue;
	  return (0);
	}
      ++i;
    }
  
  /* Supexec() (or rather its prototype) requires us to return something */
  return (0);
}

/*
 * open_server() opens a socket to the required hostname.
 *
 * hostname's use is obvious.
 */

int open_server( char *hostname )
{
  int32 server_ip ;
  char alertstr[MAXLINELENGTH] ;
  char line[MAXLINELENGTH] ;

  /* Look up host */
  if( _resolve( hostname, (char **)NULL, &server_ip, 1 ) < 0 )
    {
      /* Resolve error */
      sprintf( alertstr, strings.cantresolve, hostname ) ;
      alert( alertstr ) ;
      byebye(1) ;
    }

  event_loop() ;

  /* Open socket and bind it to address */
  if( ( cn = _TCP_open( server_ip, NNTP, 0, TCPBUFSIZ ) ) < 0 )
    {
      alert( strings.opensocket ) ;
      byebye(1) ;
    }

  event_loop() ;

  /* Wait for connection to be established */
  if( _TCP_wait_state( cn, TESTABLISH, TIMEOUT ) != E_NORMAL )
    {
      alert( strings.opensocket ) ;
      _TCP_close( cn, TIMEOUT ) ;
      byebye(1) ;
    }

  /* sock_fp = fdopen( sd, "r" ) ; ... dream on */

  /* Check for 200 reply */
  read_line( line, MAXLINELENGTH ) ;

  line[3] = '\0' ;
  if( strcmp( line, "200" ) )
    {
      alert( strings.no200 ) ;
      _TCP_close( cn, TIMEOUT ) ;
      byebye(1) ;
    }

  return cn ;
}

/*
 * close_server() closes the current connection. Send a quit command
 *    first.
 */

void close_server( void )
{
  status_line( strings.serverclose ) ;

  write_string( "quit\n" ) ;

  event_loop() ;

  _TCP_close( cn, TIMEOUT ) ;
}

/*
 * kick() "kicks" the connection. It shouldn't be necessary
 */

void kick( void )
{
  _CNkick( cn ) ;
}

/*
 * write_string() sends a null terminated string to the current connection.
 *    We have to be prepared to retry if the buffer is full
 *
 * string is, well, the string to write.
 */

void write_string( char *string )
{
  while( _TCP_send( cn, string, strlen( string ) ) == E_OBUFFULL )
    sleep( RETRY_TIMEOUT ) ;

  if( opt_logging )
    {
      fprintf( logfile, "> %s", string ) ;
      fflush( logfile ) ;
    }
}

/*
 * read_line() reads one line (up to a CR) or up to maxlength bytes. We
 *   have to do it a character at a time.
 */

void read_line( char *line, int maxlength )
{
  int count ;
  short c ;
  int i = 0 ;

  while ( i < maxlength )
    {
      /* Read as much as we can */
      count = _CNbyte_count( cn ) ;
      while ( count > 0 && i < maxlength )
	{
	  count -= 1 ;
	  c = _CNget_char( cn ) ;
	  switch( c )
	    {
	    case '\r' :
	      /* Ignore CR */
	      break ;

	    case '\n' :
	      /* LF terminates line */
	      line[i] = '\0' ;

	      /* Write to log file */
	      if( opt_logging )
		{
		  fprintf( logfile, "< %s\n", line ) ;
		  fflush( logfile ) ;
		}
	      
	      return ;
	      break ;

	    default :
	      line[i++] = (char) c ;
	    }
	}

      /* Let other processes in while we wait */
      event_loop() ;
   }
  line[maxlength-1] = '\0' ;

  /* Write to log file */
  if( opt_logging )
    {
      fprintf( logfile, "< %s\n", line ) ;
      fflush( logfile ) ;
    }
}








